aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/relx.app.src2
-rw-r--r--src/relx.erl12
-rw-r--r--src/rlx_cmd_args.erl43
-rw-r--r--src/rlx_goal.erl16
-rw-r--r--src/rlx_prv_assembler.erl200
-rw-r--r--src/rlx_prv_release.erl17
-rw-r--r--src/rlx_rel_discovery.erl2
-rw-r--r--src/rlx_state.erl28
-rw-r--r--src/rlx_util.erl16
-rw-r--r--test/rlx_discover_SUITE.erl2
-rw-r--r--test/rlx_release_SUITE.erl14
11 files changed, 258 insertions, 94 deletions
diff --git a/src/relx.app.src b/src/relx.app.src
index 3f655cf..c52958b 100644
--- a/src/relx.app.src
+++ b/src/relx.app.src
@@ -20,7 +20,7 @@
{application, relx,
[{description, "Release assembler for Erlang/OTP Releases"},
- {vsn, "0.0.5"},
+ {vsn, "semver"},
{modules, []},
{registered, []},
{applications, [kernel, stdlib, getopt, erlware_commons]}]}.
diff --git a/src/relx.erl b/src/relx.erl
index 30ee77f..7419352 100644
--- a/src/relx.erl
+++ b/src/relx.erl
@@ -156,12 +156,12 @@ do(RootDir, RelName, RelVsn, Goals, LibDirs, LogLevel, OutputDir, Overrides, Con
-spec do(proplists:proplist(), [string()]) ->
ok | error() | {ok, rlx_state:t()}.
do(Opts, NonOpts) ->
- case rlx_cmd_args:args2state(Opts, NonOpts) of
- {ok, State} ->
- run_relx_process(State);
- Error={error, _} ->
- Error
- end.
+ case rlx_cmd_args:args2state(Opts, NonOpts) of
+ {ok, State} ->
+ run_relx_process(State);
+ Error={error, _} ->
+ Error
+ end.
-spec opt_spec_list() -> [getopt:option_spec()].
opt_spec_list() ->
diff --git a/src/rlx_cmd_args.erl b/src/rlx_cmd_args.erl
index 1a8e7f2..4fbadba 100644
--- a/src/rlx_cmd_args.erl
+++ b/src/rlx_cmd_args.erl
@@ -32,24 +32,21 @@
-spec args2state([getopt:option()], [string()]) ->
{ok, {rlx_state:t(), [string()]}} |
relx:error().
-args2state(Opts, Target)
- when erlang:length(Target) == 0; erlang:length(Target) == 1 ->
+args2state(Opts, Targets) ->
RelName = rlx_util:to_atom(proplists:get_value(relname, Opts, undefined)),
RelVsn = proplists:get_value(relvsn, Opts, undefined),
- case convert_target(Target) of
- {ok, AtomizedTarget} ->
+ case convert_targets(Targets) of
+ {ok, AtomizedTargets} ->
case create_log(Opts, [{relname, RelName},
{relvsn, RelVsn}]) of
Error = {error, _} ->
Error;
{ok, CommandLineConfig} ->
- handle_config(Opts, AtomizedTarget, CommandLineConfig)
+ handle_config(Opts, AtomizedTargets, CommandLineConfig)
end;
Error ->
Error
- end;
-args2state(_Opts, Targets) ->
- ?RLX_ERROR({invalid_targets, Targets}).
+ end.
-spec format_error(Reason::term()) -> iolist().
format_error({invalid_targets, Targets}) ->
@@ -88,25 +85,33 @@ format_error({invalid_target, Target}) ->
%%%===================================================================
%%% Internal Functions
%%%===================================================================
--spec handle_config([getopt:option()], atom(), proplists:proplist()) ->
+-spec handle_config([getopt:option()], [atom()], proplists:proplist()) ->
{ok, {rlx_state:t(), [string()]}} |
relx:error().
-handle_config(Opts, Target, CommandLineConfig) ->
+handle_config(Opts, Targets, CommandLineConfig) ->
case validate_config(proplists:get_value(config, Opts, [])) of
Error = {error, _} ->
Error;
{ok, Config} ->
- {ok, rlx_state:new([{config, Config} | CommandLineConfig], Target)}
+ {ok, rlx_state:new([{config, Config} | CommandLineConfig], Targets)}
end.
--spec convert_target([string()]) -> {ok, release | relup} | relx:error().
-convert_target([]) ->
- {ok, release};
-convert_target(["release"]) ->
- {ok, release};
-convert_target(["relup"]) ->
- {ok, relup};
-convert_target(Target) ->
+-spec convert_targets([string()]) -> {ok, release | relup} | relx:error().
+convert_targets(Targets) ->
+ convert_targets(Targets, []).
+
+-spec convert_targets([string()], [string()]) -> {ok, release | relup} | relx:error().
+convert_targets([], []) ->
+ {ok, [release]};
+convert_targets([], Acc) ->
+ {ok, Acc};
+convert_targets(["release" | T], Acc) ->
+ convert_targets(T, [release | Acc]);
+convert_targets(["relup" | T], Acc) ->
+ convert_targets(T, [relup | Acc]);
+convert_targets(["tar" | T], Acc) ->
+ convert_targets(T, [tar | Acc]);
+convert_targets([Target | _T], _Acc) ->
?RLX_ERROR({invalid_target, Target}).
-spec validate_config(file:filename() | undefined) ->
diff --git a/src/rlx_goal.erl b/src/rlx_goal.erl
index 7226525..138a197 100644
--- a/src/rlx_goal.erl
+++ b/src/rlx_goal.erl
@@ -5,7 +5,7 @@
-compile(export_all).
-spec file(file:name()) -> any().
-file(Filename) -> {ok, Bin} = file:read_file(Filename), parse(Bin).
+file(Filename) -> case file:read_file(Filename) of {ok,Bin} -> parse(Bin); Err -> Err end.
-spec parse(binary() | list()) -> any().
parse(List) when is_list(List) -> parse(list_to_binary(List));
@@ -18,7 +18,7 @@ parse(Input) when is_binary(Input) ->
release_memo(), Result.
'constraint'(Input, Index) ->
- p(Input, Index, 'constraint', fun(I,D) -> (p_choose([p_seq([p_optional(fun 'ws'/2), fun 'app_name'/2, p_optional(fun 'ws'/2), fun 'between_op'/2, p_optional(fun 'ws'/2), fun 'version'/2, p_optional(fun 'ws'/2), p_string(<<",">>), p_optional(fun 'ws'/2), fun 'version'/2, p_optional(fun 'ws'/2), p_not(p_anything())]), p_seq([p_optional(fun 'ws'/2), fun 'app_name'/2, p_optional(fun 'ws'/2), fun 'constraint_op'/2, p_optional(fun 'ws'/2), fun 'version'/2, p_optional(fun 'ws'/2), p_not(p_anything())]), p_seq([p_optional(fun 'ws'/2), fun 'app_name'/2, p_optional(fun 'ws'/2), p_not(p_anything())])]))(I,D) end, fun(Node, _Idx) ->
+ p(Input, Index, 'constraint', fun(I,D) -> (p_choose([p_seq([p_optional(fun 'ws'/2), fun 'app_name'/2, p_optional(fun 'ws'/2), fun 'between_op'/2, p_optional(fun 'ws'/2), fun 'version'/2, p_optional(fun 'ws'/2), p_string(<<",">>), p_optional(fun 'ws'/2), fun 'version'/2, p_optional(fun 'ws'/2), p_not(p_anything())]), p_seq([p_optional(fun 'ws'/2), fun 'app_name'/2, p_optional(fun 'ws'/2), fun 'constraint_op'/2, p_optional(fun 'ws'/2), fun 'version'/2, p_optional(fun 'ws'/2), p_not(p_anything())]), p_seq([p_optional(fun 'ws'/2), fun 'app_name'/2, p_optional(fun 'ws'/2), p_not(p_anything())])]))(I,D) end, fun(Node, _Idx) ->
case Node of
[_,AppName,_, _] ->
{ok, AppName};
@@ -39,30 +39,30 @@ parse(Input) when is_binary(Input) ->
end).
'ws'(Input, Index) ->
- p(Input, Index, 'ws', fun(I,D) -> (p_charclass(<<"[\s\t\n\s\r]">>))(I,D) end, fun(Node, Idx) -> transform('ws', Node, Idx) end).
+ p(Input, Index, 'ws', fun(I,D) -> (p_charclass(<<"[\s\t\n\s\r]">>))(I,D) end, fun(Node, Idx) ->transform('ws', Node, Idx) end).
'app_name'(Input, Index) ->
- p(Input, Index, 'app_name', fun(I,D) -> (p_one_or_more(p_charclass(<<"[a-zA-Z0-9_]">>)))(I,D) end, fun(Node, _Idx) -> erlang:list_to_atom(erlang:binary_to_list(erlang:iolist_to_binary(Node))) end).
+ p(Input, Index, 'app_name', fun(I,D) -> (p_one_or_more(p_charclass(<<"[a-zA-Z0-9_]">>)))(I,D) end, fun(Node, _Idx) -> erlang:list_to_atom(erlang:binary_to_list(erlang:iolist_to_binary(Node))) end).
'between_op'(Input, Index) ->
- p(Input, Index, 'between_op', fun(I,D) -> (p_seq([p_string(<<":">>), p_optional(fun 'ws'/2), p_choose([p_string(<<"btwn">>), p_string(<<"between">>)]), p_optional(fun 'ws'/2), p_string(<<":">>)]))(I,D) end, fun(Node, _Idx) -> case Node of
+ p(Input, Index, 'between_op', fun(I,D) -> (p_seq([p_string(<<":">>), p_optional(fun 'ws'/2), p_choose([p_string(<<"btwn">>), p_string(<<"between">>)]), p_optional(fun 'ws'/2), p_string(<<":">>)]))(I,D) end, fun(Node, _Idx) -> case Node of
[C,_,Op,_,C] -> erlang:iolist_to_binary([C,Op,C]);
_ -> Node
end
end).
'constraint_op'(Input, Index) ->
- p(Input, Index, 'constraint_op', fun(I,D) -> (p_choose([p_string(<<"=">>), p_string(<<"-">>), p_string(<<"<=">>), p_string(<<"<">>), p_string(<<"~>">>), p_string(<<">=">>), p_string(<<">">>), fun 'word_constraint_op'/2, p_string(<<":">>)]))(I,D) end, fun(Node, Idx) -> transform('constraint_op', Node, Idx) end).
+ p(Input, Index, 'constraint_op', fun(I,D) -> (p_choose([p_string(<<"=">>), p_string(<<"-">>), p_string(<<"<=">>), p_string(<<"<">>), p_string(<<"~>">>), p_string(<<">=">>), p_string(<<">">>), fun 'word_constraint_op'/2, p_string(<<":">>)]))(I,D) end, fun(Node, Idx) ->transform('constraint_op', Node, Idx) end).
'word_constraint_op'(Input, Index) ->
- p(Input, Index, 'word_constraint_op', fun(I,D) -> (p_seq([p_string(<<":">>), p_optional(fun 'ws'/2), p_choose([p_string(<<"gte">>), p_string(<<"lte">>), p_string(<<"gt">>), p_string(<<"lt">>), p_string(<<"pes">>)]), p_optional(fun 'ws'/2), p_string(<<":">>)]))(I,D) end, fun(Node, _Idx) -> case Node of
+ p(Input, Index, 'word_constraint_op', fun(I,D) -> (p_seq([p_string(<<":">>), p_optional(fun 'ws'/2), p_choose([p_string(<<"gte">>), p_string(<<"lte">>), p_string(<<"gt">>), p_string(<<"lt">>), p_string(<<"pes">>)]), p_optional(fun 'ws'/2), p_string(<<":">>)]))(I,D) end, fun(Node, _Idx) -> case Node of
[C,_,Op,_,C] -> erlang:iolist_to_binary([C,Op,C]);
_ -> Node
end
end).
'version'(Input, Index) ->
- p(Input, Index, 'version', fun(I,D) -> (p_one_or_more(p_charclass(<<"[0-9a-zA-Z-+.]">>)))(I,D) end, fun(Node, Idx) -> transform('version', Node, Idx) end).
+ p(Input, Index, 'version', fun(I,D) -> (p_one_or_more(p_charclass(<<"[0-9a-zA-Z-+.]">>)))(I,D) end, fun(Node, Idx) ->transform('version', Node, Idx) end).
transform(_,Node,_Index) -> Node.
diff --git a/src/rlx_prv_assembler.erl b/src/rlx_prv_assembler.erl
index 52894f2..db5d147 100644
--- a/src/rlx_prv_assembler.erl
+++ b/src/rlx_prv_assembler.erl
@@ -48,7 +48,7 @@ do(State) ->
ok ->
case rlx_release:realized(Release) of
true ->
- copy_app_directories_to_output(State, Release, OutputDir);
+ run_actions(State, Release, OutputDir);
false ->
?RLX_ERROR({unresolved_release, RelName, RelVsn})
end;
@@ -56,6 +56,34 @@ do(State) ->
Error
end.
+do(release, State, Release, OutputDir) ->
+ copy_app_directories_to_output(State, Release, OutputDir);
+do(relup, State, Release, _OutputDir) ->
+ RelName = rlx_release:name(Release),
+ RelVsn = rlx_release:vsn(Release),
+ Release0 = rlx_state:get_realized_release(State, RelName, RelVsn),
+ make_relup(State, Release0);
+do(tar, State, Release, OutputDir) ->
+ make_tar(State, Release, OutputDir).
+
+run_actions(State, Release, OutputDir) ->
+ run_actions(State, Release, OutputDir, rlx_state:actions(State), [release, relup, tar]).
+
+run_actions(State, _Release, _OutputDir, _Actions, []) ->
+ {ok, State};
+run_actions(State, Release, OutputDir, Actions, [H | T]) ->
+ case lists:member(H, Actions) of
+ true ->
+ case do(H, State, Release, OutputDir) of
+ {ok, NewState} ->
+ run_actions(NewState, Release, OutputDir, Actions, T);
+ Error ->
+ Error
+ end;
+ false ->
+ run_actions(State, Release, OutputDir, Actions, T)
+ end.
+
-spec format_error(ErrorDetail::term()) -> iolist().
format_error({unresolved_release, RelName, RelVsn}) ->
io_lib:format("The release has not been resolved ~p-~s", [RelName, RelVsn]);
@@ -87,7 +115,7 @@ format_error({relup_generation_warning, Module, Warnings}) ->
["Warnings generating relup \s",
rlx_util:indent(1), Module:format_warning(Warnings)];
format_error({relup_script_generation_error,
- {relupcript_generation_error, systools_relup,
+ {relup_script_generation_error, systools_relup,
{missing_sasl, _}}}) ->
"Unfortunately, due to requirements in systools, you need to have the sasl application "
"in both the current release and the release to upgrade from.";
@@ -97,7 +125,16 @@ format_error({relup_script_generation_error, Module, Errors}) ->
format_error({unable_to_make_symlink, AppDir, TargetDir, Reason}) ->
io_lib:format("Unable to symlink directory ~s to ~s because \n~s~s",
[AppDir, TargetDir, rlx_util:indent(1),
- file:format_error(Reason)]).
+ file:format_error(Reason)]);
+format_error({tar_unknown_generation_error, Module, Vsn}) ->
+ io_lib:format("Tarball generation error of ~s ~s",
+ [Module, Vsn]);
+format_error({tar_generation_warn, Module, Warnings}) ->
+ io_lib:format("Tarball generation warnings for ~p : ~p",
+ [Module, Warnings]);
+format_error({tar_generation_error, Module, Errors}) ->
+ io_lib:format("Tarball generation error for ~p reason ~p",
+ [Module, Errors]).
%%%===================================================================
%%% Internal Functions
@@ -208,7 +245,7 @@ copy_dir(AppDir, TargetDir, SubDir) ->
end.
create_release_info(State0, Release0, OutputDir) ->
- RelName = erlang:atom_to_list(rlx_release:name(Release0)),
+ RelName = atom_to_list(rlx_release:name(Release0)),
ReleaseDir = release_output_dir(State0, Release0),
ReleaseFile = filename:join([ReleaseDir, RelName ++ ".rel"]),
ok = ec_file:mkdir_p(ReleaseDir),
@@ -330,9 +367,13 @@ include_erts(State, Release, OutputDir, RelDir) ->
ok = ec_file:copy(filename:join([Prefix, "bin", "start_clean.boot"]),
filename:join([OutputDir, "bin", "start_clean.boot"])),
NodeToolFile = nodetool_contents(),
+ InstallUpgradeFile = install_upgrade_escript_contents(),
NodeTool = filename:join([LocalErts, "bin", "nodetool"]),
+ InstallUpgrade = filename:join([LocalErts, "bin", "install_upgrade.escript"]),
ok = file:write_file(NodeTool, NodeToolFile),
- ok = file:change_mode(NodeTool, 8#755);
+ ok = file:write_file(InstallUpgrade, InstallUpgradeFile),
+ ok = file:change_mode(NodeTool, 8#755),
+ ok = file:change_mode(InstallUpgrade, 8#755);
false ->
ok
end,
@@ -358,13 +399,13 @@ make_boot_script(State, Release, OutputDir, RelDir) ->
ok ->
rlx_log:error(rlx_state:log(State),
"release successfully created!"),
- make_relup(State, Release);
+ {ok, State};
error ->
?RLX_ERROR({release_script_generation_error, ReleaseFile});
{ok, _, []} ->
rlx_log:error(rlx_state:log(State),
"release successfully created!"),
- make_relup(State, Release);
+ {ok, State};
{ok,Module,Warnings} ->
?RLX_ERROR({release_script_generation_warn, Module, Warnings});
{error,Module,Error} ->
@@ -385,30 +426,72 @@ make_script(Options, RunFun) ->
end.
make_relup(State, Release) ->
- case rlx_state:action(State) of
- relup ->
- UpFrom =
- case rlx_state:upfrom(State) of
- undefined ->
- get_last_release(State, Release);
- Vsn ->
- get_up_release(State, Release, Vsn)
- end,
- case UpFrom of
- undefined ->
- ?RLX_ERROR(no_upfrom_release_found);
- _ ->
- make_upfrom_script(State, Release, UpFrom)
- end;
+ UpFrom =
+ case rlx_state:upfrom(State) of
+ undefined ->
+ get_last_release(State, Release);
+ Vsn ->
+ get_up_release(State, Release, Vsn)
+ end,
+ case UpFrom of
+ undefined ->
+ ?RLX_ERROR(no_upfrom_release_found);
_ ->
- {ok, State}
+ make_upfrom_script(State, Release, UpFrom)
+ end.
+
+make_tar(State, Release, OutputDir) ->
+ Name = atom_to_list(rlx_release:name(Release)),
+ Vsn = rlx_release:vsn(Release),
+ Prefix = code:root_dir(),
+ ErtsVersion = rlx_release:erts(Release),
+ ErtsDir = filename:join([Prefix]),
+ case systools:make_tar(filename:join([OutputDir, "releases", Vsn, Name]),
+ [{path, [filename:join([OutputDir, "lib", "*", "ebin"])]},
+ {erts, ErtsDir},
+ {outdir, OutputDir}]) of
+ ok ->
+ TempDir = filename:join(OutputDir, integer_to_list(erlang:phash2(make_ref()))),
+ try
+ update_tar(State, TempDir, OutputDir, Name, Vsn, ErtsVersion)
+ catch
+ E:R ->
+ file:del_dir(TempDir),
+ ?RLX_ERROR({tar_generation_error, E, R})
+ end;
+ {ok, Module, Warnings} ->
+ ?RLX_ERROR({tar_generation_warn, Module, Warnings});
+ error ->
+ ?RLX_ERROR({tar_unknown_generation_error, Name, Vsn});
+ {error, Module, Errors} ->
+ ?RLX_ERROR({tar_generation_error, Module, Errors})
end.
+update_tar(State, TempDir, OutputDir, Name, Vsn, ErtsVersion) ->
+ TarFile = filename:join(OutputDir, Name++"-"++Vsn++".tar.gz"),
+ file:rename(filename:join(OutputDir, Name++".tar.gz"), TarFile),
+ erl_tar:extract(TarFile, [{cwd, TempDir}, compressed]),
+ ok = erl_tar:create(TarFile,
+ [{"erts-"++ErtsVersion, filename:join(TempDir, "erts-"++ErtsVersion)},
+ {filename:join(["erts-"++ErtsVersion, "bin", "nodetool"]),
+ hd(nodetool_contents())},
+ {filename:join(["erts-"++ErtsVersion, "bin", "install_upgrade.escript"]),
+ hd(install_upgrade_escript_contents())},
+ {"lib", filename:join(TempDir, "lib")},
+ {"releases", filename:join(TempDir, "releases")},
+ {filename:join(["releases", Vsn, "vm.args"]),
+ filename:join([OutputDir, "releases", Vsn, "vm.args"])},
+ {"bin", filename:join([OutputDir, "bin"])}], [compressed]),
+ rlx_log:info(rlx_state:log(State),
+ "tarball ~s successfully created!~n", [TarFile]),
+ rlx_util:delete_dir(TempDir),
+ {ok, State}.
+
make_upfrom_script(State, Release, UpFrom) ->
OutputDir = rlx_state:output_dir(State),
Options = [{outdir, OutputDir},
{path, get_code_paths(Release, OutputDir) ++
- get_code_paths(UpFrom, OutputDir)},
+ get_code_paths(UpFrom, OutputDir)},
silent],
CurrentRel = strip_rel(rlx_release:relfile(Release)),
UpFromRel = strip_rel(rlx_release:relfile(UpFrom)),
@@ -434,13 +517,12 @@ make_upfrom_script(State, Release, UpFrom) ->
{ok,_, Module,Warnings} ->
?RLX_ERROR({relup_script_generation_warn, Module, Warnings});
{error,Module,Errors} ->
- ?RLX_ERROR({relupcript_generation_error, Module, Errors})
+ ?RLX_ERROR({relup_script_generation_error, Module, Errors})
end.
write_relup_file(State, Release, Relup) ->
OutDir = release_output_dir(State, Release),
- RelName = rlx_util:to_string(rlx_release:name(Release)),
- RelupFile = filename:join(OutDir, RelName ++ ".relup"),
+ RelupFile = filename:join(OutDir, "relup"),
ok = ec_file:write_term(RelupFile, Relup).
strip_rel(Name) ->
@@ -485,7 +567,7 @@ release_output_dir(State, Release) ->
OutputDir = rlx_state:output_dir(State),
filename:join([OutputDir,
"releases",
- rlx_release:canonical_name(Release)]).
+ rlx_release:vsn(Release)]).
%% @doc Generates the correct set of code paths for the system.
-spec get_code_paths(rlx_release:t(), file:name()) -> [file:name()].
@@ -522,7 +604,7 @@ RELEASE_ROOT_DIR=`cd $SCRIPT_DIR/.. && pwd`
REL_NAME=">>, RelName, <<"
REL_VSN=">>, RelVsn, <<"
ERTS_VSN=">>, ErtsVsn, <<"
-REL_DIR=$RELEASE_ROOT_DIR/releases/$REL_NAME-$REL_VSN
+REL_DIR=$RELEASE_ROOT_DIR/releases/$REL_VSN
ERL_OPTS=">>, ErlOpts, <<"
find_erts_dir() {
@@ -568,9 +650,9 @@ RELEASE_ROOT_DIR=`cd $SCRIPT_DIR/.. && pwd`
REL_NAME=">>, RelName, <<"
REL_VSN=">>, RelVsn, <<"
ERTS_VSN=">>, ErtsVsn, <<"
-REL_DIR=$RELEASE_ROOT_DIR/releases/$REL_NAME-$REL_VSN
+REL_DIR=$RELEASE_ROOT_DIR/releases/$REL_VSN
ERL_OPTS=">>, ErlOpts, <<"
-PIPE_DIR=/tmp/$REL_DIR/
+PIPE_DIR=/tmp/erl_pipes/">>, RelName, <<"/
find_erts_dir() {
local erts_dir=$RELEASE_ROOT_DIR/erts-$ERTS_VSN
@@ -789,7 +871,7 @@ case \"$1\" in
node_name=`echo $NAME_ARG | awk '{print $2}'`
erlang_cookie=`echo $COOKIE_ARG | awk '{print $2}'`
- $ERTS_DIR/bin/escript $SCRIPT_DIR/bin/install_upgrade.escript $node_name $erlang_cookie $2
+ $ERTS_DIR/bin/escript $ERTS_DIR/bin/install_upgrade.escript $node_name $erlang_cookie $2
;;
console|console_clean|console_boot)
@@ -797,7 +879,7 @@ case \"$1\" in
# however, for debugging, sometimes start_clean.boot is useful.
# For e.g. 'setup', one may even want to name another boot script.
case \"$1\" in
- console) BOOTFILE=$REL_NAME ;;
+ console) [[ -f $REL_DIR/$REL_NAME ]] && BOOTFILE=$REL_NAME || BOOTFILE=start ;;
console_clean) BOOTFILE=start_clean ;;
console_boot)
shift
@@ -831,7 +913,7 @@ case \"$1\" in
# start up the release in the foreground for use by runit
# or other supervision services
- BOOTFILE=$REL_NAME
+ [[ -f $REL_DIR/$REL_NAME ]] && BOOTFILE=$REL_NAME || BOOTFILE=start
FOREGROUNDOPTIONS=\"-noinput +Bd\"
# Setup beam-required vars
@@ -860,6 +942,56 @@ esac
exit 0">>].
+install_upgrade_escript_contents() ->
+ [<<"#!/usr/bin/env escript
+%%! -noshell -noinput
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ft=erlang ts=4 sw=4 et
+
+-define(TIMEOUT, 60000).
+-define(INFO(Fmt,Args), io:format(Fmt,Args)).
+
+main([NodeName, Cookie, ReleasePackage]) ->TargetNode = start_distribution(NodeName, Cookie),
+ {ok, Cwd} = file:get_cwd(),
+ ok = rpc:call(TargetNode, file, set_cwd,
+ [Cwd], ?TIMEOUT),
+ case rpc:call(TargetNode, release_handler, unpack_release,
+ [ReleasePackage], ?TIMEOUT) of
+ {ok, Vsn} ->
+ ?INFO(\"Unpacked Release ~p~n\", [Vsn]),
+ {ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler,
+ check_install_release, [Vsn], ?TIMEOUT),
+ {ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler,
+ install_release, [Vsn], ?TIMEOUT),
+ ?INFO(\"Installed Release ~p~n\", [Vsn]),
+ ok = rpc:call(TargetNode, release_handler, make_permanent, [Vsn], ?TIMEOUT),
+ ?INFO(\"Made Release ~p Permanent~n\", [Vsn]);
+ {error, {existing_release, Vsn}} ->
+ ?INFO(\"Release ~s already installed~n\", [Vsn])
+ end;
+main(_) ->
+ init:stop(1).
+
+start_distribution(NodeName, Cookie) ->
+ MyNode = make_script_node(NodeName),
+ {ok, _Pid} = net_kernel:start([MyNode, longnames]),
+ erlang:set_cookie(node(), list_to_atom(Cookie)),
+ TargetNode = list_to_atom(NodeName),
+ case {net_kernel:connect_node(TargetNode),
+ net_adm:ping(TargetNode)} of
+ {true, pong} ->
+ ok;
+ {_, pang} ->
+ io:format(\"Node ~p not responding to pings.\n\", [TargetNode]),
+ init:stop(1)
+ end,
+ TargetNode.
+
+make_script_node(Node) ->
+ [Name, Host] = string:tokens(Node, \"@\"),
+ list_to_atom(lists:concat([Name, \"_upgrader_\", os:getpid(), \"@\", Host])).
+">>].
+
nodetool_contents() ->
[<<"%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ft=erlang ts=4 sw=4 et
diff --git a/src/rlx_prv_release.erl b/src/rlx_prv_release.erl
index ce14f4e..cf13953 100644
--- a/src/rlx_prv_release.erl
+++ b/src/rlx_prv_release.erl
@@ -142,7 +142,13 @@ solve_release(State0, DepGraph, RelName, RelVsn) ->
"Solving Release ~p-~s~n",
[RelName, RelVsn]),
try
- Release = rlx_state:get_configured_release(State0, RelName, RelVsn),
+ Release =
+ case get_realized_release(State0, RelName, RelVsn) of
+ undefined ->
+ rlx_state:get_configured_release(State0, RelName, RelVsn);
+ {ok, Release0} ->
+ rlx_release:relfile(rlx_state:get_configured_release(State0, RelName, RelVsn), rlx_release:relfile(Release0))
+ end,
Goals = rlx_release:goals(Release),
case Goals of
[] ->
@@ -176,6 +182,15 @@ set_resolved(State, Release0, Pkgs) ->
?RLX_ERROR({release_error, E})
end.
+get_realized_release(State, RelName, RelVsn) ->
+ try
+ Release = rlx_state:get_realized_release(State, RelName, RelVsn),
+ {ok, Release}
+ catch
+ throw:not_found ->
+ undefined
+ end.
+
%%%===================================================================
%%% Test Functions
%%%===================================================================
diff --git a/src/rlx_rel_discovery.erl b/src/rlx_rel_discovery.erl
index c1e74fb..3cdca3e 100644
--- a/src/rlx_rel_discovery.erl
+++ b/src/rlx_rel_discovery.erl
@@ -86,6 +86,8 @@ resolve_rel_metadata(State, LibDirs, AppMeta) ->
end.
-spec format_detail(ErrorDetail::term()) -> iolist().
+format_detail({_Module, {could_not_find, {ReleaseName, {Version, _}}}}) ->
+ io_lib:format("could not find app ~p ~p", [ReleaseName, Version]);
format_detail({accessing, File, eaccess}) ->
io_lib:format("permission denied accessing file ~s", [File]);
format_detail({accessing, File, Type}) ->
diff --git a/src/rlx_state.erl b/src/rlx_state.erl
index f5f363e..d03631d 100644
--- a/src/rlx_state.erl
+++ b/src/rlx_state.erl
@@ -25,7 +25,7 @@
-export([new/2,
log/1,
- action/1,
+ actions/1,
output_dir/1,
lib_dirs/1,
overrides/1,
@@ -72,18 +72,18 @@
-record(state_t, {log :: rlx_log:t(),
root_dir :: file:name(),
caller :: caller(),
- action :: atom(),
+ actions=[] :: [atom()],
output_dir :: file:name(),
lib_dirs=[] :: [file:name()],
config_file=[] :: file:filename() | undefined,
goals=[] :: [rlx_depsolver:constraint()],
- providers = [] :: [rlx_provider:t()],
- available_apps = [] :: [rlx_app_info:t()],
+ providers=[] :: [rlx_provider:t()],
+ available_apps=[] :: [rlx_app_info:t()],
default_configured_release :: {rlx_release:name(), rlx_release:vsn()},
vm_args :: file:filename() | undefined,
sys_config :: file:filename() | undefined,
overrides :: [{AppName::atom(), Directory::file:filename()}],
- skip_apps = [] :: [AppName::atom()],
+ skip_apps=[] :: [AppName::atom()],
configured_releases :: releases(),
realized_releases :: releases(),
upfrom :: string() | binary() | undefined,
@@ -106,17 +106,17 @@
%% API
%%============================================================================
%% @doc Create a new 'log level' for the system
--spec new(proplists:proplist(), atom()) -> t().
-new(PropList, Target)
+-spec new(proplists:proplist(), [atom()]) -> t().
+new(PropList, Targets)
when erlang:is_list(PropList),
- erlang:is_atom(Target) ->
+ erlang:is_list(Targets) ->
{ok, Root} = file:get_cwd(),
State0 =
#state_t{log = proplists:get_value(log, PropList, rlx_log:new(error)),
output_dir=proplists:get_value(output_dir, PropList, ""),
lib_dirs=[to_binary(Dir) || Dir <- proplists:get_value(lib_dirs, PropList, [])],
config_file=proplists:get_value(config, PropList, undefined),
- action = Target,
+ actions = Targets,
caller = proplists:get_value(caller, PropList, api),
goals=proplists:get_value(goals, PropList, []),
providers = [],
@@ -132,10 +132,10 @@ new(PropList, Target)
disable_default_libs,
proplists:get_value(disable_default_libs, PropList, false)).
-%% @doc the action targeted for this system
--spec action(t()) -> atom().
-action(#state_t{action=Action}) ->
- Action.
+%% @doc the actions targeted for this system
+-spec actions(t()) -> atom().
+actions(#state_t{actions=Actions}) ->
+ Actions.
%% @doc the application overrides for the system
-spec overrides(t()) -> [{AppName::atom(), Directory::file:filename()}].
@@ -359,7 +359,7 @@ to_binary(Dir)
new_test() ->
LogState = rlx_log:new(error),
- RCLState = new([{log, LogState}], release),
+ RCLState = new([{log, LogState}], [release]),
?assertMatch(LogState, log(RCLState)).
-endif.
diff --git a/src/rlx_util.erl b/src/rlx_util.erl
index ac6af5c..c821896 100644
--- a/src/rlx_util.erl
+++ b/src/rlx_util.erl
@@ -21,7 +21,8 @@
%%% @doc Trivial utility file to help handle common tasks
-module(rlx_util).
--export([mkdir_p/1,
+-export([delete_dir/1,
+ mkdir_p/1,
to_binary/1,
to_string/1,
to_atom/1,
@@ -39,6 +40,19 @@
%%============================================================================
%% API
%%============================================================================
+%% @doc Deletes non-empty directory
+delete_dir(Path) ->
+ lists:foldr(fun(File, ok) ->
+ case filelib:is_dir(File) of
+ true ->
+ file:del_dir(File);
+ false ->
+ file:delete(File)
+ end
+ end, ok, filelib:wildcard(filename:join(Path, "**"))),
+ ok = file:del_dir(Path).
+
+
%% @doc Makes a directory including parent dirs if they are missing.
-spec mkdir_p(string()) -> ok | {error, Reason::file:posix()}.
mkdir_p(Path) ->
diff --git a/test/rlx_discover_SUITE.erl b/test/rlx_discover_SUITE.erl
index d225ee6..d404d8c 100644
--- a/test/rlx_discover_SUITE.erl
+++ b/test/rlx_discover_SUITE.erl
@@ -46,7 +46,7 @@ init_per_testcase(_, Config) ->
LibDir2 = filename:join([DataDir, create_random_name("lib_dir2_")]),
ok = rlx_util:mkdir_p(LibDir1),
ok = rlx_util:mkdir_p(LibDir2),
- State = rlx_state:new([{lib_dirs, [LibDir1, LibDir2]}], release),
+ State = rlx_state:new([{lib_dirs, [LibDir1, LibDir2]}], [release]),
[{lib1, LibDir1},
{lib2, LibDir2},
{state, State} | Config].
diff --git a/test/rlx_release_SUITE.erl b/test/rlx_release_SUITE.erl
index ea4b20d..ccf5a8f 100644
--- a/test/rlx_release_SUITE.erl
+++ b/test/rlx_release_SUITE.erl
@@ -56,7 +56,7 @@ init_per_testcase(_, Config) ->
DataDir = proplists:get_value(data_dir, Config),
LibDir1 = filename:join([DataDir, create_random_name("lib_dir1_")]),
ok = rlx_util:mkdir_p(LibDir1),
- State = rlx_state:new([{lib_dirs, [LibDir1]}], release),
+ State = rlx_state:new([{lib_dirs, [LibDir1]}], [release]),
[{lib1, LibDir1},
{state, State} | Config].
@@ -602,7 +602,7 @@ make_relup_release(Config) ->
{lib_dirs, [LibDir1]},
{log_level, 2},
{output_dir, OutputDir},
- {config, ConfigFile}], ["relup"]),
+ {config, ConfigFile}], ["release", "relup"]),
%% we should have one 'resolved' release and three discovered realized_releases.
?assertMatch([{foo, "0.0.1"},
@@ -619,9 +619,7 @@ make_relup_release(Config) ->
?assertMatch({ok, [{"0.0.3",
[{"0.0.2",[],[point_of_no_return]}],
[{"0.0.2",[],[point_of_no_return]}]}]},
- file:consult(filename:join(filename:dirname(rlx_release:relfile(Release)),
- filename:basename(rlx_release:relfile(Release), ".rel") ++
- ".relup"))),
+ file:consult(filename:join(filename:dirname(rlx_release:relfile(Release)), "relup"))),
?assertMatch(foo, rlx_release:name(Release)),
?assertMatch("0.0.3", rlx_release:vsn(Release)),
@@ -685,7 +683,7 @@ make_relup_release2(Config) ->
{lib_dirs, [LibDir1]},
{log_level, 2},
{output_dir, OutputDir},
- {config, ConfigFile}], ["relup"]),
+ {config, ConfigFile}], ["release", "relup"]),
%% we should have one 'resolved' release and three discovered realized_releases.
?assertMatch([{foo, "0.0.1"},
@@ -702,9 +700,7 @@ make_relup_release2(Config) ->
?assertMatch({ok, [{"0.0.3",
[{"0.0.1",[],[point_of_no_return]}],
[{"0.0.1",[],[point_of_no_return]}]}]},
- file:consult(filename:join(filename:dirname(rlx_release:relfile(Release)),
- filename:basename(rlx_release:relfile(Release), ".rel") ++
- ".relup"))),
+ file:consult(filename:join(filename:dirname(rlx_release:relfile(Release)), "relup"))),
?assertMatch(foo, rlx_release:name(Release)),
?assertMatch("0.0.3", rlx_release:vsn(Release)),