Skip to content

Commit

Permalink
Merge fac59dd into d50f04e
Browse files Browse the repository at this point in the history
  • Loading branch information
TheFirstAvenger committed Feb 12, 2019
2 parents d50f04e + fac59dd commit 7821b0d
Show file tree
Hide file tree
Showing 19 changed files with 516 additions and 79 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Changelog

## Next
## 0.2.0

* Change dependency checking to use regex
* Clear dialyzer ignores fixed in elixir 1.8.1
* Change format of IO questions
* Add :git_hooks check
* Add :dialyzer_config check
* Mox/Impl all File operations

## 0.1.2

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Contributions welcome. Specifically looking to:

### Add Configuration

* [ ] [`:dialyxir`](https://github.com/jeremyjh/dialyxir)
* [X] [`:dialyxir`](https://github.com/jeremyjh/dialyxir)
* [ ] [`:credo`](https://github.com/rrrene/credo)
* [ ] [`:excoveralls`](https://github.com/parroty/excoveralls)
* [ ] [`:git_hooks`](https://github.com/qgadrian/elixir_git_hooks)
Expand Down
3 changes: 2 additions & 1 deletion config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ config :ironman,
hex_repo: "https://hex.pm",
http_client: Ironman.MockHttpClient,
io: Ironman.MockIO,
cmd: Ironman.MockCmd
cmd: Ironman.MockCmd,
file: Ironman.MockFile
9 changes: 9 additions & 0 deletions coveralls.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"skip_files": [
"lib/ironman.ex",
"lib/mix/tasks/suit_up.ex",
"lib/ironman/utils/io/default_impl.ex",
"lib/ironman/utils/http_client/default_impl.ex",
"lib/ironman/utils/cmd/default_impl.ex"
]
}
88 changes: 88 additions & 0 deletions lib/ironman/checks/dialyzer_config.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
defmodule Ironman.Checks.DialyzerConfig do
@moduledoc false
alias Ironman.{Config, Utils}

@spec run(Config.t()) :: {:error, any()} | {:no | :yes | :up_to_date, Config.t()}
def run(%Config{} = config) do
config
|> Config.starting_project_config()
|> Keyword.get(:dialyzer, nil)
|> case do
nil -> offer_add_dialyzer_config(config)
_ -> {:up_to_date, config}
end
end

def offer_add_dialyzer_config(%Config{} = config) do
Utils.ask(
"Add dialyzer config to project?",
fn -> do_add_config(config) end,
fn -> skip_install(config) end
)
end

def do_add_config(%Config{} = config) do
config = set_dialyzer_mix_exs(config)

config = add_dialyzer_ignore_file(config)

config = add_plt_to_gitignore(config)

{:yes, config}
end

defp set_dialyzer_mix_exs(config) do
mix_exs = Config.mix_exs(config)
mix_exs = Regex.replace(~r/def project do\n.*?\[/, mix_exs, "def project do\n [ " <> dialyzer_config(config))
Config.set_mix_exs(config, mix_exs)
end

defp add_dialyzer_ignore_file(config) do
case Config.dialyzer_ignore(config) do
nil ->
Utils.puts("Adding dialyzer ignore file")
Config.set_dialyzer_ignore(config, "[\n\n]")

_ ->
config
end
end

defp dialyzer_config(config) do
app_name = Config.app_name(config)

"""
dialyzer: [
ignore_warnings: ".dialyzer_ignore.exs",
list_unused_filters: true,
plt_file: {:no_warn, "#{app_name}.plt"}
],
"""
end

defp add_plt_to_gitignore(config) do
app_name = Config.app_name(config)

case Config.gitignore(config) do
nil ->
Utils.puts("Creating .gitignore with #{app_name}.plt")
Config.set_gitignore(config, "# dialyzer plt for CI caching\n#{app_name}.plt\n")

gitignore ->
if String.contains?(gitignore, "#{app_name}.plt") do
config
else
Utils.puts("Adding #{app_name}.plt to .gitignore")

newlines = if String.ends_with?(gitignore, "\n"), do: "", else: "\n\n"
Config.set_gitignore(config, "#{gitignore}#{newlines}# dialyzer plt\n#{app_name}.plt\n")
end
end
end

@spec skip_install(Config.t()) :: {:no, Config.t()}
def skip_install(%Config{} = config) do
Utils.puts("Skipping dialyzer config")
{:no, config}
end
end
69 changes: 65 additions & 4 deletions lib/ironman/config.ex
Original file line number Diff line number Diff line change
@@ -1,18 +1,79 @@
defmodule Ironman.Config do
@moduledoc """
This struct represents the state of the project. It is created at the beginning of a run, passed through
all the checks, where it is updated, and then mix.exs is written out at the end based on its contents.
all the checks, where it is updated, and then files are written out at the end based on its contents.
"""

alias Ironman.Config
alias Ironman.Utils.File, as: IFile

@type t :: %__MODULE__{
mix_exs: String.t(),
changed: boolean()
mix_exs_changed: boolean(),
gitignore: String.t() | nil,
gitignore_changed: boolean(),
dialyzer_ignore: String.t() | nil,
dialyzer_ignore_changed: boolean(),
starting_project_config: keyword()
}
defstruct mix_exs: nil,
changed: false
mix_exs_changed: false,
gitignore: nil,
gitignore_changed: false,
dialyzer_ignore: nil,
dialyzer_ignore_changed: false,
starting_project_config: nil

def new!() do
%__MODULE__{
mix_exs: File.read!("mix.exs")
mix_exs: file_or_nil("mix.exs"),
gitignore: file_or_nil(".gitignore"),
dialyzer_ignore: file_or_nil(".dialyzer_ignore.exs"),
starting_project_config: Mix.Project.config()
}
end

## Getters
def starting_project_config(%Config{starting_project_config: starting_project_config}), do: starting_project_config
def mix_exs(%Config{mix_exs: mix_exs}), do: mix_exs
def gitignore(%Config{gitignore: gitignore}), do: gitignore
def dialyzer_ignore(%Config{dialyzer_ignore: dialyzer_ignore}), do: dialyzer_ignore

## Setters when not actually changed

def set_mix_exs(%Config{mix_exs: mix_exs} = config, new_content)
when new_content == mix_exs,
do: config

def set_mix_exs(%Config{} = config, new_content),
do: %Config{config | mix_exs: new_content, mix_exs_changed: true}

def set_gitignore(%Config{gitignore: gitignore} = config, new_content)
when new_content == gitignore,
do: config

def set_gitignore(%Config{} = config, new_content),
do: %Config{config | gitignore: new_content, gitignore_changed: true}

def set_dialyzer_ignore(%Config{dialyzer_ignore: dialyzer_ignore} = config, new_content)
when new_content == dialyzer_ignore,
do: config

def set_dialyzer_ignore(%Config{} = config, new_content),
do: %Config{config | dialyzer_ignore: new_content, dialyzer_ignore_changed: true}

def mix_exs_changed(%Config{mix_exs_changed: mix_exs_changed}), do: mix_exs_changed
def gitignore_changed(%Config{gitignore_changed: gitignore_changed}), do: gitignore_changed
def dialyzer_ignore_changed(%Config{dialyzer_ignore_changed: dialyzer_ignore_changed}), do: dialyzer_ignore_changed

def app_name(%Config{starting_project_config: starting_project_config}),
do: Keyword.fetch!(starting_project_config, :app)

defp file_or_nil(path) do
if IFile.exists?(path) do
IFile.read!(path)
else
nil
end
end
end
12 changes: 7 additions & 5 deletions lib/ironman/runner.ex
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
defmodule Ironman.Runner do
@moduledoc false
alias Ironman.Checks.SimpleDep
alias Ironman.Checks.{DialyzerConfig, SimpleDep}
alias Ironman.{Config, Utils}

@checks [:ex_doc, :earmark, :dialyxir, :mix_test_watch, :credo, :excoveralls, :git_hooks]
@checks [:ex_doc, :earmark, :dialyxir, :mix_test_watch, :credo, :excoveralls, :git_hooks, :dialyzer_config]

def run do
if Utils.check_self_version() == :exit, do: System.halt()
if Utils.check_mix_format() == :exit, do: System.halt()
config = Config.new!()
%Config{changed: changed} = config = Enum.reduce(@checks, config, &run_check(&2, &1))
config = Enum.reduce(@checks, config, &run_check(&2, &1))

if changed do
Utils.write_mix_exs(config)
if Utils.write_changes(config) do
Utils.run_mix_format()
Utils.run_mix_deps_get()
Utils.run_mix_clean()
Expand Down Expand Up @@ -43,6 +42,9 @@ defmodule Ironman.Runner do
def run_check(%Config{} = config, :git_hooks),
do: config |> SimpleDep.run(:git_hooks, only: :test) |> unwrap(:git_hooks)

def run_check(%Config{} = config, :dialyzer_config),
do: config |> DialyzerConfig.run() |> unwrap(:dialyzer_config)

@spec unwrap({atom(), Config.t()} | {:error, any()}, atom()) :: Config.t()
def unwrap({:no, config}, _check), do: config
def unwrap({:yes, config}, _check), do: config
Expand Down
45 changes: 31 additions & 14 deletions lib/ironman/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ defmodule Ironman.Utils do
@moduledoc false
alias Ironman.Config
alias Ironman.Utils.{Cmd, Deps, HttpClient}
alias Ironman.Utils.File, as: IFile
alias Ironman.Utils.IO, as: IIO

def puts(out) do
if Mix.env() != :test do
IO.puts(out)
end
if Mix.env() != :test, do: IO.puts(out)
end

def check_self_version do
Expand All @@ -24,8 +23,7 @@ defmodule Ironman.Utils do
ask(
"Ironman is out of date. Upgrade?",
fn -> upgrade_ironman() end,
fn -> :declined end,
fn -> ask_self_upgrade() end
fn -> :declined end
)
end

Expand All @@ -37,10 +35,10 @@ defmodule Ironman.Utils do
end

def check_mix_format do
start = File.read!("mix.exs")
start = IFile.read!("mix.exs")
run_mix_format()

if start == File.read!("mix.exs") do
if start == IFile.read!("mix.exs") do
:ok
else
ask_mix_format()
Expand All @@ -67,28 +65,47 @@ defmodule Ironman.Utils do
ask(
"Mix format changed mix.exs, would you like to exit (to commit format changes separately)?",
fn -> :exit end,
fn -> :ok end,
fn -> ask_mix_format() end
fn -> :ok end
)
end

def write_changes(%Config{} = config) do
if Config.mix_exs_changed(config), do: write_mix_exs(config)
if Config.gitignore_changed(config), do: write_gitignore(config)
if Config.dialyzer_ignore_changed(config), do: write_dialyzer_ignore(config)

Config.mix_exs_changed(config) or Config.gitignore_changed(config) or Config.dialyzer_ignore_changed(config)
end

@spec write_mix_exs(Config.t()) :: :ok
def write_mix_exs(%Config{mix_exs: mix_exs}) do
defp write_mix_exs(%Config{} = config) do
puts("Writing new mix.exs...")
File.write!("mix.exs", mix_exs)
IFile.write!("mix.exs", Config.mix_exs(config))
end

@spec write_gitignore(Config.t()) :: :ok
defp write_gitignore(%Config{} = config) do
puts("Writing new gitignore...")
IFile.write!(".gitignore", Config.gitignore(config))
end

@spec write_dialyzer_ignore(Config.t()) :: :ok
defp write_dialyzer_ignore(%Config{} = config) do
puts("Writing new dialyzer_ignore...")
IFile.write!(".dialyzer_ignore.exs", Config.dialyzer_ignore(config))
end

@spec get_body_as_term(String.t()) :: {:ok, any()} | {:error, any()}
def get_body_as_term(url) do
HttpClient.get_body_as_term(url)
end

@spec ask(String.t(), function(), function(), function()) :: any()
def ask(q, yes, no, other) do
@spec ask(String.t(), function(), function()) :: any()
def ask(q, yes, no) do
case IIO.get("#{q} [Yn] ") do
x when x in ["Y\n", "y\n", "\n"] -> yes.()
x when x in ["N\n", "n\n"] -> no.()
_ -> other.()
_ -> ask(q, yes, no)
end
end
end

0 comments on commit 7821b0d

Please sign in to comment.