diff options
Diffstat (limited to 'src/rebar_prv_upgrade.erl')
-rw-r--r-- | src/rebar_prv_upgrade.erl | 112 |
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 -> |