Skip to content

Commit

Permalink
improvement: add installer archive
Browse files Browse the repository at this point in the history
  • Loading branch information
zachdaniel committed Jun 3, 2024
1 parent d8b4633 commit f1e0fff
Show file tree
Hide file tree
Showing 11 changed files with 364 additions and 192 deletions.
27 changes: 27 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,30 @@ igniter-*.tar

# Temporary files, for example, from tests.
/tmp/

# The directory Mix will write compiled artifacts to.
/installer/_build/

# If you run "mix test --cover", coverage assets end up here.
/installer/cover/

# The directory Mix downloads your dependencies sources to.
/installer/deps/

# Where third-party dependencies like ExDoc output generated docs.
/installer/doc/

# Ignore .fetch files in case you like to edit your project deps locally.
/installer/.fetch

# If the VM crashes, it generates a dump, let's ignore it too.
installer/erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez

# Ignore package tarball (built via "mix hex.build").
igniter-*.tar

# Temporary files, for example, from tests.
installer/tmp/
17 changes: 17 additions & 0 deletions installer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## mix igniter.new

Provides `igniter.new` installer as an archive.

To install from Hex, run:

$ mix archive.install hex igniter_new

To build and install it locally,
ensure any previous archive versions are removed:

$ mix archive.uninstall phx_new

Then run:

$ cd installer
$ MIX_ENV=prod mix do archive.build, archive.install
87 changes: 87 additions & 0 deletions installer/lib/mix/tasks/igniter.new.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
defmodule Mix.Tasks.Igniter.New do
use Mix.Task

@igniter_version Mix.Project.config()[:version]

@shortdoc "Creates a new Igniter application"
def run([name | _ ] = argv) do
{options, argv, _errors} = OptionParser.parse(argv,
strict: [install: :keep, local: :string, example: :boolean],
aliases: [i: :install, l: :local, e: :example]
)

install =
options[:install]
|> List.wrap()
|> Enum.join(",")
|> String.split(",", trim: true)

if File.exists?(name) do
Mix.shell().error("""
The directory #{name} already exists. You must either:
1. remove or move it
2. If you are trying to modify an existing project add `{:igniter` to the project, if it is not
already added, and then run `mix igniter.install #{Enum.join(install, ",")}` inside the project
""")

exit({:shutdown, 1})
end

exit = Mix.shell().cmd("mix new #{Enum.join(argv, " ")}")

if exit == 0 do
version_requirement =
if options[:local] do
local = Path.join(["..", Path.relative_to_cwd(options[:local])])
"path: #{inspect(local)}"
else
inspect(version_requirement())
end

File.cd!(name)

contents =
"mix.exs"
|> File.read!()

if String.contains?(contents, "{:igniter") do
Mix.shell().info("It looks like the project already exists and igniter is already installed, not adding it to deps.")
else
new_contents =
String.replace(contents, "defp deps do\n [\n", "defp deps do\n [\n{:igniter, #{version_requirement}}\n")

File.write!("mix.exs", new_contents)
end

Mix.shell().cmd("mix deps.get")
Mix.shell().cmd("mix compile")

unless Enum.empty?(install) do
example =
if options[:example] do
"--example"
end
Mix.shell().cmd("mix igniter.install #{Enum.join(install, ",")} --yes #{example}" |> IO.inspect())
end

else
Mix.shell().info("Aborting command because associated `mix new` command failed.")

exit({:shutdown, 1})
end

:ok
end

defp version_requirement do
@igniter_version
|> Version.parse!()
|> case do
%Version{major: 0, minor: minor} ->
"~> 0.#{minor}"

%Version{major: major} ->
"~> #{major}.0"
end
end
end
41 changes: 41 additions & 0 deletions installer/mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
defmodule Igniter.New.MixProject do
use Mix.Project

@version "0.1.0"
@scm_url "https://github.com/ash-project/igniter"

def project do
[
app: :igniter_new,
start_permanent: Mix.env() == :prod,
version: @version,
elixir: "~> 1.14",
deps: deps(),
package: [
maintainers: ["Zach Daniel"],
licenses: ["MIT"],
links: %{"GitHub" => @scm_url},
files: ~w(lib templates mix.exs README.md)
],
preferred_cli_env: [docs: :docs],
source_url: @scm_url,
docs: docs(),
homepage_url: "https://www.ash-hq.org",
description: """
Create a new mix project with igniter, and run igniter installers in one command!
"""
]
end

def deps do
[
{:ex_doc, "~> 0.24", only: :docs}
]
end

defp docs do
[
source_url_pattern: "#{@scm_url}/blob/v#{@version}/installer/%{path}#L%{line}"
]
end
end
8 changes: 8 additions & 0 deletions installer/mix.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
%{
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
"ex_doc": {:hex, :ex_doc, "0.34.0", "ab95e0775db3df71d30cf8d78728dd9261c355c81382bcd4cefdc74610bef13e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "60734fb4c1353f270c3286df4a0d51e65a2c1d9fba66af3940847cc65a8066d7"},
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
}
21 changes: 4 additions & 17 deletions lib/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,15 @@ defmodule Igniter.Config do

igniter
|> Igniter.include_or_create_elixir_file(file_path, "import Config\n")
|> Igniter.update_file(file_path, fn source ->
quoted = Rewrite.Source.get(source, :quoted)
zipper = Zipper.zip(quoted)

|> Igniter.update_elixir_file(file_path, fn zipper ->
case try_update_three_arg(zipper, config_path, app_name, updater) do
{:ok, zipper} ->
Rewrite.Source.update(source, :configure, :quoted, Zipper.root(zipper))
zipper

:error ->
case try_update_two_arg(zipper, config_path, app_name, value, updater) do
{:ok, zipper} ->
Rewrite.Source.update(source, :configure, :quoted, Zipper.root(zipper))
zipper

:error ->
# add new code here
Expand All @@ -32,17 +29,7 @@ defmodule Igniter.Config do
config =
{:config, [], [app_name, [{first, Igniter.Common.keywordify(rest, value)}]]}

code =
zipper
|> Igniter.Common.add_code(config)
|> Zipper.root()

Rewrite.Source.update(
source,
:configure,
:quoted,
code
)
Igniter.Common.add_code(zipper, config)
end
end
end)
Expand Down
80 changes: 25 additions & 55 deletions lib/deps.ex
Original file line number Diff line number Diff line change
Expand Up @@ -63,70 +63,40 @@ defmodule Igniter.Deps do

defp remove_dependency(igniter, name) do
igniter
|> Igniter.update_file("mix.exs", fn source ->
quoted = Rewrite.Source.get(source, :quoted)

new_quoted =
with zipper <- Zipper.zip(quoted),
{:ok, zipper} <- Common.move_to_module_using(zipper, Mix.Project),
{:ok, zipper} <- Common.move_to_defp(zipper, :deps, 0),
true <- Common.node_matches_pattern?(zipper, value when is_list(value)),
current_declaration_index when not is_nil(current_declaration_index) <-
Common.find_list_item_index(zipper, fn item ->
if Common.tuple?(item) do
first_elem = Common.tuple_elem(item, 0)
first_elem && Common.node_matches_pattern?(first_elem, ^name)
end
end) do
zipper
|> Common.remove_index(current_declaration_index)
|> Zipper.root()
else
_ ->
quoted
end

if new_quoted == quoted do
Rewrite.Source.add_issue(
source,
"Failed to remove dependency #{inspect(name)}"
)
|> Igniter.update_elixir_file("mix.exs", fn zipper ->
with {:ok, zipper} <- Common.move_to_module_using(zipper, Mix.Project),
{:ok, zipper} <- Common.move_to_defp(zipper, :deps, 0),
true <- Common.node_matches_pattern?(zipper, value when is_list(value)),
current_declaration_index when not is_nil(current_declaration_index) <-
Common.find_list_item_index(zipper, fn item ->
if Common.tuple?(item) do
first_elem = Common.tuple_elem(item, 0)
first_elem && Common.node_matches_pattern?(first_elem, ^name)
end
end) do
Common.remove_index(zipper, current_declaration_index)
else
Rewrite.Source.update(source, :add_dependency, :quoted, new_quoted)
_ ->
{:error, "Failed to remove dependency #{inspect(name)}"}
end
end)
end

defp do_add_dependency(igniter, name, version) do
igniter
|> Igniter.update_file("mix.exs", fn source ->
quoted = Rewrite.Source.get(source, :quoted)

new_quoted =
with zipper <- Zipper.zip(quoted),
{:ok, zipper} <- Common.move_to_module_using(zipper, Mix.Project),
{:ok, zipper} <- Common.move_to_defp(zipper, :deps, 0),
true <- Common.node_matches_pattern?(zipper, value when is_list(value)) do
quoted =
quote do
{unquote(name), unquote(version)}
end

zipper
|> Common.prepend_to_list(quoted)
|> Zipper.root()
else
_ ->
quoted
end
|> Igniter.update_elixir_file("mix.exs", fn zipper ->
with {:ok, zipper} <- Common.move_to_module_using(zipper, Mix.Project),
{:ok, zipper} <- Common.move_to_defp(zipper, :deps, 0),
true <- Common.node_matches_pattern?(zipper, value when is_list(value)) do
quoted =
quote do
{unquote(name), unquote(version)}
end

if new_quoted == quoted do
Rewrite.Source.add_issue(
source,
"Failed to add dependency #{inspect({inspect(name), inspect(version)})}"
)
Common.prepend_to_list(zipper, quoted)
else
Rewrite.Source.update(source, :add_dependency, :quoted, new_quoted)
_ ->
{:error, "Failed to add dependency #{inspect({inspect(name), inspect(version)})}"}
end
end)
end
Expand Down
Loading

0 comments on commit f1e0fff

Please sign in to comment.