summaryrefslogtreecommitdiff
path: root/src/rebar_prv_upgrade.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/rebar_prv_upgrade.erl')
-rw-r--r--src/rebar_prv_upgrade.erl112
1 files changed, 96 insertions, 16 deletions
diff --git a/src/rebar_prv_upgrade.erl b/src/rebar_prv_upgrade.erl
index 18c307b..565f342 100644
--- a/src/rebar_prv_upgrade.erl
+++ b/src/rebar_prv_upgrade.erl
@@ -32,7 +32,7 @@ init(State) ->
{deps, ?DEPS},
{example, "rebar3 upgrade [cowboy[,ranch]]"},
{short_desc, "Upgrade dependencies."},
- {desc, "Upgrade project dependecies. Mentioning no application "
+ {desc, "Upgrade project dependencies. Mentioning no application "
"will upgrade all dependencies. To upgrade specific dependencies, "
"their names can be listed in the command."},
{opts, [
@@ -43,6 +43,19 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
+ Cwd = rebar_state:dir(State),
+ Providers = rebar_state:providers(State),
+ rebar_hooks:run_project_and_app_hooks(Cwd, pre, ?PROVIDER, Providers, State),
+ case do_(State) of
+ {ok, NewState} ->
+ rebar_hooks:run_project_and_app_hooks(Cwd, post, ?PROVIDER, Providers, NewState),
+ {ok, NewState};
+ Other ->
+ rebar_hooks:run_project_and_app_hooks(Cwd, post, ?PROVIDER, Providers, State),
+ Other
+ end.
+
+do_(State) ->
{Args, _} = rebar_state:command_parsed_args(State),
Locks = rebar_state:get(State, {locks, default}, []),
%% We have 3 sources of dependencies to upgrade from:
@@ -68,16 +81,23 @@ do(State) ->
ProfileDeps = rebar_state:get(State, {deps, default}, []),
Deps = [Dep || Dep <- TopDeps ++ ProfileDeps, % TopDeps > ProfileDeps
is_atom(Dep) orelse is_atom(element(1, Dep))],
- Names = parse_names(ec_cnv:to_binary(proplists:get_value(package, Args, <<"">>)), Locks),
+ Names = parse_names(rebar_utils: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} ->
+ {Locks0, Unlocks0} ->
Deps0 = top_level_deps(Deps, Locks),
State1 = rebar_state:set(State, {deps, default}, Deps0),
DepsDir = rebar_prv_install_deps:profile_dep_dir(State, default),
D = rebar_app_utils:parse_deps(root, DepsDir, Deps0, State1, Locks0, 0),
+
+ %% first update the package index for the packages to be upgraded
+ update_pkg_deps(Unlocks0, D, State1),
+
State2 = rebar_state:set(State1, {parsed_deps, default}, D),
State3 = rebar_state:set(State2, {locks, default}, Locks0),
State4 = rebar_state:set(State3, upgrade, true),
@@ -100,14 +120,44 @@ do(State) ->
format_error({unknown_dependency, Name}) ->
io_lib:format("Dependency ~ts not found", [Name]);
format_error({transitive_dependency, Name}) ->
- io_lib:format("Dependency ~ts is transient and cannot be safely upgraded. "
+ io_lib:format("Dependency ~ts is transitive and cannot be safely upgraded. "
"Promote it to your top-level rebar.config file to upgrade it.",
[Name]);
format_error(Reason) ->
io_lib:format("~p", [Reason]).
+%% fetch updates for package deps that have been unlocked for upgrade
+update_pkg_deps([], _, _) ->
+ ok;
+update_pkg_deps([{Name, _, _} | Rest], AppInfos, State) ->
+ case rebar_app_utils:find(Name, AppInfos) of
+ {ok, AppInfo} ->
+ Source = rebar_app_info:source(AppInfo),
+ case element(1, Source) of
+ pkg ->
+ Resources = rebar_state:resources(State),
+ #{repos := RepoConfigs} = rebar_resource_v2:find_resource_state(pkg, Resources),
+ PkgName = element(2, Source),
+ [update_package(PkgName, RepoConfig, State) || RepoConfig <- RepoConfigs];
+ _ ->
+ skip
+ end;
+ _ ->
+ %% this should be impossible...
+ skip
+ end,
+ update_pkg_deps(Rest, AppInfos, State).
+
+update_package(Name, RepoConfig, State) ->
+ case rebar_packages:update_package(Name, RepoConfig, State) of
+ fail ->
+ ?WARN("Failed to fetch updates for package ~ts from repo ~ts", [Name, maps:get(name, RepoConfig)]);
+ _ ->
+ ok
+ end.
+
parse_names(Bin, Locks) ->
- case lists:usort(re:split(Bin, <<" *, *">>, [trim])) of
+ case lists:usort(re:split(Bin, <<" *, *">>, [trim, unicode])) of
%% Nothing submitted, use *all* apps
[<<"">>] -> [Name || {Name, _, 0} <- Locks];
[] -> [Name || {Name, _, 0} <- Locks];
@@ -115,20 +165,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);
+ ?WARN("Dependency ~ts has been removed and will not be upgraded", [Name]),
+ 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 +212,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) ->
@@ -149,7 +229,7 @@ prepare_lock(Dep, Lock, Locks, Dict) ->
{Name, _, Src} -> {Name, Src};
_ when is_atom(Dep) ->
%% version-free package. Must unlock whatever matches in locks
- {_, Vsn, _} = lists:keyfind(ec_cnv:to_binary(Dep), 1, Locks),
+ {_, Vsn, _} = lists:keyfind(rebar_utils:to_binary(Dep), 1, Locks),
{Dep, Vsn}
end,
Children = all_children(Name1, Dict),
@@ -165,7 +245,7 @@ unlock_children(Children, Locks) ->
unlock_children(_, [], Locks, Unlocks) ->
{Locks, Unlocks};
unlock_children(Children, [App = {Name,_,_} | Apps], Locks, Unlocks) ->
- case lists:member(ec_cnv:to_binary(Name), Children) of
+ case lists:member(rebar_utils:to_binary(Name), Children) of
true ->
unlock_children(Children, Apps, Locks, [App | Unlocks]);
false ->
@@ -183,7 +263,7 @@ all_children(Name, Dict) ->
lists:flatten(all_children_(Name, Dict)).
all_children_(Name, Dict) ->
- case dict:find(ec_cnv:to_binary(Name), Dict) of
+ case dict:find(rebar_utils:to_binary(Name), Dict) of
{ok, Children} ->
[Children | [all_children_(Child, Dict) || Child <- Children]];
error ->