Skip to content

Commit 601d010

Browse files
fix: tolerate 3-tuple dep entries in recursively_compose_schema (#372)
`Igniter.Util.Info.recursively_compose_schema/4` merged `installs` and `adds_deps` with `Keyword.merge/2`, which raises `ArgumentError` when a dep entry is a 3-tuple like `{:credo, "~> 1.7", only: [:dev, :test]}`. The type spec at `lib/mix/task/info.ex:129-132` explicitly allows 3-tuples, and other code in the same module (`add_deps/3`, installs normalization) already handles them. Replace both `Keyword.merge/2` calls with a `merge_deps/2` helper that dedupes by dep name while preserving the tuple shape, keeping the parent's entry on conflict to match `Keyword.merge/2` semantics. Co-authored-by: Matt Pruitt <41898282+guitsaru@users.noreply.github.com>
1 parent 5b7a312 commit 601d010

2 files changed

Lines changed: 101 additions & 2 deletions

File tree

lib/igniter/util/info.ex

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,8 +481,8 @@ defmodule Igniter.Util.Info do
481481
alias_conflicts: alias_conflicts(schema, composing_schema, composing_task_name),
482482
composes: rest,
483483
extra_args?: schema.extra_args? || composing_schema.extra_args?,
484-
installs: Keyword.merge(composing_schema.installs, schema.installs),
485-
adds_deps: Keyword.merge(composing_schema.adds_deps, schema.adds_deps)
484+
installs: merge_deps(composing_schema.installs, schema.installs),
485+
adds_deps: merge_deps(composing_schema.adds_deps, schema.adds_deps)
486486
},
487487
argv,
488488
parent,
@@ -501,6 +501,11 @@ defmodule Igniter.Util.Info do
501501
end
502502
end
503503

504+
defp merge_deps(composed, parent) do
505+
names = MapSet.new(parent, &elem(&1, 0))
506+
Enum.reject(composed, fn entry -> elem(entry, 0) in names end) ++ parent
507+
end
508+
504509
defp flag_conflicts(schema, composing_schema, composing_task_name) do
505510
Map.merge(
506511
schema.flag_conflicts,

test/igniter/mix/task_test.exs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,100 @@ defmodule Igniter.Mix.TaskTest do
293293
end
294294
end
295295

296+
describe "composed schema dep merging" do
297+
# Covers every form in the `Igniter.Mix.Task.Info.dep` type:
298+
# {name, version}
299+
# {name, opts}
300+
# {name, version, opts}
301+
302+
defmodule Elixir.Mix.Tasks.ParentAddsDepsAllFormats do
303+
use Igniter.Mix.Task
304+
305+
def info(_argv, _parent) do
306+
%Igniter.Mix.Task.Info{
307+
composes: ["child_adds_deps_all_formats"],
308+
adds_deps: [
309+
{:boundary, "~> 0.10", runtime: false},
310+
{:parent_git_dep, git: "https://example.com/parent.git"},
311+
{:parent_versioned, "~> 1.0"}
312+
]
313+
}
314+
end
315+
316+
def igniter(igniter), do: igniter
317+
end
318+
319+
defmodule Elixir.Mix.Tasks.ChildAddsDepsAllFormats do
320+
use Igniter.Mix.Task
321+
322+
def info(_argv, _parent) do
323+
%Igniter.Mix.Task.Info{
324+
adds_deps: [
325+
{:cloak, "~> 1.1"},
326+
{:child_git_dep, git: "https://example.com/child.git"},
327+
{:child_with_opts, "~> 2.0", only: :test}
328+
]
329+
}
330+
end
331+
332+
def igniter(igniter), do: igniter
333+
end
334+
335+
defmodule Elixir.Mix.Tasks.ParentInstallsAllFormats do
336+
use Igniter.Mix.Task
337+
338+
def info(_argv, _parent) do
339+
%Igniter.Mix.Task.Info{
340+
composes: ["child_installs_all_formats"],
341+
installs: [
342+
{:boundary, "~> 0.10", runtime: false},
343+
{:parent_git_install, git: "https://example.com/parent.git"},
344+
{:parent_versioned_install, "~> 1.0"}
345+
]
346+
}
347+
end
348+
349+
def igniter(igniter), do: igniter
350+
end
351+
352+
defmodule Elixir.Mix.Tasks.ChildInstallsAllFormats do
353+
use Igniter.Mix.Task
354+
355+
def info(_argv, _parent) do
356+
%Igniter.Mix.Task.Info{
357+
installs: [
358+
{:credo, "~> 1.7", only: [:dev, :test]},
359+
{:child_git_install, git: "https://example.com/child.git"},
360+
{:child_versioned_install, "~> 2.0"}
361+
]
362+
}
363+
end
364+
365+
def igniter(igniter), do: igniter
366+
end
367+
368+
setup do
369+
Elixir.Mix.Task.load_all()
370+
:ok
371+
end
372+
373+
test "adds_deps merges cleanly across every supported mix dep format" do
374+
Igniter.Util.Info.validate!(
375+
[],
376+
Mix.Tasks.ParentAddsDepsAllFormats.info(nil, nil),
377+
"parent_adds_deps_all_formats"
378+
)
379+
end
380+
381+
test "installs merges cleanly across every supported mix dep format" do
382+
Igniter.Util.Info.validate!(
383+
[],
384+
Mix.Tasks.ParentInstallsAllFormats.info(nil, nil),
385+
"parent_installs_all_formats"
386+
)
387+
end
388+
end
389+
296390
describe "parse_argv/1" do
297391
defmodule ExampleTaskWithOverriddenParseArgv do
298392
use Igniter.Mix.Task

0 commit comments

Comments
 (0)