Skip to content

Commit 4e73e6c

Browse files
committed
Support filtering mix deps output
Makes it easier to report versions of specific dependencies by allowing users (or tools) to pass dependency names as arguments to `mix deps`. Warns when a dependency is not found, similar to `mix deps.unlock`. Proposal: https://groups.google.com/g/elixir-lang-core/c/5tlLZ1yu4rQ/m/g7Z8fNWiBwAJ
1 parent 18c81d6 commit 4e73e6c

File tree

2 files changed

+74
-4
lines changed

2 files changed

+74
-4
lines changed

lib/mix/lib/mix/tasks/deps.ex

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,19 +186,37 @@ defmodule Mix.Tasks.Deps do
186186
187187
* `--all` - lists all dependencies, regardless of specified environment
188188
189+
## Filtering dependencies (since v1.20.0)
190+
191+
You can list specific dependencies by passing their names as arguments:
192+
193+
$ mix deps phoenix phoenix_live_view
194+
195+
This is particularly useful when you need to quickly check versions for
196+
bug reports or similar tasks.
197+
189198
"""
190199

191200
@impl true
192201
def run(args) do
193202
Mix.Project.get!()
194-
{opts, _, _} = OptionParser.parse(args, switches: [all: :boolean])
203+
{opts, apps, _} = OptionParser.parse(args, switches: [all: :boolean])
195204
loaded_opts = if opts[:all], do: [], else: [env: Mix.env(), target: Mix.target()]
196205

206+
deps = Mix.Dep.Converger.converge(loaded_opts)
207+
208+
# Sort deps when showing all; preserve input order when filtering
209+
{deps, unknown} =
210+
if apps == [] do
211+
{Enum.sort_by(deps, & &1.app), []}
212+
else
213+
apps = Enum.map(apps, &String.to_atom/1)
214+
filter_deps(deps, apps)
215+
end
216+
197217
shell = Mix.shell()
198218

199-
Mix.Dep.Converger.converge(loaded_opts)
200-
|> Enum.sort_by(& &1.app)
201-
|> Enum.each(fn dep ->
219+
Enum.each(deps, fn dep ->
202220
%Mix.Dep{scm: scm, manager: manager} = dep
203221
dep = check_lock(dep)
204222
extra = if manager, do: " (#{manager})", else: ""
@@ -211,5 +229,18 @@ defmodule Mix.Tasks.Deps do
211229

212230
shell.info(" #{format_status(dep)}")
213231
end)
232+
233+
# Warnings last for visibility
234+
for app <- unknown do
235+
shell.error("warning: unknown dependency #{app}")
236+
end
237+
end
238+
239+
defp filter_deps(deps, apps) do
240+
deps_map = Map.new(deps, fn dep -> {dep.app, dep} end)
241+
unknown = Enum.reject(apps, &Map.has_key?(deps_map, &1))
242+
243+
# Preserve apps order
244+
{Enum.map(apps -- unknown, &deps_map[&1]), unknown}
214245
end
215246
end

lib/mix/test/mix/tasks/deps_test.exs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,45 @@ defmodule Mix.Tasks.DepsTest do
100100
end)
101101
end
102102

103+
test "filters dependencies by name" do
104+
in_fixture("deps_status", fn ->
105+
Mix.Project.push(DepsApp)
106+
107+
Mix.Tasks.Deps.run(["ok"])
108+
109+
assert_received {:mix_shell, :info, ["* ok (https://github.com/elixir-lang/ok.git) (mix)"]}
110+
refute_received {:mix_shell, :info, ["* invalidvsn" <> _]}
111+
refute_received {:mix_shell, :info, ["* invalidapp" <> _]}
112+
refute_received {:mix_shell, :info, ["* noappfile" <> _]}
113+
refute_received {:mix_shell, :info, ["* nosemver" <> _]}
114+
end)
115+
end
116+
117+
test "filters multiple dependencies by name" do
118+
in_fixture("deps_status", fn ->
119+
Mix.Project.push(DepsApp)
120+
121+
Mix.Tasks.Deps.run(["ok", "nosemver"])
122+
123+
assert_received {:mix_shell, :info, ["* ok (https://github.com/elixir-lang/ok.git) (mix)"]}
124+
assert_received {:mix_shell, :info, ["* nosemver (deps/nosemver)"]}
125+
refute_received {:mix_shell, :info, ["* invalidvsn" <> _]}
126+
refute_received {:mix_shell, :info, ["* invalidapp" <> _]}
127+
refute_received {:mix_shell, :info, ["* noappfile" <> _]}
128+
end)
129+
end
130+
131+
test "warns when filtering for unknown dependencies" do
132+
in_fixture("deps_status", fn ->
133+
Mix.Project.push(DepsApp)
134+
135+
Mix.Tasks.Deps.run(["ok", "notmydep"])
136+
137+
assert_received {:mix_shell, :info, ["* ok (https://github.com/elixir-lang/ok.git) (mix)"]}
138+
assert_received {:mix_shell, :error, ["warning: unknown dependency notmydep"]}
139+
end)
140+
end
141+
103142
test "prints list of dependencies and their status, including req mismatches and custom apps" do
104143
in_fixture("deps_status", fn ->
105144
Mix.Project.push(ReqDepsApp)

0 commit comments

Comments
 (0)