Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions installer/lib/mix/tasks/igniter.new.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ defmodule Mix.Tasks.Igniter.New do
to the `--with` command, as it may or may not support it. Use `--with-args`
to provide arguments to that command.
* `--no-installer-version-check` - skip the version check for the latest igniter_new version
* `--git` - Initialize a git repository in the project directory and commit the initial state.

## Options for `mix.new`

Expand Down Expand Up @@ -71,7 +72,8 @@ defmodule Mix.Tasks.Igniter.New do
module: :string,
sup: :boolean,
umbrella: :boolean,
installer_version_check: :boolean
installer_version_check: :boolean,
git: :boolean
],
aliases: [i: :install, l: :local, e: :example, w: :with]
)
Expand Down Expand Up @@ -241,6 +243,10 @@ defmodule Mix.Tasks.Igniter.New do
)
end

if options[:git] do
initialize_git_repo()
end

:ok
end

Expand Down Expand Up @@ -312,7 +318,7 @@ defmodule Mix.Tasks.Igniter.New do
end
end

@flags ~w(example sup umbrella installer-version-check no-installer-version-check)
@flags ~w(example sup umbrella installer-version-check no-installer-version-check git)
@flags_with_values ~w(install local with with-args module)
@switches ~w(e)
@switches_with_values ~w(i l)
Expand Down Expand Up @@ -383,6 +389,33 @@ defmodule Mix.Tasks.Igniter.New do
@doc false
def igniter_version, do: @igniter_version

defp initialize_git_repo do
Igniter.Installer.Loading.with_spinner(
"Initializing local git repository, staging all files, and committing",
fn ->
case System.cmd("git", ["init"]) do
{_, 0} ->
case System.cmd("git", ["add", "."]) do
{_, 0} ->
case System.cmd("git", ["commit", "-m", "🔥 initial commit 🔥"]) do
{_, 0} ->
Mix.shell().info("Git repository initialized and initial commit created.")

{output, _} ->
Mix.shell().error("Failed to create initial commit: #{output}")
end

{output, _} ->
Mix.shell().error("Failed to add files to git: #{output}")
end

{output, _} ->
Mix.shell().error("Failed to initialize git repository: #{output}")
end
end
)
end

defp maybe_warn_outdated(latest_version, opts) do
if Version.compare(@installer_version, latest_version) == :lt do
if opts[:yes] do
Expand Down
222 changes: 222 additions & 0 deletions installer/test/mix/tasks/igniter.new_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
defmodule Mix.Tasks.Igniter.NewTest do
use ExUnit.Case, async: true

@moduletag :tmp_dir

describe "igniter.new with --git flag" do
@tag :integration
test "initializes git repository and creates initial commit", %{tmp_dir: tmp_dir} do
unless git_available?() do
:ok
else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well want to remove inspects etc.

IO.inspect(tmp_dir)
project_name = "test_project"
project_path = Path.join(tmp_dir, project_name)

# Change to tmp directory to run the command
original_cwd = File.cwd!()

try do
File.cd!(tmp_dir)

# Run igniter.new with --git flag using System.cmd
{output, exit_code} =
System.cmd(
"mix",
["igniter.new", project_name, "--git", "--yes", "--no-installer-version-check"],
stderr_to_stdout: true,
env: [{"MIX_ENV", "test"}]
)

if exit_code != 0 do
IO.puts("Command failed with output: #{output}")
end

assert exit_code == 0,
"Expected igniter.new to succeed, but got exit code #{exit_code}. Output: #{output}"

# Verify project directory was created
assert File.exists?(project_path), "Project directory should exist at #{project_path}"

# Change to project directory to check git status
File.cd!(project_path)

# Verify .git directory exists
assert File.exists?(".git"), ".git directory should exist"
assert File.dir?(".git"), ".git should be a directory"

# Verify git repository was initialized by checking git status
{output, 0} = System.cmd("git", ["status", "--porcelain"])
# Empty output means all files are committed (working directory clean)
assert String.trim(output) == "",
"Working directory should be clean after initial commit"

# Verify there is at least one commit
{output, 0} = System.cmd("git", ["log", "--oneline"])

assert String.contains?(output, "🔥 initial commit 🔥"),
"Should have initial commit with fire emoji"

# Verify essential files are tracked
{output, 0} = System.cmd("git", ["ls-files"])
tracked_files = String.split(String.trim(output), "\n")

assert "mix.exs" in tracked_files, "mix.exs should be tracked"
assert ".gitignore" in tracked_files, ".gitignore should be tracked"
assert "README.md" in tracked_files, "README.md should be tracked"

# Verify igniter was added to mix.exs
mix_exs_content = File.read!("mix.exs")

assert String.contains?(mix_exs_content, "{:igniter"),
"Igniter dependency should be added to mix.exs"
after
File.cd!(original_cwd)
end
end
end

@tag :integration
test "creates project without git when --git flag is not provided", %{tmp_dir: tmp_dir} do
unless git_available?() do
:ok
else
project_name = "test_project_no_git"
project_path = Path.join(tmp_dir, project_name)

original_cwd = File.cwd!()

try do
File.cd!(tmp_dir)

# Run igniter.new without --git flag
{output, exit_code} =
System.cmd(
"mix",
["igniter.new", project_name, "--yes", "--no-installer-version-check"],
stderr_to_stdout: true,
env: [{"MIX_ENV", "test"}]
)

assert exit_code == 0,
"Expected igniter.new to succeed, but got exit code #{exit_code}. Output: #{output}"

assert File.exists?(project_path), "Project directory should exist"
File.cd!(project_path)

# Verify .git directory does NOT exist
refute File.exists?(".git"),
".git directory should not exist when --git flag is not provided"
after
File.cd!(original_cwd)
end
end
end

@tag :integration
test "git functionality works with other flags", %{tmp_dir: tmp_dir} do
unless git_available?() do
:ok
else
project_name = "test_project_with_sup"
project_path = Path.join(tmp_dir, project_name)

original_cwd = File.cwd!()

try do
File.cd!(tmp_dir)

# Run igniter.new with --git and --sup flags
{output, exit_code} =
System.cmd(
"mix",
[
"igniter.new",
project_name,
"--git",
"--sup",
"--yes",
"--no-installer-version-check"
],
stderr_to_stdout: true,
env: [{"MIX_ENV", "test"}]
)

assert exit_code == 0,
"Expected igniter.new to succeed, but got exit code #{exit_code}. Output: #{output}"

assert File.exists?(project_path)
File.cd!(project_path)

# Verify git repository exists and is clean
assert File.exists?(".git")
{output, 0} = System.cmd("git", ["status", "--porcelain"])
assert String.trim(output) == ""

# Verify project has supervision tree (--sup flag worked)
mix_exs_content = File.read!("mix.exs")

assert String.contains?(mix_exs_content, "mod: {"),
"Should have supervision tree when --sup is used"

# Verify commit exists
{output, 0} = System.cmd("git", ["rev-list", "--count", "HEAD"])
commit_count = String.trim(output) |> String.to_integer()
assert commit_count >= 1, "Should have at least one commit"
after
File.cd!(original_cwd)
end
end
end
end

describe "handle missing git" do
@tag :integration
test "handles missing git gracefully in development", %{tmp_dir: tmp_dir} do
# This test simulates what happens when git is not available
# but --git flag is used. In practice, the initialize_git_repo function
# will output error messages but not crash the entire task.

project_name = "test_project_no_git_binary"
project_path = Path.join(tmp_dir, project_name)

original_cwd = File.cwd!()

try do
File.cd!(tmp_dir)

# Create project first without git
{_output, exit_code} =
System.cmd(
"mix",
["igniter.new", project_name, "--yes", "--no-installer-version-check"],
stderr_to_stdout: true,
env: [{"MIX_ENV", "test"}]
)

assert exit_code == 0
assert File.exists?(project_path)

File.cd!(project_path)

# Now test that the git repo functionality produces appropriate output
# when git commands fail (we can't easily simulate missing git binary in tests)
if git_available?() do
# At least verify that git operations would work in normal circumstances
{_output, exit_code} = System.cmd("git", ["init"])
assert exit_code == 0
end
after
File.cd!(original_cwd)
end
end
end

# Helper to ensure git is available for testing
defp git_available? do
case System.cmd("git", ["--version"]) do
{_, 0} -> true
_ -> false
end
end
end
1 change: 1 addition & 0 deletions installer/test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ExUnit.start()
2 changes: 1 addition & 1 deletion mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"},
"rewrite": {:hex, :rewrite, "1.1.2", "f5a5d10f5fed1491a6ff48e078d4585882695962ccc9e6c779bae025d1f92eda", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "7f8b94b1e3528d0a47b3e8b7bfeca559d2948a65fa7418a9ad7d7712703d39d4"},
"sourceror": {:hex, :sourceror, "1.10.0", "38397dedbbc286966ec48c7af13e228b171332be1ad731974438c77791945ce9", [:mix], [], "hexpm", "29dbdfc92e04569c9d8e6efdc422fc1d815f4bd0055dc7c51b8800fb75c4b3f1"},
"spitfire": {:hex, :spitfire, "0.2.0", "0de1f519a23f65bde40d316adad53c07a9563f25cc68915d639d8a509a0aad8a", [:mix], [], "hexpm", "743daaee2d81a0d8095431729f478ce49b47ea8943c7d770de86704975cb7775"},
"spitfire": {:hex, :spitfire, "0.2.1", "29e154873f05444669c7453d3d931820822cbca5170e88f0f8faa1de74a79b47", [:mix], [], "hexpm", "6eeed75054a38341b2e1814d41bb0a250564092358de2669fdb57ff88141d91b"},
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"},
Expand Down