diff --git a/lib/mix/tasks/hex.outdated.ex b/lib/mix/tasks/hex.outdated.ex index eb57ecf6..6dc185f1 100644 --- a/lib/mix/tasks/hex.outdated.ex +++ b/lib/mix/tasks/hex.outdated.ex @@ -44,10 +44,11 @@ defmodule Mix.Tasks.Hex.Outdated do * `--all` - shows all outdated packages, including children of packages defined in `mix.exs` * `--pre` - include pre-releases when checking for newer versions * `--within-requirements` - exit with non-zero code only if requirements specified in `mix.exs` is met. + * `--sort ` - sort results by the given column. Currently supports `status`. """ @behaviour Hex.Mix.TaskDescription - @switches [all: :boolean, pre: :boolean, within_requirements: :boolean] + @switches [all: :boolean, pre: :boolean, within_requirements: :boolean, sort: :string] @impl true def run(args) do @@ -175,7 +176,8 @@ defmodule Mix.Tasks.Hex.Outdated do |> Enum.sort() |> get_versions(deps, lock, opts[:pre]) - values = Enum.map(versions, &format_all_row/1) + values = versions |> Enum.map(&format_all_row/1) |> maybe_sort_by(opts[:sort]) + diff_links = Enum.map(versions, &build_diff_link/1) |> Enum.reject(&is_nil/1) if Enum.empty?(values) do @@ -207,6 +209,20 @@ defmodule Mix.Tasks.Hex.Outdated do end end + defp maybe_sort_by(values, "status") do + status_order = %{ + "Up-to-date" => 1, + "Update not possible" => 2, + "Update possible" => 3 + } + + Enum.sort_by(values, fn [_package, _lock, _latest, [_color, status]] -> + Map.fetch!(status_order, status) + end) + end + + defp maybe_sort_by(values, _), do: values + defp get_versions(dep_names, deps, lock, pre?) do Enum.flat_map(dep_names, fn name -> case Hex.Utils.lock(lock[name]) do diff --git a/test/mix/tasks/hex.outdated_test.exs b/test/mix/tasks/hex.outdated_test.exs index 09298861..7b75e772 100644 --- a/test/mix/tasks/hex.outdated_test.exs +++ b/test/mix/tasks/hex.outdated_test.exs @@ -154,6 +154,65 @@ defmodule Mix.Tasks.Hex.OutdatedTest do end) end + test "outdated --all --sort status" do + Mix.Project.push(OutdatedApp.MixProject) + + in_tmp(fn -> + set_home_tmp() + Mix.Dep.Lock.write(%{bar: {:hex, :bar, "0.1.0"}, foo: {:hex, :foo, "0.1.0"}}) + + Mix.Task.run("deps.get") + flush() + + assert catch_throw(Mix.Task.run("hex.outdated", ["--all", "--sort", "status"])) == + {:exit_code, 1} + + _bar = + [ + [:bright, "bar", :reset], + [" ", "0.1.0", :reset], + [" ", :green, "0.1.0", :reset], + [" ", :green, "Up-to-date", :reset], + " " + ] + |> IO.ANSI.format() + |> List.to_string() + + _foo = + [ + [:bright, "foo", :reset], + [" ", "0.1.0", :reset], + [" ", :red, "0.1.1", :reset], + [" ", :yellow, "Update possible", :reset], + " " + ] + |> IO.ANSI.format() + |> List.to_string() + + _ex_doc = + [ + [:bright, "ex_doc", :reset], + [" ", "0.0.1", :reset], + [" ", :red, "0.1.0", :reset], + [" ", :red, "Update not possible", :reset], + " " + ] + |> IO.ANSI.format() + |> List.to_string() + + lines = flush() + extracted_statuses = extract_statuses(lines) + + assert extracted_statuses == [ + "Up-to-date", + "Update not possible", + "Update not possible", + "Update not possible", + "Update possible" + ] + end) + end + test "outdated --all with multiple dependent packages" do Mix.Project.push(OutdatedMultiDeps.MixProject) @@ -509,4 +568,12 @@ defmodule Mix.Tasks.Hex.OutdatedTest do end) end) end + + defp extract_statuses(lines) do + Enum.flat_map(lines, fn {_, _, [line]} -> + ~r/Up-to-date|Update not possible|Update possible/ + |> Regex.scan(line) + |> List.flatten() + end) + end end