diff --git a/lib/mix/tasks/hex.outdated.ex b/lib/mix/tasks/hex.outdated.ex index ebf34be7..c2b9a966 100644 --- a/lib/mix/tasks/hex.outdated.ex +++ b/lib/mix/tasks/hex.outdated.ex @@ -198,21 +198,11 @@ defmodule Mix.Tasks.Hex.Outdated do diff_message = maybe_diff_message(diff_links) Hex.Shell.info(["\n", base_message, diff_message]) - any_outdated? = any_outdated?(versions) - req_met? = any_req_matches?(versions) + outdated = outdated(versions) + any_updatable? = any_possible_to_update?(outdated) - cond do - any_outdated? && opts[:within_requirements] && req_met? -> - Mix.Tasks.Hex.set_exit_code(1) - - any_outdated? && opts[:within_requirements] && not req_met? -> - nil - - any_outdated? -> - Mix.Tasks.Hex.set_exit_code(1) - - true -> - nil + if outdated != [] && (!opts[:within_requirements] || any_updatable?) do + Mix.Tasks.Hex.set_exit_code(1) end end end @@ -325,8 +315,8 @@ defmodule Mix.Tasks.Hex.Outdated do defp version_match?(_version, nil), do: true defp version_match?(version, req), do: Version.match?(version, req) - defp any_outdated?(versions) do - Enum.any?(versions, fn [_package, _dep_only, lock, latest, _requirements] -> + defp outdated(versions) do + Enum.filter(versions, fn [_package, _dep_only, lock, latest, _requirements] -> Version.compare(lock, latest) == :lt end) end @@ -355,8 +345,8 @@ defmodule Mix.Tasks.Hex.Outdated do end end - defp any_req_matches?(versions) do - Enum.any?(versions, fn [_package, _dep_only, _lock, latest, requirements] -> + defp any_possible_to_update?(outdated_versions) do + Enum.any?(outdated_versions, fn [_package, _dep_only, _lock, latest, requirements] -> req_matches?(requirements, latest) end) end diff --git a/test/mix/tasks/hex.outdated_test.exs b/test/mix/tasks/hex.outdated_test.exs index 0bf5e8a0..c98eff79 100644 --- a/test/mix/tasks/hex.outdated_test.exs +++ b/test/mix/tasks/hex.outdated_test.exs @@ -52,6 +52,19 @@ defmodule Mix.Tasks.Hex.OutdatedTest do end end + defmodule UpdateNotPossibleApp.MixProject do + def project do + [ + app: :outdated_app, + version: "0.0.1", + deps: [ + {:baz, "0.1.0"}, + {:bar, "0.1.0"} + ] + ] + end + end + defmodule WithoutHexDeps.MixProject do def project do [ @@ -323,6 +336,61 @@ defmodule Mix.Tasks.Hex.OutdatedTest do end) end + test "outdated --all --within-requirements (update not possible)" do + Mix.Project.push(UpdateNotPossibleApp.MixProject) + + in_tmp(fn -> + set_home_tmp() + Mix.Dep.Lock.write(%{foo: {:hex, :foo, "0.1.0"}}) + + Mix.Task.run("deps.get") + flush() + + assert Mix.Task.run("hex.outdated", ["--all", "--within-requirements"]) == + nil + + bar = + [ + [:bright, "bar", :reset], + [" ", :reset], + [" ", "0.1.0", :reset], + [" ", :green, "0.1.0", :reset], + [" ", :green, "Up-to-date", :reset], + " " + ] + |> IO.ANSI.format() + |> List.to_string() + + baz = + [ + [:bright, "baz", :reset], + [" ", :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], + [" ", :reset], + [" ", "0.1.0", :reset], + [" ", :red, "0.1.1", :reset], + [" ", :red, "Update not possible", :reset], + " " + ] + |> IO.ANSI.format() + |> List.to_string() + + assert_received {:mix_shell, :info, [^bar]} + assert_received {:mix_shell, :info, [^baz]} + assert_received {:mix_shell, :info, [^foo]} + end) + end + test "outdated --all --within-requirements (not outdated)" do Mix.Project.push(NotOutdatedApp.MixProject) @@ -709,7 +777,7 @@ defmodule Mix.Tasks.Hex.OutdatedTest do catch_throw(Mix.Task.run("hex.outdated", ["--only", "dev,test"])) - # Should show dependencies with :only set to :dev or :test + # Should show dependencies with :only set to :dev or :test # This should include ex_doc (dev) and plug (test) # bypass has [:dev, :test] which displays as "dev,test" but should match both "dev" and "test" individually output_lines =