diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | priv/shell-completion/bash/rebar3 | 1 | ||||
-rw-r--r-- | priv/shell-completion/zsh/_rebar3 | 12 | ||||
-rw-r--r-- | src/rebar_app_info.erl | 41 | ||||
-rw-r--r-- | src/rebar_prv_deps.erl | 2 | ||||
-rw-r--r-- | src/rebar_prv_install_deps.erl | 95 | ||||
-rw-r--r-- | test/rebar_test_utils.erl | 24 | ||||
-rw-r--r-- | test/rebar_upgrade_SUITE.erl | 84 |
8 files changed, 188 insertions, 73 deletions
@@ -4,7 +4,7 @@ rebar rebar [3.0](#30) is an Erlang build tool that makes it easy to compile and test Erlang applications, port drivers and releases. -[![Build Status](https://travis-ci.org/rebar/rebar3.svg?branch=master)](https://travis-ci.org/rebar/rebar3) +[![Build Status](https://travis-ci.org/rebar/rebar3.svg?branch=master)](https://travis-ci.org/rebar/rebar3) [![Windows build status](https://ci.appveyor.com/api/projects/status/yx4oitd9pvd2kab3?svg=true)](https://ci.appveyor.com/project/TristanSloughter/rebar3) rebar is a self-contained Erlang script, so it's easy to distribute or even embed directly in a project. Where possible, rebar uses standard Erlang/OTP diff --git a/priv/shell-completion/bash/rebar3 b/priv/shell-completion/bash/rebar3 index cb6f69d..b6d3de2 100644 --- a/priv/shell-completion/bash/rebar3 +++ b/priv/shell-completion/bash/rebar3 @@ -27,6 +27,7 @@ _rebar3() report \ shell \ tar \ + unlock \ update \ upgrade \ version \ diff --git a/priv/shell-completion/zsh/_rebar3 b/priv/shell-completion/zsh/_rebar3 index 6950688..5fcdd91 100644 --- a/priv/shell-completion/zsh/_rebar3 +++ b/priv/shell-completion/zsh/_rebar3 @@ -72,6 +72,9 @@ _rebar3 () { '(-v --verbose)'{-v,--verbose}'[Print coverage analysis]' \ && ret=0 ;; + (deps) + _message 'no more arguments' && ret=0 + ;; (dialyzer) _arguments \ '(-u --update-plt)'{-u, --update-plt}'[Enable updating the PLT.]' \ @@ -100,7 +103,7 @@ _rebar3 () { ;; (new) _arguments \ - '1:type:(app lib release plugin)' \ + '1:type:(app cmake escript lib plugin release)' \ '2:name:' \ '(-f --force)'{-f,--force}'[ overwrite existing files]' \ && ret=0 @@ -160,6 +163,11 @@ _rebar3 () { '(-r --root)'{-r,--root}'[The project root directory]:system libs:_files -/' \ && ret=0 ;; + (unlock) + _arguments \ + '*: :_rebar3_list_deps' \ + && ret=0 + ;; (update) _message 'rebar update' && ret=0 ;; @@ -185,6 +193,7 @@ _rebar3_tasks() { 'compile:Compile apps .app.src and .erl files.' 'cover:Perform coverage analysis.' 'ct:Run Common Tests.' + 'deps:List dependencies.' 'dialyzer:Run the Dialyzer analyzer on the project.' 'do:Higher order provider for running multiple tasks in a sequence.' 'edoc:Generate documentation using edoc.' @@ -197,6 +206,7 @@ _rebar3_tasks() { 'report:Provide a crash report to be sent to the rebar3 issues page.' 'shell:Run shell with project apps and deps in path.' 'tar:Tar archive of release built of project.' + 'unlock:Unlock dependencies..' 'update:Update package index.' 'upgrade:Upgrade dependencies.' 'version:Print version for rebar and current Erlang.' diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl index 91640f2..247e5f4 100644 --- a/src/rebar_app_info.erl +++ b/src/rebar_app_info.erl @@ -34,6 +34,8 @@ source/2, state/1, state/2, + is_lock/1, + is_lock/2, is_checkout/1, is_checkout/2, valid/1, @@ -41,22 +43,23 @@ -export_type([t/0]). --record(app_info_t, {name :: binary(), - app_file_src :: file:filename_all() | undefined, - app_file :: file:filename_all() | undefined, - config :: rebar_state:t() | undefined, - original_vsn :: binary() | string() | undefined, - app_details=[] :: list(), - applications=[] :: list(), - deps=[] :: list(), +-record(app_info_t, {name :: binary(), + app_file_src :: file:filename_all() | undefined, + app_file :: file:filename_all() | undefined, + config :: rebar_state:t() | undefined, + original_vsn :: binary() | string() | undefined, + app_details=[] :: list(), + applications=[] :: list(), + deps=[] :: list(), profiles=[default] :: [atom()], - dep_level=0 :: integer(), - dir :: file:name(), - out_dir :: file:name(), - source :: string() | tuple() | undefined, - state :: rebar_state:t() | undefined, - is_checkout=false :: boolean(), - valid :: boolean()}). + dep_level=0 :: integer(), + dir :: file:name(), + out_dir :: file:name(), + source :: string() | tuple() | undefined, + state :: rebar_state:t() | undefined, + is_lock=false :: boolean(), + is_checkout=false :: boolean(), + valid :: boolean()}). %%============================================================================ %% types @@ -254,6 +257,14 @@ state(AppInfo=#app_info_t{}, State) -> state(#app_info_t{state=State}) -> State. +-spec is_lock(t(), boolean()) -> t(). +is_lock(AppInfo=#app_info_t{}, IsLock) -> + AppInfo#app_info_t{is_lock=IsLock}. + +-spec is_lock(t()) -> boolean(). +is_lock(#app_info_t{is_lock=IsLock}) -> + IsLock. + -spec is_checkout(t(), boolean()) -> t(). is_checkout(AppInfo=#app_info_t{}, IsCheckout) -> AppInfo#app_info_t{is_checkout=IsCheckout}. diff --git a/src/rebar_prv_deps.erl b/src/rebar_prv_deps.erl index be81c31..5e3b1c5 100644 --- a/src/rebar_prv_deps.erl +++ b/src/rebar_prv_deps.erl @@ -18,7 +18,7 @@ init(State) -> providers:create([ {name, ?PROVIDER}, {module, ?MODULE}, - {bare, true}, + {bare, false}, {deps, ?DEPS}, {example, "rebar3 deps"}, {short_desc, "List dependencies"}, diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl index 2d69aa9..cdc008c 100644 --- a/src/rebar_prv_install_deps.erl +++ b/src/rebar_prv_install_deps.erl @@ -202,20 +202,25 @@ update_pkg_deps(Profile, Packages, PkgDeps, Graph, Upgrade, Seen, State, Locks) update_pkg_deps(Profile, S, Packages, Upgrade, Seen, State, Locks) end. +pkg_locked({Name, _, _}, Locks) -> + pkg_locked(Name, Locks); pkg_locked({Name, _}, Locks) -> + pkg_locked(Name, Locks); +pkg_locked(Name, Locks) -> false =/= lists:keyfind(Name, 1, Locks). -update_pkg_deps(Profile, Pkgs, Packages, Upgrade, Seen, State, _Locks) -> +update_pkg_deps(Profile, Pkgs, Packages, Upgrade, Seen, State, Locks) -> %% Create app_info record for each pkg dep DepsDir = profile_dep_dir(State, Profile), {Solved, _, State1} = lists:foldl(fun(Pkg, {Acc, SeenAcc, StateAcc}) -> - handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Acc, SeenAcc, StateAcc) + handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Acc, SeenAcc, Locks, StateAcc) end, {[], Seen, State}, Pkgs), {Solved, State1}. -handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Fetched, Seen, State) -> - AppInfo = package_to_app(DepsDir, Packages, Pkg, State), +handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Fetched, Seen, Locks, State) -> + IsLock = pkg_locked(Pkg, Locks), + AppInfo = package_to_app(DepsDir, Packages, Pkg, IsLock, State), Level = rebar_app_info:dep_level(AppInfo), {NewSeen, NewState} = maybe_lock(Profile, AppInfo, Seen, State, Level), {_, AppInfo1} = maybe_fetch(AppInfo, Profile, Upgrade, Seen, NewState), @@ -247,7 +252,7 @@ maybe_lock(Profile, AppInfo, Seen, State, Level) -> {Seen, State} end. -package_to_app(DepsDir, Packages, {Name, Vsn, Level}, State) -> +package_to_app(DepsDir, Packages, {Name, Vsn, Level}, IsLock, State) -> case dict:find({Name, Vsn}, Packages) of error -> case rebar_packages:check_registry(Name, Vsn, State) of @@ -263,7 +268,8 @@ package_to_app(DepsDir, Packages, {Name, Vsn, Level}, State) -> AppInfo1 = rebar_app_info:deps(AppInfo, PkgDeps), AppInfo2 = rebar_app_info:dir(AppInfo1, filename:join([DepsDir, Name])), AppInfo3 = rebar_app_info:dep_level(AppInfo2, Level), - rebar_app_info:source(AppInfo3, {pkg, Name, Vsn}) + AppInfo4 = rebar_app_info:is_lock(AppInfo3, IsLock), + rebar_app_info:source(AppInfo4, {pkg, Name, Vsn}) end. -spec update_src_deps(atom(), non_neg_integer(), list(), list(), list(), rebar_state:t(), boolean(), sets:set(binary()), list()) -> {rebar_state:t(), list(), list(), sets:set(binary())}. @@ -385,10 +391,10 @@ handle_dep(State, DepsDir, AppInfo, Locks, Level) -> NewLocks = [{DepName, Source, LockLevel+Level} || {DepName, Source, LockLevel} <- rebar_state:get(S3, {locks, default}, [])], AppInfo2 = rebar_app_info:deps(AppInfo1, rebar_state:deps_names(Deps)), - {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, S3, Locks, Level), + {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, S3, Locks, Level+1), {AppInfo2, SrcDeps, PkgDeps, Locks++NewLocks, State1}. --spec maybe_fetch(rebar_app_info:t(), atom(), boolean() | {true, binary(), integer()}, +-spec maybe_fetch(rebar_app_info:t(), atom(), boolean(), sets:set(binary()), rebar_state:t()) -> {boolean(), rebar_app_info:t()}. maybe_fetch(AppInfo, Profile, Upgrade, Seen, State) -> AppDir = ec_cnv:to_list(rebar_app_info:dir(AppInfo)), @@ -476,69 +482,69 @@ parse_deps(DepsDir, Deps, State, Locks, Level) -> end, case lists:keyfind(ec_cnv:to_binary(Name), 1, Locks) of false -> - parse_dep(Dep, Acc, DepsDir, State); + parse_dep(Dep, Acc, DepsDir, false, State); LockedDep -> LockedLevel = element(3, LockedDep), case LockedLevel > Level of true -> - parse_dep(Dep, Acc, DepsDir, State); + parse_dep(Dep, Acc, DepsDir, false, State); false -> - parse_dep(LockedDep, Acc, DepsDir, State) + parse_dep(LockedDep, Acc, DepsDir, true, State) end end end, {[], []}, Deps). -parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_list(Vsn) -> +parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_list(Vsn) -> CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)), case rebar_app_info:discover(CheckoutsDir) of {ok, _App} -> - Dep = new_dep(DepsDir, Name, [], [], State), + Dep = new_dep(DepsDir, Name, [], [], IsLock, State), {[Dep | SrcDepsAcc], PkgDepsAcc}; not_found -> {SrcDepsAcc, [parse_goal(ec_cnv:to_binary(Name) ,ec_cnv:to_binary(Vsn)) | PkgDepsAcc]} end; -parse_dep(Name, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_atom(Name) -> +parse_dep(Name, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_atom(Name) -> {PkgName, PkgVsn} = get_package(ec_cnv:to_binary(Name), State), CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)), case rebar_app_info:discover(CheckoutsDir) of {ok, _App} -> - Dep = new_dep(DepsDir, Name, [], [], State), + Dep = new_dep(DepsDir, Name, [], [], IsLock, State), {[Dep | SrcDepsAcc], PkgDepsAcc}; not_found -> {SrcDepsAcc, [{PkgName, PkgVsn} | PkgDepsAcc]} end; -parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) -> - Dep = new_dep(DepsDir, Name, [], Source, State), +parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) -> + Dep = new_dep(DepsDir, Name, [], Source, IsLock, State), {[Dep | SrcDepsAcc], PkgDepsAcc}; -parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) -> - Dep = new_dep(DepsDir, Name, [], Source, State), +parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) -> + Dep = new_dep(DepsDir, Name, [], Source, IsLock, State), {[Dep | SrcDepsAcc], PkgDepsAcc}; -parse_dep({Name, _Vsn, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) -> - Dep = new_dep(DepsDir, Name, [], Source, State), +parse_dep({Name, _Vsn, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) -> + Dep = new_dep(DepsDir, Name, [], Source, IsLock, State), {[Dep | SrcDepsAcc], PkgDepsAcc}; -parse_dep({Name, _Vsn, Source, Opts}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) -> +parse_dep({Name, _Vsn, Source, Opts}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) -> ?WARN("Dependency option list ~p in ~p is not supported and will be ignored", [Opts, Name]), - Dep = new_dep(DepsDir, Name, [], Source, State), + Dep = new_dep(DepsDir, Name, [], Source, IsLock, State), {[Dep | SrcDepsAcc], PkgDepsAcc}; -parse_dep({_Name, {pkg, Name, Vsn}, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_integer(Level) -> +parse_dep({_Name, {pkg, Name, Vsn}, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_integer(Level) -> CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)), case rebar_app_info:discover(CheckoutsDir) of {ok, _App} -> - Dep = new_dep(DepsDir, Name, [], [], State), + Dep = new_dep(DepsDir, Name, [], [], IsLock, State), {[Dep | SrcDepsAcc], PkgDepsAcc}; not_found -> {SrcDepsAcc, [{Name, Vsn} | PkgDepsAcc]} end; -parse_dep({Name, Source, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) +parse_dep({Name, Source, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) , is_integer(Level) -> - Dep = new_dep(DepsDir, Name, [], Source, State), + Dep = new_dep(DepsDir, Name, [], Source, IsLock, State), {[Dep | SrcDepsAcc], PkgDepsAcc}; -parse_dep(Dep, _, _, _) -> +parse_dep(Dep, _, _, _, _) -> throw(?PRV_ERROR({parse_dep, Dep})). -new_dep(DepsDir, Name, Vsn, Source, State) -> +new_dep(DepsDir, Name, Vsn, Source, IsLock, State) -> CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)), {ok, Dep} = case rebar_app_info:discover(CheckoutsDir) of {ok, App} -> @@ -559,7 +565,7 @@ new_dep(DepsDir, Name, Vsn, Source, State) -> ParentOverrides = rebar_state:overrides(State), Dep1 = rebar_app_info:state(Dep, rebar_state:overrides(S, ParentOverrides++Overrides)), - rebar_app_info:source(Dep1, Source). + rebar_app_info:is_lock(rebar_app_info:source(Dep1, Source), IsLock). fetch_app(AppInfo, AppDir, State) -> ?INFO("Fetching ~s (~p)", [rebar_app_info:name(AppInfo), rebar_app_info:source(AppInfo)]), @@ -580,22 +586,29 @@ update_app_info(AppInfo) -> IncludedApplications++Applications), rebar_app_info:valid(AppInfo1, false). -maybe_upgrade(AppInfo, AppDir, false, State) -> +maybe_upgrade(AppInfo, AppDir, Upgrade, State) -> Source = rebar_app_info:source(AppInfo), - rebar_fetch:needs_update(AppDir, Source, State); -maybe_upgrade(AppInfo, AppDir, true, State) -> - Source = rebar_app_info:source(AppInfo), - case rebar_fetch:needs_update(AppDir, Source, State) of + case Upgrade orelse rebar_app_info:is_lock(AppInfo) of true -> - ?INFO("Upgrading ~s", [rebar_app_info:name(AppInfo)]), - case rebar_fetch:download_source(AppDir, Source, State) of + case rebar_fetch:needs_update(AppDir, Source, State) of true -> - true; - Error -> - throw(Error) + ?INFO("Upgrading ~s", [rebar_app_info:name(AppInfo)]), + case rebar_fetch:download_source(AppDir, Source, State) of + true -> + true; + Error -> + throw(Error) + end; + false -> + case Upgrade of + true -> + ?INFO("No upgrade needed for ~s", [rebar_app_info:name(AppInfo)]), + false; + false -> + false + end end; false -> - ?INFO("No upgrade needed for ~s", [rebar_app_info:name(AppInfo)]), false end. diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl index c4dc663..68e0603 100644 --- a/test/rebar_test_utils.erl +++ b/test/rebar_test_utils.erl @@ -182,7 +182,7 @@ check_results(AppDir, Expected) -> lists:foreach( fun({app, Name}) -> - ct:pal("Name: ~p", [Name]), + ct:pal("App Name: ~p", [Name]), case lists:keyfind(Name, 1, DepsNames) of false -> error({app_not_found, Name}); @@ -190,7 +190,7 @@ check_results(AppDir, Expected) -> ok end ; ({app, Name, invalid}) -> - ct:pal("Name: ~p", [Name]), + ct:pal("Invalid Name: ~p", [Name]), case lists:keyfind(Name, 1, InvalidDepsNames) of false -> error({app_not_found, Name}); @@ -198,7 +198,7 @@ check_results(AppDir, Expected) -> ok end ; ({app, Name, valid}) -> - ct:pal("Name: ~p", [Name]), + ct:pal("Valid Name: ~p", [Name]), case lists:keyfind(Name, 1, ValidDepsNames) of false -> error({app_not_found, Name}); @@ -206,13 +206,13 @@ check_results(AppDir, Expected) -> ok end ; ({checkout, Name}) -> - ct:pal("Name: ~p", [Name]), + ct:pal("Checkout Name: ~p", [Name]), ?assertNotEqual(false, lists:keyfind(Name, 1, CheckoutsNames)) ; ({dep, Name}) -> - ct:pal("Name: ~p", [Name]), + ct:pal("Dep Name: ~p", [Name]), ?assertNotEqual(false, lists:keyfind(Name, 1, DepsNames)) ; ({dep, Name, Vsn}) -> - ct:pal("Name: ~p, Vsn: ~p", [Name, Vsn]), + ct:pal("Dep Name: ~p, Vsn: ~p", [Name, Vsn]), case lists:keyfind(Name, 1, DepsNames) of false -> error({dep_not_found, Name}); @@ -221,10 +221,10 @@ check_results(AppDir, Expected) -> iolist_to_binary(rebar_app_info:original_vsn(App))) end ; ({plugin, Name}) -> - ct:pal("Name: ~p", [Name]), + ct:pal("Plugin Name: ~p", [Name]), ?assertNotEqual(false, lists:keyfind(Name, 1, PluginsNames)) ; ({plugin, Name, Vsn}) -> - ct:pal("Name: ~p, Vsn: ~p", [Name, Vsn]), + ct:pal("Plugin Name: ~p, Vsn: ~p", [Name, Vsn]), case lists:keyfind(Name, 1, PluginsNames) of false -> error({plugin_not_found, Name}); @@ -233,10 +233,10 @@ check_results(AppDir, Expected) -> iolist_to_binary(rebar_app_info:original_vsn(App))) end ; ({global_plugin, Name}) -> - ct:pal("Name: ~p", [Name]), + ct:pal("Global Plugin Name: ~p", [Name]), ?assertNotEqual(false, lists:keyfind(Name, 1, GlobalPluginsNames)) ; ({global_plugin, Name, Vsn}) -> - ct:pal("Name: ~p, Vsn: ~p", [Name, Vsn]), + ct:pal("Global Plugin Name: ~p, Vsn: ~p", [Name, Vsn]), case lists:keyfind(Name, 1, GlobalPluginsNames) of false -> error({global_plugin_not_found, Name}); @@ -245,10 +245,10 @@ check_results(AppDir, Expected) -> iolist_to_binary(rebar_app_info:original_vsn(App))) end ; ({lock, Name}) -> - ct:pal("Name: ~p", [Name]), + ct:pal("Lock Name: ~p", [Name]), ?assertNotEqual(false, lists:keyfind(iolist_to_binary(Name), 1, Locks)) ; ({lock, Name, Vsn}) -> - ct:pal("Name: ~p, Vsn: ~p", [Name, Vsn]), + ct:pal("Lock Name: ~p, Vsn: ~p", [Name, Vsn]), case lists:keyfind(iolist_to_binary(Name), 1, Locks) of false -> error({lock_not_found, Name}); diff --git a/test/rebar_upgrade_SUITE.erl b/test/rebar_upgrade_SUITE.erl index 1dc0af2..793f10a 100644 --- a/test/rebar_upgrade_SUITE.erl +++ b/test/rebar_upgrade_SUITE.erl @@ -10,7 +10,7 @@ groups() -> pair_a, pair_b, pair_ab, pair_c, pair_all, triplet_a, triplet_b, triplet_c, tree_a, tree_b, tree_c, tree_c2, tree_ac, tree_all, - delete_d, promote]}, + delete_d, promote, stable_lock, fwd_lock]}, {git, [], [{group, all}]}, {pkg, [], [{group, all}]}]. @@ -361,7 +361,30 @@ upgrades(promote) -> {"C", "3", []} ], ["A","B","C","D"], - {"C", [{"A","1"},{"C","3"},{"B","1"},{"D","1"}]}}. + {"C", [{"A","1"},{"C","3"},{"B","1"},{"D","1"}]}}; +upgrades(stable_lock) -> + {[{"A", "1", [{"C", "1", []}]}, + {"B", "1", [{"D", "1", []}]} + ], % lock after this + [{"A", "2", [{"C", "2", []}]}, + {"B", "2", [{"D", "2", []}]} + ], + [], + %% Run a regular lock and no app should be upgraded + {"any", [{"A","1"},{"C","1"},{"B","1"},{"D","1"}]}}; +upgrades(fwd_lock) -> + {[{"A", "1", [{"C", "1", []}]}, + {"B", "1", [{"D", "1", []}]} + ], + [{"A", "2", [{"C", "2", []}]}, + {"B", "2", [{"D", "2", []}]} + ], + ["A","B","C","D"], + %% For this one, we should build, rewrite the lock + %% file to include the result post-upgrade, and then + %% run a regular lock to see that the lock file is respected + %% in deps. + {"any", [{"A","2"},{"C","2"},{"B","2"},{"D","2"}]}}. %% TODO: add a test that verifies that unlocking files and then %% running the upgrade code is enough to properly upgrade things. @@ -435,6 +458,46 @@ delete_d(Config) -> ?assertNotEqual([], [1 || {"App ~ts is no longer needed and can be deleted.", [<<"D">>]} <- Infos]). + +stable_lock(Config) -> + apply(?config(mock, Config), []), + {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)), + %% Install dependencies before re-mocking for an upgrade + rebar_test_utils:run_and_check(Config, RebarConfig, ["lock"], {ok, []}), + {App, Unlocks} = ?config(expected, Config), + ct:pal("Upgrades: ~p -> ~p", [App, Unlocks]), + Expectation = case Unlocks of + {error, Term} -> {error, Term}; + _ -> {ok, Unlocks} + end, + apply(?config(mock_update, Config), []), + NewRebarConf = rebar_test_utils:create_config(?config(apps, Config), + [{deps, ?config(next_top_deps, Config)}]), + {ok, NewRebarConfig} = file:consult(NewRebarConf), + rebar_test_utils:run_and_check( + Config, NewRebarConfig, ["lock", App], Expectation + ). + +fwd_lock(Config) -> + apply(?config(mock, Config), []), + {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)), + %% Install dependencies before re-mocking for an upgrade + rebar_test_utils:run_and_check(Config, RebarConfig, ["lock"], {ok, []}), + {App, Unlocks} = ?config(expected, Config), + ct:pal("Upgrades: ~p -> ~p", [App, Unlocks]), + Expectation = case Unlocks of + {error, Term} -> {error, Term}; + _ -> {ok, Unlocks} + end, + rewrite_locks(Expectation, Config), + apply(?config(mock_update, Config), []), + NewRebarConf = rebar_test_utils:create_config(?config(apps, Config), + [{deps, ?config(next_top_deps, Config)}]), + {ok, NewRebarConfig} = file:consult(NewRebarConf), + rebar_test_utils:run_and_check( + Config, NewRebarConfig, ["lock", App], Expectation + ). + run(Config) -> apply(?config(mock, Config), []), {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)), @@ -453,3 +516,20 @@ run(Config) -> rebar_test_utils:run_and_check( Config, NewRebarConfig, ["upgrade", App], Expectation ). + +rewrite_locks({ok, Expectations}, Config) -> + AppDir = ?config(apps, Config), + LockFile = filename:join([AppDir, "rebar.lock"]), + {ok, [Locks]} = file:consult(LockFile), + ExpLocks = [{list_to_binary(Name), Vsn} + || {lock, Name, Vsn} <- Expectations], + NewLocks = lists:foldl( + fun({App, {pkg, Name, _}, Lvl}, Acc) -> + Vsn = list_to_binary(proplists:get_value(App,ExpLocks)), + [{App, {pkg, Name, Vsn}, Lvl} | Acc] + ; ({App, {git, URL, {ref, _}}, Lvl}, Acc) -> + Vsn = proplists:get_value(App,ExpLocks), + [{App, {git, URL, {ref, Vsn}}, Lvl} | Acc] + end, [], Locks), + ct:pal("rewriting locks from ~p to~n~p", [Locks, NewLocks]), + file:write_file(LockFile, io_lib:format("~p.~n", [NewLocks])). |