summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/rebar_prv_upgrade.erl46
-rw-r--r--test/rebar_upgrade_SUITE.erl93
2 files changed, 130 insertions, 9 deletions
diff --git a/src/rebar_prv_upgrade.erl b/src/rebar_prv_upgrade.erl
index 34631ff..2de30e4 100644
--- a/src/rebar_prv_upgrade.erl
+++ b/src/rebar_prv_upgrade.erl
@@ -70,7 +70,9 @@ do(State) ->
is_atom(Dep) orelse is_atom(element(1, Dep))],
Names = parse_names(ec_cnv:to_binary(proplists:get_value(package, Args, <<"">>)), Locks),
DepsDict = deps_dict(rebar_state:all_deps(State)),
- case prepare_locks(Names, Deps, Locks, [], DepsDict) of
+ AltDeps = find_non_default_deps(Deps, State),
+ FilteredNames = cull_default_names_if_profiles(Names, Deps, State),
+ case prepare_locks(FilteredNames, Deps, Locks, [], DepsDict, AltDeps) of
{error, Reason} ->
{error, Reason};
{Locks0, _Unlocks0} ->
@@ -115,20 +117,45 @@ parse_names(Bin, Locks) ->
Other -> Other
end.
-prepare_locks([], _, Locks, Unlocks, _Dict) ->
+%% Find alternative deps in non-default profiles since they may
+%% need to be passed through (they are never locked)
+find_non_default_deps(Deps, State) ->
+ AltProfiles = rebar_state:current_profiles(State) -- [default],
+ AltProfileDeps = lists:append([
+ rebar_state:get(State, {deps, Profile}, []) || Profile <- AltProfiles]
+ ),
+ [Dep || Dep <- AltProfileDeps,
+ is_atom(Dep) orelse is_atom(element(1, Dep))
+ andalso not lists:member(Dep, Deps)].
+
+%% If any alt profiles are used, remove the default profiles from
+%% the upgrade list and warn about it.
+cull_default_names_if_profiles(Names, Deps, State) ->
+ case rebar_state:current_profiles(State) of
+ [default] ->
+ Names;
+ _ ->
+ ?INFO("Dependencies in the default profile will not be upgraded", []),
+ lists:filter(fun(Name) ->
+ AtomName = binary_to_atom(Name, utf8),
+ rebar_utils:tup_find(AtomName, Deps) == false
+ end, Names)
+ end.
+
+prepare_locks([], _, Locks, Unlocks, _Dict, _AltDeps) ->
{Locks, Unlocks};
-prepare_locks([Name|Names], Deps, Locks, Unlocks, Dict) ->
+prepare_locks([Name|Names], Deps, Locks, Unlocks, Dict, AltDeps) ->
AtomName = binary_to_atom(Name, utf8),
case lists:keyfind(Name, 1, Locks) of
{_, _, 0} = Lock ->
case rebar_utils:tup_find(AtomName, Deps) of
false ->
?WARN("Dependency ~s has been removed and will not be upgraded", [Name]),
- prepare_locks(Names, Deps, Locks, Unlocks, Dict);
+ prepare_locks(Names, Deps, Locks, Unlocks, Dict, AltDeps);
Dep ->
{Source, NewLocks, NewUnlocks} = prepare_lock(Dep, Lock, Locks, Dict),
prepare_locks(Names, Deps, NewLocks,
- [{Name, Source, 0} | NewUnlocks ++ Unlocks], Dict)
+ [{Name, Source, 0} | NewUnlocks ++ Unlocks], Dict, AltDeps)
end;
{_, _, Level} = Lock when Level > 0 ->
case rebar_utils:tup_find(AtomName, Deps) of
@@ -137,10 +164,15 @@ prepare_locks([Name|Names], Deps, Locks, Unlocks, Dict) ->
Dep -> % Dep has been promoted
{Source, NewLocks, NewUnlocks} = prepare_lock(Dep, Lock, Locks, Dict),
prepare_locks(Names, Deps, NewLocks,
- [{Name, Source, 0} | NewUnlocks ++ Unlocks], Dict)
+ [{Name, Source, 0} | NewUnlocks ++ Unlocks], Dict, AltDeps)
end;
false ->
- ?PRV_ERROR({unknown_dependency, Name})
+ case rebar_utils:tup_find(AtomName, AltDeps) of
+ false ->
+ ?PRV_ERROR({unknown_dependency, Name});
+ _ -> % non-default profile dependency found, pass through
+ prepare_locks(Names, Deps, Locks, Unlocks, Dict, AltDeps)
+ end
end.
prepare_lock(Dep, Lock, Locks, Dict) ->
diff --git a/test/rebar_upgrade_SUITE.erl b/test/rebar_upgrade_SUITE.erl
index 66e1fdf..45a7433 100644
--- a/test/rebar_upgrade_SUITE.erl
+++ b/test/rebar_upgrade_SUITE.erl
@@ -11,7 +11,8 @@ groups() ->
triplet_a, triplet_b, triplet_c,
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]},
+ compile_upgrade_parity, umbrella_config,
+ profiles, profiles_exclusion]},
{git, [], [{group, all}]},
{pkg, [], [{group, all}]}].
@@ -78,6 +79,23 @@ setup_project(Case=umbrella_config, Config0, Deps, UpDeps) ->
[{rebarconfig, TopConf},
{rebarumbrella, RebarConf},
{next_top_deps, rebar_test_utils:top_level_deps(UpDeps)} | Config];
+setup_project(Case, Config0, Deps, UpDeps) when Case == profiles;
+ Case == profiles_exclusion ->
+ DepsType = ?config(deps_type, Config0),
+ NameRoot = atom_to_list(Case)++"_"++atom_to_list(DepsType),
+ Config = rebar_test_utils:init_rebar_state(Config0, NameRoot++"_"),
+ AppDir = filename:join([?config(apps, Config), "apps", NameRoot]),
+ rebar_test_utils:create_app(AppDir, "Root", "0.0.0", [kernel, stdlib]),
+ [Top|ProfileDeps] = rebar_test_utils:top_level_deps(Deps),
+ RebarConf = rebar_test_utils:create_config(AppDir, [
+ {deps, [Top]},
+ {profiles, [{fake, [{deps, ProfileDeps}]}]}
+ ]),
+ [NextTop|NextPDeps] = rebar_test_utils:top_level_deps(UpDeps),
+ NextConfig = [{deps, [NextTop]},
+ {profiles, [{fake, [{deps, NextPDeps}]}]}],
+ [{rebarconfig, RebarConf},
+ {next_config, NextConfig} | Config];
setup_project(Case, Config0, Deps, UpDeps) ->
DepsType = ?config(deps_type, Config0),
Config = rebar_test_utils:init_rebar_state(
@@ -454,7 +472,47 @@ upgrades(umbrella_config) ->
{[{"A", "1", []}],
[{"A", "2", []}],
["A"],
- {"A", [{"A","2"}]}}.
+ {"A", [{"A","2"}]}};
+upgrades(profiles) ->
+ %% Ensure that we can unlock deps under a given profile;
+ %% B and C should both be in a custom profile
+ %% and must not be locked.
+ {[{"A", "1", [{"D",[]},
+ {"E","3",[]}]},
+ {"B", "1", [{"F","1",[]},
+ {"G",[]}]},
+ {"C", "0", [{"H","3",[]},
+ {"I",[]}]}],
+ [{"A", "2", [{"D",[]},
+ {"E","2",[]}]},
+ {"B", "2", [{"F","2",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H","4",[]},
+ {"I",[]}]}],
+ ["A","B","C","E","F","H"],
+ {"C", [{"A","1"}, "D", {"E","3"},
+ {"B","2"}, {"F","2"}, "G",
+ {"C","1"}, {"H","4"}, "I"]}};
+upgrades(profiles_exclusion) ->
+ %% Ensure that we can unlock deps under a given profile;
+ %% B and C should both be in a custom profile
+ %% and must not be locked.
+ {[{"A", "1", [{"D",[]},
+ {"E","3",[]}]},
+ {"B", "1", [{"F","1",[]},
+ {"G",[]}]},
+ {"C", "0", [{"H","3",[]},
+ {"I",[]}]}],
+ [{"A", "2", [{"D",[]},
+ {"E","2",[]}]},
+ {"B", "2", [{"F","2",[]},
+ {"G",[]}]},
+ {"C", "1", [{"H","4",[]},
+ {"I",[]}]}],
+ ["A","B","C","E","F","H"],
+ {"A", [{"A","1"}, "D", {"E","3"},
+ {"B","2"}, {"F","2"}, "G",
+ {"C","1"}, {"H","4"}, "I"]}}.
%% TODO: add a test that verifies that unlocking files and then
%% running the upgrade code is enough to properly upgrade things.
@@ -613,6 +671,37 @@ umbrella_config(Config) ->
),
meck:unload(rebar_prv_upgrade).
+profiles(Config) ->
+ apply(?config(mock, Config), []),
+ {ok, TopConfig} = file:consult(?config(rebarconfig, Config)),
+ %% Install dependencies before re-mocking for an upgrade
+ rebar_test_utils:run_and_check(Config, TopConfig, ["lock"], {ok, []}),
+ %% Install test deps along with them
+ rebar_test_utils:run_and_check(Config, TopConfig, ["as","fake","lock"], {ok, []}),
+ {App, Unlocks} = ?config(expected, Config),
+ ct:pal("Upgrades: ~p -> ~p", [App, Unlocks]),
+ Expectation = case Unlocks of
+ {error, Term} -> {error, Term};
+ _ -> {ok, [T || T <- Unlocks,
+ element(1,T) == dep orelse
+ lists:member(element(2,T), ["A","D","E"])]}
+ end,
+
+ meck:new(rebar_prv_app_discovery, [passthrough]),
+ meck:expect(rebar_prv_app_discovery, do, fun(S) ->
+ apply(?config(mock_update, Config), []),
+ meck:passthrough([S])
+ end),
+ NewRebarConf = rebar_test_utils:create_config(?config(apps, Config),
+ ?config(next_config, Config)),
+ {ok, NewRebarConfig} = file:consult(NewRebarConf),
+ rebar_test_utils:run_and_check(
+ Config, NewRebarConfig, ["as","fake","upgrade", App], Expectation
+ ),
+ meck:unload(rebar_prv_app_discovery).
+
+profiles_exclusion(Config) -> profiles(Config).
+
run(Config) ->
apply(?config(mock, Config), []),
ConfigPath = ?config(rebarconfig, Config),