aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml2
-rw-r--r--src/rlx_cmd_args.erl3
-rw-r--r--src/rlx_config.erl56
-rw-r--r--src/rlx_prv_overlay.erl15
-rw-r--r--src/rlx_state.erl8
-rw-r--r--test/rlx_archive_SUITE.erl2
-rw-r--r--test/rlx_command_SUITE.erl2
-rw-r--r--test/rlx_discover_SUITE.erl2
-rw-r--r--test/rlx_eunit_SUITE.erl2
-rw-r--r--test/rlx_extended_bin_SUITE.erl2
-rw-r--r--test/rlx_release_SUITE.erl88
-rw-r--r--test/rlx_test_utils.erl4
12 files changed, 149 insertions, 37 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 8e693dd..4ca365a 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -1,5 +1,5 @@
test_task:
- use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
+ use_compute_credits: true
container:
matrix:
- image: erlang:22
diff --git a/src/rlx_cmd_args.erl b/src/rlx_cmd_args.erl
index 4f5e9da..1dc10c1 100644
--- a/src/rlx_cmd_args.erl
+++ b/src/rlx_cmd_args.erl
@@ -298,6 +298,9 @@ create(add_providers, Opts) ->
create(providers, Opts) ->
Providers = proplists:get_value(providers, Opts, []),
{providers, Providers};
+create(api_caller_overlays, Opts) ->
+ ApiCallerOverlays = proplists:get_value(api_caller_overlays, Opts, []),
+ {api_caller_overlays, ApiCallerOverlays};
create(_, _) ->
[].
diff --git a/src/rlx_config.erl b/src/rlx_config.erl
index 90cfe7c..9eafb7d 100644
--- a/src/rlx_config.erl
+++ b/src/rlx_config.erl
@@ -191,31 +191,20 @@ load_terms({upfrom, UpFrom}, {ok, State0}) ->
{ok, rlx_state:upfrom(State0, UpFrom)};
load_terms({include_src, IncludeSrc}, {ok, State0}) ->
{ok, rlx_state:include_src(State0, IncludeSrc)};
+load_terms({release, {RelName, Vsn, {extend, {RelName2, Vsn2}}}, Applications}, {ok, State0}) ->
+ NewVsn = parse_vsn(Vsn),
+ NewVsn2 = parse_vsn(Vsn2),
+ add_extended_release(RelName, NewVsn, RelName2, NewVsn2, Applications, State0);
load_terms({release, {RelName, Vsn, {extend, RelName2}}, Applications}, {ok, State0}) ->
NewVsn = parse_vsn(Vsn),
- Release0 = rlx_release:new(RelName, NewVsn),
- ExtendRelease = rlx_state:get_configured_release(State0, RelName2, NewVsn),
- Applications1 = rlx_release:goals(ExtendRelease),
- case rlx_release:goals(Release0,
- rlx_release:merge_application_goals(Applications, Applications1)) of
- E={error, _} ->
- E;
- {ok, Release1} ->
- {ok, rlx_state:add_configured_release(State0, Release1)}
- end;
+ add_extended_release(RelName, NewVsn, RelName2, NewVsn, Applications, State0);
+load_terms({release, {RelName, Vsn, {extend, {RelName2, Vsn2}}}, Applications, Config}, {ok, State0}) ->
+ NewVsn = parse_vsn(Vsn),
+ NewVsn2 = parse_vsn(Vsn2),
+ add_extended_release(RelName, NewVsn, RelName2, NewVsn2, Applications, Config, State0);
load_terms({release, {RelName, Vsn, {extend, RelName2}}, Applications, Config}, {ok, State0}) ->
NewVsn = parse_vsn(Vsn),
- Release0 = rlx_release:new(RelName, NewVsn),
- ExtendRelease = rlx_state:get_configured_release(State0, RelName2, NewVsn),
- Applications1 = rlx_release:goals(ExtendRelease),
- case rlx_release:goals(Release0,
- rlx_release:merge_application_goals(Applications, Applications1)) of
- E={error, _} ->
- E;
- {ok, Release1} ->
- Release2 = rlx_release:config(Release1, Config),
- {ok, rlx_state:add_configured_release(State0, Release2)}
- end;
+ add_extended_release(RelName, NewVsn, RelName2, NewVsn, Applications, Config, State0);
load_terms({release, {RelName, Vsn}, {erts, ErtsVsn}, Applications}, {ok, State}) ->
NewVsn = parse_vsn(Vsn),
Release0 = rlx_release:erts(rlx_release:new(RelName, NewVsn), ErtsVsn),
@@ -414,3 +403,28 @@ git_ref(Arg) ->
{plain, "0"}
end
end.
+
+add_extended_release(RelName, NewVsn, RelName2, NewVsn2, Applications, State0) ->
+ Release0 = rlx_release:new(RelName, NewVsn),
+ ExtendRelease = rlx_state:get_configured_release(State0, RelName2, NewVsn2),
+ Applications1 = rlx_release:goals(ExtendRelease),
+ case rlx_release:goals(Release0,
+ rlx_release:merge_application_goals(Applications, Applications1)) of
+ E={error, _} ->
+ E;
+ {ok, Release1} ->
+ {ok, rlx_state:add_configured_release(State0, Release1)}
+ end.
+
+add_extended_release(RelName, NewVsn, RelName2, NewVsn2, Applications, Config, State0) ->
+ Release0 = rlx_release:new(RelName, NewVsn),
+ ExtendRelease = rlx_state:get_configured_release(State0, RelName2, NewVsn2),
+ Applications1 = rlx_release:goals(ExtendRelease),
+ case rlx_release:goals(Release0,
+ rlx_release:merge_application_goals(Applications, Applications1)) of
+ E={error, _} ->
+ E;
+ {ok, Release1} ->
+ Release2 = rlx_release:config(Release1, Config),
+ {ok, rlx_state:add_configured_release(State0, Release2)}
+ end.
diff --git a/src/rlx_prv_overlay.erl b/src/rlx_prv_overlay.erl
index 87fa91f..ec8a97f 100644
--- a/src/rlx_prv_overlay.erl
+++ b/src/rlx_prv_overlay.erl
@@ -152,10 +152,15 @@ get_overlay_vars_from_file(State, OverlayVars) ->
-spec read_overlay_vars(rlx_state:t(), proplists:proplist(), [file:name()]) ->
proplists:proplist() | relx:error().
read_overlay_vars(State, OverlayVars, FileNames) ->
+ ApiCallerVars = rlx_state:api_caller_overlays(State),
Terms = merge_overlay_vars(State, FileNames),
- case render_overlay_vars(OverlayVars, Terms, []) of
+ case render_overlay_vars(OverlayVars ++ ApiCallerVars, Terms, []) of
{ok, NewTerms} ->
- OverlayVars ++ NewTerms;
+ % We place `ApiCallerVars' at the end on purpose; their
+ % definitions should be overwrittenable by both internal
+ % and rendered vars, as not to change behaviour in
+ % setups preceding the support for API caller overlays.
+ OverlayVars ++ NewTerms ++ ApiCallerVars;
Error ->
Error
end.
@@ -187,9 +192,11 @@ merge_overlay_vars(State, FileNames) ->
%% to the current one being read
OverlayRelativeRoot = filename:dirname(FileName),
NewTerms = check_overlay_inclusion(State, OverlayRelativeRoot, Terms),
+ %% Remove already defined variables from Acc,
+ %% append NewTerms, preserving order
lists:foldl(fun(NewTerm, A) ->
- lists:keystore(element(1, NewTerm), 1, A, NewTerm)
- end, Acc, NewTerms);
+ lists:keydelete(element(1, NewTerm), 1, A)
+ end, Acc, NewTerms) ++ NewTerms;
{error, Reason} ->
ec_cmd_log:warn(rlx_state:log(State),
format_error({unable_to_read_varsfile, FileName, Reason})),
diff --git a/src/rlx_state.erl b/src/rlx_state.erl
index cab55f6..9f9d835 100644
--- a/src/rlx_state.erl
+++ b/src/rlx_state.erl
@@ -44,6 +44,7 @@
goals/2,
config_file/1,
config_file/2,
+ api_caller_overlays/1,
cli_args/1,
cli_args/2,
providers/1,
@@ -104,6 +105,7 @@
output_dir :: file:name(),
lib_dirs=[] :: [file:name()],
config_file=[] :: file:filename() | undefined,
+ api_caller_overlays=[] :: [{atom(),term()}],
cli_args=[] :: proplists:proplist(),
goals=[] :: [rlx_depsolver:raw_constraint()],
providers=[] :: [providers:t()],
@@ -160,8 +162,10 @@ new(Config, CommandLineConfig, Targets)
Log = proplists:get_value(
log, CommandLineConfig,
ec_cmd_log:new(error, Caller, rlx_util:intensity())),
+ ApiCallerOverlays = proplists:get_value(api_caller_overlays, CommandLineConfig, []),
State0 = #state_t{log=Log,
config_file=Config,
+ api_caller_overlays=ApiCallerOverlays,
cli_args=CommandLineConfig,
actions=Targets,
caller=Caller,
@@ -270,6 +274,10 @@ config_file(#state_t{config_file=ConfigFiles}) ->
config_file(State, ConfigFiles) ->
State#state_t{config_file=ConfigFiles}.
+-spec api_caller_overlays(t()) -> [{atom(),term()}].
+api_caller_overlays(#state_t{api_caller_overlays = ApiCallerOverlays}) ->
+ ApiCallerOverlays.
+
-spec cli_args(t()) -> proplists:proplist().
cli_args(#state_t{cli_args=CliArgs}) ->
CliArgs.
diff --git a/test/rlx_archive_SUITE.erl b/test/rlx_archive_SUITE.erl
index 8d30915..705950f 100644
--- a/test/rlx_archive_SUITE.erl
+++ b/test/rlx_archive_SUITE.erl
@@ -18,7 +18,7 @@
-include_lib("kernel/include/file.hrl").
suite() ->
- [{timetrap, {seconds, 120}}].
+ [].
init_per_suite(Config) ->
Config.
diff --git a/test/rlx_command_SUITE.erl b/test/rlx_command_SUITE.erl
index 8dd2da8..916bdf7 100644
--- a/test/rlx_command_SUITE.erl
+++ b/test/rlx_command_SUITE.erl
@@ -34,7 +34,7 @@
-include_lib("eunit/include/eunit.hrl").
suite() ->
- [{timetrap,{seconds,120}}].
+ [].
init_per_suite(Config) ->
Config.
diff --git a/test/rlx_discover_SUITE.erl b/test/rlx_discover_SUITE.erl
index 9385229..fc81bc2 100644
--- a/test/rlx_discover_SUITE.erl
+++ b/test/rlx_discover_SUITE.erl
@@ -34,7 +34,7 @@
-include_lib("eunit/include/eunit.hrl").
suite() ->
- [{timetrap,{seconds,120}}].
+ [].
init_per_suite(Config) ->
Config.
diff --git a/test/rlx_eunit_SUITE.erl b/test/rlx_eunit_SUITE.erl
index c7c0751..780e409 100644
--- a/test/rlx_eunit_SUITE.erl
+++ b/test/rlx_eunit_SUITE.erl
@@ -30,7 +30,7 @@
-include_lib("eunit/include/eunit.hrl").
suite() ->
- [{timetrap,{seconds,120}}].
+ [].
init_per_suite(Config) ->
Config.
diff --git a/test/rlx_extended_bin_SUITE.erl b/test/rlx_extended_bin_SUITE.erl
index d17a375..c43e8a6 100644
--- a/test/rlx_extended_bin_SUITE.erl
+++ b/test/rlx_extended_bin_SUITE.erl
@@ -67,7 +67,7 @@
-define(SLEEP_TIME, 2500).
suite() ->
- [{timetrap,{seconds,300}}].
+ [].
init_per_suite(Config) ->
Config.
diff --git a/test/rlx_release_SUITE.erl b/test/rlx_release_SUITE.erl
index a582526..a2aab71 100644
--- a/test/rlx_release_SUITE.erl
+++ b/test/rlx_release_SUITE.erl
@@ -31,6 +31,7 @@
make_release/1,
make_config_release/1,
make_extend_release/1,
+ make_extend_release_versioned/1,
make_extend_config_release/1,
make_scriptless_release/1,
make_overridden_release/1,
@@ -67,7 +68,7 @@
-include_lib("kernel/include/file.hrl").
suite() ->
- [{timetrap,{seconds,120}}].
+ [].
init_per_suite(Config) ->
Config.
@@ -87,6 +88,7 @@ init_per_testcase(_, Config) ->
all() ->
[providers, providers_via_api_options, add_providers, add_providers_via_api_options,
make_release, make_config_release, make_extend_release, make_extend_config_release,
+ make_extend_release_versioned,
make_scriptless_release, make_overridden_release, make_auto_skip_empty_app_release,
make_skip_app_release, make_exclude_app_release, make_app_type_none_release,
make_implicit_config_release, make_rerun_overridden_release, overlay_release,
@@ -380,6 +382,41 @@ make_extend_release(Config) ->
?assert(lists:member({goal_app_2, "0.0.1"}, AppSpecs)),
?assert(lists:member({lib_dep_1, "0.0.1", load}, AppSpecs)).
+make_extend_release_versioned(Config) ->
+ LibDir1 = proplists:get_value(lib1, Config),
+
+ rlx_test_utils:create_app(LibDir1, "goal_app_1", "0.0.1", [stdlib,kernel,non_goal_1], []),
+ rlx_test_utils:create_app(LibDir1, "lib_dep_1", "0.0.1", [stdlib,kernel], []),
+ rlx_test_utils:create_app(LibDir1, "goal_app_2", "0.0.1", [stdlib,kernel,goal_app_1,non_goal_2], []),
+ rlx_test_utils:create_app(LibDir1, "non_goal_1", "0.0.1", [stdlib,kernel], [lib_dep_1]),
+ rlx_test_utils:create_app(LibDir1, "non_goal_2", "0.0.1", [stdlib,kernel], []),
+
+ ConfigFile = filename:join([LibDir1, "relx.config"]),
+ rlx_test_utils:write_config(ConfigFile,
+ [{release, {foo, "0.0.2"},
+ [goal_app_1,
+ goal_app_2]},
+ {release, {foo_test, "0.0.3", {extend, {foo, "0.0.2"}}},
+ [goal_app_2]},
+ {lib_dirs, [filename:join(LibDir1, "*")]}]),
+ OutputDir = filename:join([proplists:get_value(priv_dir, Config),
+ rlx_test_utils:create_random_name("relx-output")]),
+
+ ?assertMatch({error, {rlx_prv_release, {multiple_release_names,foo,foo_test}}},
+ catch relx:do(undefined, undefined, [], [LibDir1], 3, OutputDir, ConfigFile)),
+
+ {ok, State} = relx:do(foo_test, undefined, [], [LibDir1], 3,
+ OutputDir, ConfigFile),
+ [{{foo_test, "0.0.3"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)),
+ AppSpecs = rlx_release:applications(Release),
+ ?assert(lists:keymember(stdlib, 1, AppSpecs)),
+ ?assert(lists:keymember(kernel, 1, AppSpecs)),
+ ?assert(lists:member({non_goal_1, "0.0.1"}, AppSpecs)),
+ ?assert(lists:member({non_goal_2, "0.0.1"}, AppSpecs)),
+ ?assert(lists:member({goal_app_1, "0.0.1"}, AppSpecs)),
+ ?assert(lists:member({goal_app_2, "0.0.1"}, AppSpecs)),
+ ?assert(lists:member({lib_dep_1, "0.0.1", load}, AppSpecs)).
+
make_extend_config_release(Config) ->
LibDir1 = proplists:get_value(lib1, Config),
@@ -781,14 +818,20 @@ overlay_release(Config) ->
goal_app_2]}]),
VarsFile1 = filename:join([LibDir1, "vars1.config"]),
+ %% `tpl_var' is defined in vars1, but redifined in vars2 using template.
+ %% `api_caller_var' is to be injected as an API caller overlay var.
rlx_test_utils:write_config(VarsFile1, [{yahoo, "yahoo"},
{yahoo2, [{foo, "bar"}]},
{foo_yahoo, "foo_{{yahoo}}"},
- {foo_dir, "foodir"}]),
+ {foo_dir, "foodir"},
+ {tpl_var, "defined in vars1"},
+ {api_caller_var, "{{api_caller_var}}"}]),
VarsFile2 = filename:join([LibDir1, "vars2.config"]),
rlx_test_utils:write_config(VarsFile2, [{google, "yahoo"},
{yahoo2, "foo"},
+ {tpl_arg, "a template value"},
+ {tpl_var, "Redefined in vars2 with {{tpl_arg}}"},
OverlayVars3]),
VarsFile3 = filename:join([LibDir1, "vars3.config"]),
@@ -810,8 +853,25 @@ overlay_release(Config) ->
OutputDir = filename:join([proplists:get_value(priv_dir, Config),
rlx_test_utils:create_random_name("relx-output")]),
- {ok, State} = relx:do(undefined, undefined, [], [LibDir1], 3,
- OutputDir, ConfigFile),
+ ApiCallerVarValue = "api-caller-var",
+ ApiCallerReleaseNameValue = "release-var-conflict",
+ ApiCallerConfigFileValue = "state-var-conflict",
+ ApiCallerYahooValue = "overlays-file-conflict",
+ ApiCallerOverlays =
+ [{api_caller_var, ApiCallerVarValue},
+ {release_name, ApiCallerReleaseNameValue},
+ {config_file, ApiCallerConfigFileValue},
+ {yahoo, ApiCallerYahooValue}],
+
+ {ok, State} = relx:do([{relname, undefined},
+ {relvsn, undefined},
+ {goals, []},
+ {lib_dirs, [LibDir1]},
+ {log_level, 3},
+ {output_dir, OutputDir},
+ {config, ConfigFile},
+ {api_caller_overlays, ApiCallerOverlays}],
+ ["release"]),
[{{foo, "0.0.1"}, Release}] = ec_dictionary:to_list(rlx_state:realized_releases(State)),
AppSpecs = rlx_release:applications(Release),
@@ -848,6 +908,8 @@ overlay_release(Config) ->
proplists:get_value(release_version, TemplateData)),
?assertEqual(foo,
proplists:get_value(release_name, TemplateData)),
+ ?assertMatch(ConfigFile,
+ proplists:get_value(config_file, TemplateData)),
?assertEqual("yahoo/foo4",
proplists:get_value(yahoo4, TemplateData)),
?assertEqual("foo_yahoo",
@@ -857,7 +919,23 @@ overlay_release(Config) ->
?assertEqual("val1",
proplists:get_value(prop1, TemplateData)),
?assertEqual(2,
- proplists:get_value(prop2, TemplateData)).
+ proplists:get_value(prop2, TemplateData)),
+ %% This should be rendered correctly based on VarsFile2 file, regardless
+ %% of tpl_var defined in VarsFile1 or not.
+ ?assertEqual("Redefined in vars2 with a template value",
+ proplists:get_value(tpl_var, TemplateData)),
+
+ ?assertEqual(ApiCallerVarValue,
+ proplists:get_value(api_caller_var, TemplateData)),
+ % The following complements a few of the assertions above
+ % by ensuring that the values of the overwritten API caller
+ % overlays were distinct from their internal values.
+ ?assertNotEqual(ApiCallerReleaseNameValue,
+ proplists:get_value(release_name, TemplateData)),
+ ?assertNotEqual(ApiCallerConfigFileValue,
+ proplists:get_value(config_file, TemplateData)),
+ ?assertNotEqual(ApiCallerYahooValue,
+ proplists:get_value(yahoo, TemplateData)).
make_goalless_release(Config) ->
LibDir1 = proplists:get_value(lib1, Config),
diff --git a/test/rlx_test_utils.erl b/test/rlx_test_utils.erl
index 37d9c7a..2c5f7af 100644
--- a/test/rlx_test_utils.erl
+++ b/test/rlx_test_utils.erl
@@ -185,7 +185,9 @@ test_template_contents() ->
"{foo_yahoo, \"{{foo_yahoo}}\"}.\n"
"{google, \"{{google}}\"}.\n"
"{prop1, \"{{prop1}}\"}.\n"
- "{prop2, {{prop2}}}.\n".
+ "{prop2, {{prop2}}}.\n"
+ "{tpl_var, \"{{tpl_var}}\"}.\n"
+ "{api_caller_var, \"{{api_caller_var}}\"}.\n".
escript_contents() ->
"#!/usr/bin/env escript\n"