diff options
author | Fred Hebert <mononcqc@ferd.ca> | 2016-02-16 08:54:46 -0500 |
---|---|---|
committer | Fred Hebert <mononcqc@ferd.ca> | 2016-02-16 08:54:46 -0500 |
commit | caf4468f3bbbea75be35e0cf0560990b5db66e7e (patch) | |
tree | 731e1301a350b13acb5ba90be77f5234a2bf8428 | |
parent | b8a590cf192181170d5fdab40652340917ba3905 (diff) |
Make lock files future-proof
Changes to how hex or packages may work in the future will necessarily
bring changes to the format of lock files.
This commit adds an optional framing for future lock files of the form:
{Version, LockList}.
<Whatever consultable attributes>
This format is supported such as the LockList is the current lockfile
contents, and will never have more information than it currently does.
Attributes can be whatever and are currently undefined.
Rebar copies will be able to:
- Keep using the core locklist (which avoids breaking the last year or
so of community libraries using rebar3)
- Warn when it runs an outdated copy in comparison to the lock file
- Automatically rewrite lock files in the format it supports
- Augment or parse files in a version-specific manner.
This changes the usage interface slightly, but is backwards *and*
forwards compatible.
-rw-r--r-- | src/rebar_app_info.erl | 8 | ||||
-rw-r--r-- | src/rebar_config.erl | 38 | ||||
-rw-r--r-- | src/rebar_prv_lock.erl | 3 | ||||
-rw-r--r-- | src/rebar_prv_unlock.erl | 9 | ||||
-rw-r--r-- | src/rebar_state.erl | 8 | ||||
-rw-r--r-- | test/rebar_lock_SUITE.erl | 46 |
6 files changed, 95 insertions, 17 deletions
diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl index 9fee4e0..cf3b82e 100644 --- a/src/rebar_app_info.erl +++ b/src/rebar_app_info.erl @@ -165,13 +165,13 @@ update_opts(AppInfo, Opts, Config) -> deps_from_config(Dir, Config) -> case rebar_config:consult_lock_file(filename:join(Dir, ?LOCK_FILE)) of - [D] -> + [] -> + [{{deps, default}, proplists:get_value(deps, Config, [])}]; + D -> %% We want the top level deps only from the lock file. %% This ensures deterministic overrides for configs. Deps = [X || X <- D, element(3, X) =:= 0], - [{{locks, default}, D}, {{deps, default}, Deps}]; - _ -> - [{{deps, default}, proplists:get_value(deps, Config, [])}] + [{{locks, default}, D}, {{deps, default}, Deps}] end. %% @doc discover a complete version of the app info with all fields set. diff --git a/src/rebar_config.erl b/src/rebar_config.erl index 61301cb..8d7bcf4 100644 --- a/src/rebar_config.erl +++ b/src/rebar_config.erl @@ -30,6 +30,7 @@ ,consult_app_file/1 ,consult_file/1 ,consult_lock_file/1 + ,write_lock_file/2 ,verify_config_format/1 ,format_error/1 @@ -50,7 +51,40 @@ consult_app_file(File) -> consult_file_(File). consult_lock_file(File) -> - consult_file_(File). + Terms = consult_file_(File), + case Terms of + [] -> + []; + [Locks] when is_list(Locks) -> % beta lock file + Locks; + [{Vsn, Locks}|Attrs] when is_list(Locks) -> % versioned lock file + %% Make sure the warning above is to be shown whenever a version + %% newer than the current one is being used, as we can't parse + %% all the contents of the lock file properly. + ?WARN("Rebar3 detected a lock file from a newer version. " + "It will be loaded in compatibility mode, but important " + "information may be missing or lost. It is recommended to " + "upgrade Rebar3.", []), + read_attrs(Vsn, Locks, Attrs) + end. + +write_lock_file(LockFile, Locks) -> + NewLocks = write_attrs(Locks), + %% Write locks in the beta format, at least until it's been long + %% enough we can start modifying the lock format. + file:write_file(LockFile, io_lib:format("~p.~n", [NewLocks])). + +read_attrs(_Vsn, Locks, _Attrs) -> + %% Beta copy does not know how to expand attributes, but + %% is ready to support it. + Locks. + +write_attrs(Locks) -> + %% No attribute known that needs to be taken out of the structure, + %% just return terms as is. + Locks. + + consult_file(File) -> Terms = consult_file_(File), @@ -87,7 +121,7 @@ verify_config_format([Term | _]) -> merge_locks(Config, []) -> Config; %% lockfile with entries -merge_locks(Config, [Locks]) -> +merge_locks(Config, Locks) -> ConfigDeps = proplists:get_value(deps, Config, []), %% We want the top level deps only from the lock file. %% This ensures deterministic overrides for configs. diff --git a/src/rebar_prv_lock.erl b/src/rebar_prv_lock.erl index 8578979..cbe8dfe 100644 --- a/src/rebar_prv_lock.erl +++ b/src/rebar_prv_lock.erl @@ -35,8 +35,7 @@ do(State) -> OldLocks = rebar_state:get(State, {locks, default}, []), Locks = lists:keysort(1, build_locks(State)), Dir = rebar_state:dir(State), - file:write_file(filename:join(Dir, ?LOCK_FILE), - io_lib:format("~p.~n", [Locks])), + rebar_config:write_lock_file(filename:join(Dir, ?LOCK_FILE), Locks), State1 = rebar_state:set(State, {locks, default}, Locks), OldLockNames = [element(1,L) || L <- OldLocks], diff --git a/src/rebar_prv_unlock.erl b/src/rebar_prv_unlock.erl index b049c92..7ff0d89 100644 --- a/src/rebar_prv_unlock.erl +++ b/src/rebar_prv_unlock.erl @@ -46,15 +46,14 @@ do(State) -> {ok, State}; {error, Reason} -> ?PRV_ERROR({file,Reason}); - {ok, [Locks]} -> + {ok, _} -> + Locks = rebar_config:consult_lock_file(LockFile), case handle_unlocks(State, Locks, LockFile) of ok -> {ok, State}; {error, Reason} -> ?PRV_ERROR({file,Reason}) - end; - {ok, _Other} -> - ?PRV_ERROR(unknown_lock_format) + end end. -spec format_error(any()) -> iolist(). @@ -74,7 +73,7 @@ handle_unlocks(State, Locks, LockFile) -> _ when Names =:= [] -> % implicitly all locks file:delete(LockFile); NewLocks -> - file:write_file(LockFile, io_lib:format("~p.~n", [NewLocks])) + rebar_config:write_lock_file(LockFile, NewLocks) end. parse_names(Bin) -> diff --git a/src/rebar_state.erl b/src/rebar_state.erl index 0c07b2a..5ec2aef 100644 --- a/src/rebar_state.erl +++ b/src/rebar_state.erl @@ -115,13 +115,13 @@ new(ParentState, Config, Deps, Dir) -> deps_from_config(Dir, Config) -> case rebar_config:consult_lock_file(filename:join(Dir, ?LOCK_FILE)) of - [D] -> + [] -> + [{{deps, default}, proplists:get_value(deps, Config, [])}]; + D -> %% We want the top level deps only from the lock file. %% This ensures deterministic overrides for configs. Deps = [X || X <- D, element(3, X) =:= 0], - [{{locks, default}, D}, {{deps, default}, Deps}]; - _ -> - [{{deps, default}, proplists:get_value(deps, Config, [])}] + [{{locks, default}, D}, {{deps, default}, Deps}] end. base_state() -> diff --git a/test/rebar_lock_SUITE.erl b/test/rebar_lock_SUITE.erl new file mode 100644 index 0000000..00875f7 --- /dev/null +++ b/test/rebar_lock_SUITE.erl @@ -0,0 +1,46 @@ +%%% Most locking tests are implicit in other test suites handling +%%% dependencies. +%%% This suite is to test the compatibility layers between various +%%% versions of lockfiles. +-module(rebar_lock_SUITE). +-compile(export_all). +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +all() -> [current_version, future_versions_no_attrs, future_versions_attrs]. + +current_version(Config) -> + %% Current version just dumps the locks as is on disk. + LockFile = filename:join(?config(priv_dir, Config), "current_version"), + Locks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2}, + {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0}, + {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1}, + {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>},3}], + file:write_file(LockFile, io_lib:format("~p.~n", [Locks])), + ?assertEqual(Locks, rebar_config:consult_lock_file(LockFile)). + +future_versions_no_attrs(Config) -> + %% Future versions will keep the same core attribute in there, but + %% will do so under a new format bundled with a version and potentially + %% some trailing attributes + LockFile = filename:join(?config(priv_dir, Config), "future_versions"), + Locks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2}, + {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0}, + {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1}, + {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>},3}], + LockData = {"3.5.2", Locks}, + file:write_file(LockFile, io_lib:format("~p.~n", [LockData])), + ?assertEqual(Locks, rebar_config:consult_lock_file(LockFile)). + +future_versions_attrs(Config) -> + %% Future versions will keep the same core attribute in there, but + %% will do so under a new format bundled with a version and potentially + %% some trailing attributes + LockFile = filename:join(?config(priv_dir, Config), "future_versions"), + Locks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2}, + {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0}, + {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1}, + {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>},3}], + LockData = {"3.5.2", Locks}, + file:write_file(LockFile, io_lib:format("~p.~na.~n{b,c}.~n[d,e,f].~n", [LockData])), + ?assertEqual(Locks, rebar_config:consult_lock_file(LockFile)). |