Browse Source

Merge pull request #2031 from ferd/fix-transient-upgrade-locks

Fix handling of transient locks during upgrade
pull/2034/head
Fred Hebert 6 years ago
committed by GitHub
parent
commit
13dccdaab4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 71 additions and 4 deletions
  1. +24
    -2
      src/rebar_prv_install_deps.erl
  2. +47
    -2
      test/rebar_upgrade_SUITE.erl

+ 24
- 2
src/rebar_prv_install_deps.erl View File

@ -206,10 +206,23 @@ maybe_lock(Profile, AppInfo, Seen, State, Level) ->
default ->
case sets:is_element(Name, Seen) of
false ->
%% Check whether the currently existing lock is
%% deeper than the current one (which can happen
%% during an upgrade). If the current app is
%% shallower than the existing lock, replace the
%% existing lock. This prevents weird transient
%% lock-tree states (which would self-heal on a
%% later run) after a `rebar3 upgrade <app>'
%% command when a deep dep switches lineages for
%% another newer parent.
Locks = rebar_state:lock(State),
case lists:any(fun(App) -> rebar_app_info:name(App) =:= Name end, Locks) of
true ->
case find_app_and_level_by_name(Locks, Name) of
{ok, _App, LockLvl} when LockLvl =< Level ->
{sets:add_element(Name, Seen), State};
{ok, App, _LockLvl} ->
LockedApp = rebar_app_info:dep_level(AppInfo, Level),
{sets:add_element(Name, Seen),
rebar_state:lock(State, [LockedApp | Locks -- [App]])};
false ->
{sets:add_element(Name, Seen),
rebar_state:lock(State, rebar_app_info:dep_level(AppInfo, Level))}
@ -426,3 +439,12 @@ not_needs_compile(App) ->
not(rebar_app_info:is_checkout(App))
andalso rebar_app_info:valid(App)
andalso rebar_app_info:has_all_artifacts(App) =:= true.
find_app_and_level_by_name([], _) ->
false;
find_app_and_level_by_name([App|Apps], Name) ->
case rebar_app_info:name(App) of
Name -> {ok, App, rebar_app_info:dep_level(App)};
_ -> find_app_and_level_by_name(Apps, Name)
end.

+ 47
- 2
test/rebar_upgrade_SUITE.erl View File

@ -12,7 +12,7 @@ groups() ->
tree_a, tree_b, tree_c, tree_c2, tree_cj, tree_ac, tree_all,
delete_d, promote, stable_lock, fwd_lock,
compile_upgrade_parity, umbrella_config,
profiles, profiles_exclusion]},
profiles, profiles_exclusion, tree_migration]},
{git, [], [{group, all}]},
{pkg, [], [{group, all}]}].
@ -512,7 +512,15 @@ upgrades(profiles_exclusion) ->
["A","B","C","E","F","H"],
{"A", [{"A","1.0.0"}, "D", {"E","3.0.0"},
{"B","2.0.0"}, {"F","2.0.0"}, "G",
{"C","1.0.0"}, {"H","4.0.0"}, "I"]}}.
{"C","1.0.0"}, {"H","4.0.0"}, "I"]}};
upgrades(tree_migration) ->
{[{"B", "1.0.0", []},
{"C", "1.0.0", [{"D","1.0.0",[{"E", "1.0.0", []}]}]}],
[{"B", "2.0.0", [{"E","1.0.0",[]}]},
{"C", "1.0.0", [{"D","1.0.0",[]}]}],
["B"],
{"B", [{"A","1.0.0"}, "D", {"E","1.0.0"},
{"B","2.0.0"}]}}.
%% TODO: add a test that verifies that unlocking files and then
%% running the upgrade code is enough to properly upgrade things.
@ -702,6 +710,43 @@ profiles(Config) ->
profiles_exclusion(Config) -> profiles(Config).
tree_migration(Config) ->
apply(?config(mock, Config), []),
ConfigPath = ?config(rebarconfig, Config),
{ok, RebarConfig} = file:consult(ConfigPath),
%% Install dependencies before re-mocking for an upgrade
rebar_test_utils:run_and_check(Config, RebarConfig, ["lock"], {ok, []}),
{App, _Unlocks} = ?config(expected, Config),
meck:new(rebar_prv_upgrade, [passthrough]),
meck:expect(rebar_prv_upgrade, do, fun(S) ->
apply(?config(mock_update, Config), []),
meck:passthrough([S])
end),
NewRebarConf = rebar_test_utils:create_config(filename:dirname(ConfigPath),
[{deps, ?config(next_top_deps, Config)}]),
{ok, NewRebarConfig} = file:consult(NewRebarConf),
{ok, NewState} = rebar_test_utils:run_and_check(
Config, NewRebarConfig, ["upgrade", App], return
),
meck:unload(rebar_prv_upgrade),
%% Check that the internal state properly has E with a lock-level
%% of 1.
Locks = rebar_state:lock(NewState),
[Locked] = [X || X <- Locks, rebar_app_info:name(X) =:= <<"E">>],
?assertEqual(1, rebar_app_info:dep_level(Locked)),
%% Check that the lockfile on disk agrees
AppDir = ?config(apps, Config),
Lockfile = filename:join([AppDir, "rebar.lock"]),
case file:consult(Lockfile) of
{ok, [{_Vsn, Prop}|_]} -> % packages
?assertMatch({<<"E">>, _, 1}, lists:keyfind(<<"E">>, 1, Prop));
{ok, [Prop]} -> % git source
?assertMatch({<<"E">>, _, 1}, lists:keyfind(<<"E">>, 1, Prop))
end,
ok.
run(Config) ->
apply(?config(mock, Config), []),
ConfigPath = ?config(rebarconfig, Config),

Loading…
Cancel
Save