Skip to content

Commit 860db84

Browse files
author
José Valim
committed
Do not run converger if dependencies have diverged
The remote converger should not worry about the dep status code in case they are invalid. We have also changed mix deps.get/deps.update to raise at the end in case any of the dependencies diverged. Closes hexpm/hex#169
1 parent 77e142d commit 860db84

File tree

7 files changed

+90
-14
lines changed

7 files changed

+90
-14
lines changed

lib/mix/lib/mix/dep.ex

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -281,12 +281,17 @@ defmodule Mix.Dep do
281281
282282
Available dependencies are the ones that can be loaded.
283283
"""
284-
def available?(%Mix.Dep{status: {:unavailable, _}}), do: false
285-
def available?(%Mix.Dep{status: {:overridden, _}}), do: false
286-
def available?(%Mix.Dep{status: {:diverged, _}}), do: false
287-
def available?(%Mix.Dep{status: {:divergedreq, _}}), do: false
288-
def available?(%Mix.Dep{status: {:divergedonly, _}}), do: false
289-
def available?(%Mix.Dep{}), do: true
284+
def available?(%Mix.Dep{status: {:unavailable, _}}), do: false
285+
def available?(dep), do: not diverged?(dep)
286+
287+
@doc """
288+
Checks if a dependency has diverged.
289+
"""
290+
def diverged?(%Mix.Dep{status: {:overridden, _}}), do: true
291+
def diverged?(%Mix.Dep{status: {:diverged, _}}), do: true
292+
def diverged?(%Mix.Dep{status: {:divergedreq, _}}), do: true
293+
def diverged?(%Mix.Dep{status: {:divergedonly, _}}), do: true
294+
def diverged?(%Mix.Dep{}), do: false
290295

291296
@doc """
292297
Formats a dependency for printing.

lib/mix/lib/mix/dep/converger.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ defmodule Mix.Dep.Converger do
6969
# iteration.
7070
{deps, rest, lock} =
7171
all(main, only, [], current, callback, acc, lock, fn dep ->
72-
if (converger = Mix.RemoteConverger.get) && converger.remote?(dep) do
72+
if (remote = Mix.RemoteConverger.get) && remote.remote?(dep) do
7373
{:loaded, dep}
7474
else
7575
{:unloaded, dep, nil}
@@ -79,10 +79,11 @@ defmodule Mix.Dep.Converger do
7979
# Filter deps per environment once more. If the filtered
8080
# dependencies had no conflicts, they are removed now.
8181
{deps, _} = Mix.Dep.Loader.partition_by_env(deps, opts)
82+
diverged? = Enum.any?(deps, &Mix.Dep.diverged?/1)
8283

8384
# Run remote converger if one is available and rerun Mix's
8485
# converger with the new information
85-
if remote = Mix.RemoteConverger.get do
86+
if not diverged? && (remote = Mix.RemoteConverger.get) do
8687
# If there is a lock, it means we are doing a get/update
8788
# and we need to hit the remote converger which do external
8889
# requests and what not. In case of deps.check, deps and so

lib/mix/lib/mix/dep/fetcher.ex

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,11 @@ defmodule Mix.Dep.Fetcher do
105105
# leave out things that could not be fetched and save it.
106106
lock = Map.merge(old_lock, new_lock)
107107
Mix.Dep.Lock.write(lock)
108-
109108
mark_as_fetched(deps)
109+
110+
# See if any of the deps diverged and abort.
111+
show_diverged!(Enum.filter(all_deps, &Mix.Dep.diverged?/1))
112+
110113
{apps, all_deps}
111114
end
112115

@@ -144,4 +147,17 @@ defmodule Mix.Dep.Fetcher do
144147
if is_binary(app), do: String.to_atom(app), else: app
145148
end)
146149
end
150+
151+
defp show_diverged!([]), do: :ok
152+
defp show_diverged!(deps) do
153+
shell = Mix.shell
154+
shell.error "Dependencies have diverged:"
155+
156+
Enum.each deps, fn(dep) ->
157+
shell.error "* #{Mix.Dep.format_dep dep}"
158+
shell.error " #{Mix.Dep.format_status dep}"
159+
end
160+
161+
Mix.raise "Can't continue due to errors on dependencies"
162+
end
147163
end

lib/mix/lib/mix/dep/umbrella.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ defmodule Mix.Dep.Umbrella do
1414
paths
1515
|> extract_umbrella
1616
|> filter_umbrella(config[:apps])
17-
|> to_umbrella_dep(build)
17+
|> to_umbrella_dep(build, Path.absname("mix.exs"))
1818
else
1919
[]
2020
end
@@ -48,7 +48,7 @@ defmodule Mix.Dep.Umbrella do
4848
for {app, _} = pair <- pairs, app in apps, do: pair
4949
end
5050

51-
defp to_umbrella_dep(paths, build) do
51+
defp to_umbrella_dep(paths, build, from) do
5252
Enum.map paths, fn({app, path}) ->
5353
opts = [path: path, dest: Path.expand(path), from_umbrella: true,
5454
env: Mix.env, build: Path.join([build, "lib", Atom.to_string(app)])]
@@ -58,6 +58,7 @@ defmodule Mix.Dep.Umbrella do
5858
requirement: nil,
5959
manager: :mix,
6060
status: {:ok, nil},
61+
from: from,
6162
opts: opts}
6263
end
6364
end

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ defmodule Mix.Tasks.Deps.Check do
7070
end
7171
end
7272

73-
7473
defp partition([dep|deps], not_ok, compile) do
7574
cond do
7675
from_umbrella?(dep) -> partition(deps, not_ok, compile)

lib/mix/test/mix/dep_test.exs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,41 @@ defmodule Mix.DepTest do
208208
Mix.RemoteConverger.register(nil)
209209
end
210210

211+
defmodule RaiseRemoteConverger do
212+
@behaviour Mix.RemoteConverger
213+
214+
def remote?(_app), do: false
215+
216+
def converge(_deps, lock) do
217+
Process.put(:remote_converger, true)
218+
lock
219+
end
220+
221+
def deps(_deps, _lock) do
222+
[]
223+
end
224+
end
225+
226+
test "remote converger is not invoked if deps diverge" do
227+
deps = [{:deps_repo, "0.1.0", path: "custom/deps_repo"},
228+
{:git_repo, "0.2.0", git: MixTest.Case.fixture_path("git_repo"), only: :test}]
229+
230+
with_deps deps, fn ->
231+
Mix.RemoteConverger.register(RaiseRemoteConverger)
232+
233+
in_fixture "deps_status", fn ->
234+
assert_raise Mix.Error, fn ->
235+
Mix.Tasks.Deps.Get.run([])
236+
end
237+
238+
assert_received {:mix_shell, :error, ["Dependencies have diverged:"]}
239+
refute Process.get(:remote_converger)
240+
end
241+
end
242+
after
243+
Mix.RemoteConverger.register(nil)
244+
end
245+
211246
## Only handling
212247

213248
test "only extract deps matching environment" do

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,27 @@ defmodule Mix.Tasks.DepsTest do
344344
end
345345
end
346346

347+
test "fails on diverged dependencies on get/update" do
348+
Mix.Project.push ConflictDepsApp
349+
350+
in_fixture "deps_status", fn ->
351+
assert_raise Mix.Error, fn ->
352+
Mix.Tasks.Deps.Check.run []
353+
end
354+
assert_received {:mix_shell, :error, [" the dependency git_repo in mix.exs is overriding a child dependency" <> _]}
355+
356+
assert_raise Mix.Error, fn ->
357+
Mix.Tasks.Deps.Get.run []
358+
end
359+
assert_received {:mix_shell, :error, [" the dependency git_repo in mix.exs is overriding a child dependency" <> _]}
360+
361+
assert_raise Mix.Error, fn ->
362+
Mix.Tasks.Deps.Update.run ["--all"]
363+
end
364+
assert_received {:mix_shell, :error, [" the dependency git_repo in mix.exs is overriding a child dependency" <> _]}
365+
end
366+
end
367+
347368
test "fails on diverged dependencies on check" do
348369
Mix.Project.push DivergedDepsApp
349370

@@ -536,7 +557,6 @@ defmodule Mix.Tasks.DepsTest do
536557
app: :raw_sample,
537558
version: "0.1.0",
538559
deps: [
539-
{:deps_repo, "0.1.0", path: "custom/deps_repo", compile: false},
540560
{:git_repo, "0.1.0", git: MixTest.Case.fixture_path("git_repo"), compile: false}
541561
]
542562
]
@@ -548,7 +568,6 @@ defmodule Mix.Tasks.DepsTest do
548568

549569
in_fixture "deps_status", fn ->
550570
Mix.Tasks.Deps.Compile.run []
551-
refute_received {:mix_shell, :info, ["==> deps_repo"]}
552571
refute_received {:mix_shell, :info, ["==> git_repo"]}
553572
end
554573
end

0 commit comments

Comments
 (0)