Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
José Valim
committed
Feb 4, 2016
1 parent
0db3a62
commit 8ff3275
Showing
5 changed files
with
190 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,76 @@ | |||
defmodule Mix.Tasks.Deps.Tree do | |||
use Mix.Task | |||
|
|||
@shortdoc "Prints the dependency tree" | |||
|
|||
@moduledoc """ | |||
Prints the dependency tree. | |||
mix deps.tree | |||
If no dependency is given, it uses the tree defined in the `mix.exs` file. | |||
## Command line options | |||
* `--only` - the enviroment to show dependencies for | |||
* `--exclude` - exclude dependencies which you do not want to see printed. | |||
* `--pretty` - use Unicode codepoints for formatting the tree. | |||
Defaults to true except on Windows. | |||
""" | |||
@switches [only: :string, exclude: :keep, pretty: :boolean] | |||
|
|||
@spec run(OptionParser.argv) :: :ok | |||
def run(args) do | |||
Mix.Project.get! | |||
{opts, args, _} = OptionParser.parse(args, switches: @switches) | |||
|
|||
deps_opts = if only = opts[:only], do: [env: :"#{only}"], else: [] | |||
deps = Mix.Dep.loaded(deps_opts) | |||
excluded = Keyword.get_values(opts, :exclude) |> Enum.map(&String.to_atom/1) | |||
top_level = Enum.filter(deps, & &1.top_level) | |||
|
|||
root = | |||
case args do | |||
[] -> | |||
Mix.Project.config[:app] || Mix.raise("no application given and none found in mix.exs file") | |||
[app] -> | |||
app = String.to_atom(app) | |||
find_dep(deps, app) || Mix.raise("could not find dependency #{app}") | |||
end | |||
|
|||
Mix.Utils.print_tree([root], fn | |||
%Mix.Dep{app: app} = dep -> | |||
deps = | |||
# Do not show dependencies if they were | |||
# already show at the top level | |||
if not dep.top_level && find_dep(top_level, app) do | |||
[] | |||
else | |||
find_dep(deps, app).deps | |||
end | |||
{format_dep(dep), exclude(deps, excluded)} | |||
app -> | |||
{Atom.to_string(app), exclude(top_level, excluded)} | |||
end, opts) | |||
end | |||
|
|||
defp exclude(deps, excluded) do | |||
Enum.reject deps, & &1.app in excluded | |||
end | |||
|
|||
defp format_dep(%{app: app, scm: scm, requirement: requirement, opts: opts}) do | |||
override = if opts[:override], do: "#{IO.ANSI.bright} *override*#{IO.ANSI.normal}", else: "" | |||
"#{app}#{requirement(requirement)} (#{scm.format(opts)})#{override}" | |||
end | |||
|
|||
defp requirement(nil), do: "" | |||
defp requirement(%Regex{} = regex), do: " #{inspect regex}" | |||
defp requirement(binary) when is_binary(binary), do: " #{binary}" | |||
|
|||
defp find_dep(deps, app) do | |||
Enum.find(deps, & &1.app == app) | |||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,98 @@ | |||
Code.require_file "../../test_helper.exs", __DIR__ | |||
|
|||
defmodule Mix.Tasks.Deps.TreeTest do | |||
use MixTest.Case | |||
|
|||
defmodule ConvergedDepsApp do | |||
def project do | |||
[ | |||
app: :sample, | |||
version: "0.1.0", | |||
deps: [ | |||
{:deps_on_git_repo, "0.2.0", git: fixture_path("deps_on_git_repo")}, | |||
{:git_repo, ">= 0.1.0", git: MixTest.Case.fixture_path("git_repo")} | |||
] | |||
] | |||
end | |||
end | |||
|
|||
defmodule OverridenDepsApp do | |||
def project do | |||
[ | |||
app: :sample, | |||
version: "0.1.0", | |||
deps: [ | |||
{:deps_on_git_repo, ~r"0.2.0", git: fixture_path("deps_on_git_repo"), only: :test}, | |||
{:git_repo, git: MixTest.Case.fixture_path("git_repo"), override: true} | |||
] | |||
] | |||
end | |||
end | |||
|
|||
test "shows the dependency tree", context do | |||
Mix.Project.push ConvergedDepsApp | |||
|
|||
in_tmp context.test, fn -> | |||
Mix.Tasks.Deps.Tree.run(["--pretty"]) | |||
assert_received {:mix_shell, :info, ["sample"]} | |||
assert_received {:mix_shell, :info, ["├── git_repo >= 0.1.0 (" <> _]} | |||
assert_received {:mix_shell, :info, ["└── deps_on_git_repo 0.2.0 (" <> _]} | |||
refute_received {:mix_shell, :info, [" └── git_repo (" <> _]} | |||
|
|||
Mix.Tasks.Deps.Get.run([]) | |||
Mix.Tasks.Deps.Tree.run(["--pretty"]) | |||
assert_received {:mix_shell, :info, ["sample"]} | |||
assert_received {:mix_shell, :info, ["├── git_repo >= 0.1.0 (" <> _]} | |||
assert_received {:mix_shell, :info, ["└── deps_on_git_repo 0.2.0 (" <> _]} | |||
assert_received {:mix_shell, :info, [" └── git_repo (" <> _]} | |||
end | |||
end | |||
|
|||
test "shows the given dependency", context do | |||
Mix.Project.push ConvergedDepsApp | |||
|
|||
in_tmp context.test, fn -> | |||
assert_raise Mix.Error, "could not find dependency unknown", fn -> | |||
Mix.Tasks.Deps.Tree.run(["--pretty", "unknown"]) | |||
end | |||
|
|||
Mix.Tasks.Deps.Tree.run(["--pretty", "deps_on_git_repo"]) | |||
assert_received {:mix_shell, :info, ["deps_on_git_repo 0.2.0 (" <> _]} | |||
refute_received {:mix_shell, :info, ["└── git_repo (" <> _]} | |||
end | |||
end | |||
|
|||
test "shows overriden deps", context do | |||
Mix.Project.push OverridenDepsApp | |||
|
|||
in_tmp context.test, fn -> | |||
Mix.Tasks.Deps.Tree.run(["--pretty"]) | |||
assert_received {:mix_shell, :info, ["sample"]} | |||
assert_received {:mix_shell, :info, ["├── git_repo (" <> msg]} | |||
assert_received {:mix_shell, :info, ["└── deps_on_git_repo ~r/0.2.0/ (" <> _]} | |||
assert msg =~ "*override*" | |||
end | |||
end | |||
|
|||
test "excludes the given deps", context do | |||
Mix.Project.push OverridenDepsApp | |||
|
|||
in_tmp context.test, fn -> | |||
Mix.Tasks.Deps.Tree.run(["--pretty", "--exclude", "deps_on_git_repo"]) | |||
assert_received {:mix_shell, :info, ["sample"]} | |||
assert_received {:mix_shell, :info, ["└── git_repo (" <> _]} | |||
refute_received {:mix_shell, :info, ["└── deps_on_git_repo ~r/0.2.0/ (" <> _]} | |||
end | |||
end | |||
|
|||
test "shows a particular environment", context do | |||
Mix.Project.push OverridenDepsApp | |||
|
|||
in_tmp context.test, fn -> | |||
Mix.Tasks.Deps.Tree.run(["--pretty", "--only", "prod"]) | |||
assert_received {:mix_shell, :info, ["sample"]} | |||
assert_received {:mix_shell, :info, ["└── git_repo (" <> _]} | |||
refute_received {:mix_shell, :info, ["└── deps_on_git_repo ~r/0.2.0/ (" <> _]} | |||
end | |||
end | |||
end |