浏览代码

Feature: rebar shell [--script <FILE>]

Adds the ability to run an escript before starting the apps and
interactive shell for a project. This is intended to improve the
local development experience for projects by providing an easy way to
run companion services (mock rest APIs, databases etc) that the
project relies on.

This patch also adds {shell, Defaults} to the rebar config file so
that a project can supply default values for many of the new or
improved 'rebar3 shell' options:
* {apps, OTPApps}
* {script_file, EscriptFileName}
* {config, ConfigFileName}

The order of option precedence is command line, rebar.config, relx.
pull/893/head
Geoff Cant 9 年前
父节点
当前提交
748142838c
共有 1 个文件被更改,包括 131 次插入29 次删除
  1. +131
    -29
      src/rebar_prv_shell.erl

+ 131
- 29
src/rebar_prv_shell.erl 查看文件

@ -56,12 +56,23 @@ init(State) ->
{short_desc, "Run shell with project apps and deps in path."},
{desc, info()},
{opts, [{config, undefined, "config", string,
"Path to the config file to use. Defaults to the "
"sys_config defined for relx, if present."},
"Path to the config file to use. Defaults to "
"{shell, [{config, File}]} and then the relx "
"sys.config file if not specified."},
{name, undefined, "name", atom,
"Gives a long name to the node."},
{sname, undefined, "sname", atom,
"Gives a short name to the node."}]}
"Gives a short name to the node."},
{script_file, undefined, "script", string,
"Path to an escript file to run before "
"starting the project apps. Defaults to "
"rebar.config {shell, [{script_file, File}]} "
"if not specified."},
{apps, undefined, "apps", string,
"A list of apps to boot before starting the "
"shell. (E.g. --apps app1,app2,app3) Defaults "
"to rebar.config {shell, [{apps, Apps}]} or "
"relx apps if not specified."}]}
])
),
{ok, State1}.
@ -86,6 +97,7 @@ shell(State) ->
setup_name(State),
setup_paths(State),
setup_shell(),
maybe_run_script(State),
%% apps must be started after the change in shell because otherwise
%% their application masters never gets the new group leader (held in
%% their internal state)
@ -134,6 +146,51 @@ setup_paths(State) ->
%% add project app test paths
ok = add_test_paths(State).
maybe_run_script(State) ->
case first_value([fun find_script_option/1,
fun find_script_rebar/1], State) of
no_value ->
?DEBUG("No script_file specified.", []),
ok;
"none" ->
?DEBUG("Shell script execution skipped (--script none).", []),
ok;
RelFile ->
File = filename:absname(RelFile),
try run_script_file(File)
catch
C:E ->
?ABORT("Couldn't run shell escript ~p - ~p:~p~nStack: ~p",
[File, C, E, erlang:get_stacktrace()])
end
end.
-spec find_script_option(rebar_state:t()) -> no_value | list().
find_script_option(State) ->
{Opts, _} = rebar_state:command_parsed_args(State),
debug_get_value(script_file, Opts, no_value,
"Found script file from command line option.").
-spec find_script_rebar(rebar_state:t()) -> no_value | list().
find_script_rebar(State) ->
Config = rebar_state:get(State, shell, []),
%% Either a string, or undefined
debug_get_value(script_file, Config, no_value,
"Found script file from rebar config file.").
run_script_file(File) ->
?DEBUG("Extracting escript from ~p", [File]),
{ok, Script} = escript:extract(File, [compile_source]),
Beam = proplists:get_value(source, Script),
Mod = proplists:get_value(module, beam_lib:info(Beam)),
?DEBUG("Compiled escript as ~p", [Mod]),
FakeFile = "/fake_path/" ++ atom_to_list(Mod),
{module, Mod} = code:load_binary(Mod, FakeFile, Beam),
?DEBUG("Evaling ~p:main([]).", [Mod]),
Result = Mod:main([]),
?DEBUG("Result: ~p", [Result]),
Result.
maybe_boot_apps(State) ->
case find_apps_to_boot(State) of
undefined ->
@ -172,17 +229,42 @@ check_epmd(_) ->
find_apps_to_boot(State) ->
%% Try the shell_apps option
case rebar_state:get(State, shell_apps, undefined) of
undefined ->
%% Get to the relx tuple instead
case lists:keyfind(release, 1, rebar_state:get(State, relx, [])) of
{_, _, Apps} -> Apps;
false -> undefined
end;
case first_value([fun find_apps_option/1,
fun find_apps_rebar/1,
fun find_apps_relx/1], State) of
no_value ->
undefined;
Apps ->
Apps
end.
-spec find_apps_option(rebar_state:t()) -> no_value | [atom()].
find_apps_option(State) ->
{Opts, _} = rebar_state:command_parsed_args(State),
case debug_get_value(apps, Opts, no_value,
"Found shell apps from command line option.") of
no_value -> no_value;
AppsStr ->
[ list_to_atom(AppStr)
|| AppStr <- string:tokens(AppsStr, " ,:") ]
end.
-spec find_apps_rebar(rebar_state:t()) -> no_value | list().
find_apps_rebar(State) ->
ShellOpts = rebar_state:get(State, shell, []),
debug_get_value(apps, ShellOpts, no_value,
"Found shell opts from command line option.").
-spec find_apps_relx(rebar_state:t()) -> no_value | list().
find_apps_relx(State) ->
case lists:keyfind(release, 1, rebar_state:get(State, relx, [])) of
{_, _, Apps} ->
?DEBUG("Found shell apps from relx.", []),
Apps;
false ->
no_value
end.
load_apps(Apps) ->
[case application:load(App) of
ok ->
@ -261,31 +343,51 @@ add_test_paths(State) ->
% First try the --config flag, then try the relx sys_config
-spec find_config(rebar_state:t()) -> [tuple()] | no_config.
find_config(State) ->
case find_config_option(State) of
no_config ->
find_config_relx(State);
Result ->
Result
case first_value([fun find_config_option/1,
fun find_config_rebar/1,
fun find_config_relx/1], State) of
no_value ->
no_config;
Filename when is_list(Filename) ->
consult_config(State, Filename)
end.
-spec first_value([Fun], State) -> no_value | Value when
Value :: any(),
State :: rebar_state:t(),
Fun :: fun ((State) -> no_value | Value).
first_value([], _) -> no_value;
first_value([Fun | Rest], State) ->
case Fun(State) of
no_value ->
first_value(Rest, State);
Value ->
Value
end.
-spec find_config_option(rebar_state:t()) -> [tuple()] | no_config.
debug_get_value(Key, List, Default, Description) ->
case proplists:get_value(Key, List, Default) of
Default -> Default;
Value ->
?DEBUG(Description, []),
Value
end.
-spec find_config_option(rebar_state:t()) -> Filename::list() | no_value.
find_config_option(State) ->
{Opts, _} = rebar_state:command_parsed_args(State),
case proplists:get_value(config, Opts) of
undefined ->
no_config;
Filename ->
consult_config(State, Filename)
end.
debug_get_value(config, Opts, no_value,
"Found config from command line option.").
-spec find_config_relx(rebar_state:t()) -> [tuple()] | no_config.
-spec find_config_rebar(rebar_state:t()) -> [tuple()] | no_value.
find_config_rebar(State) ->
debug_get_value(config, rebar_state:get(State, shell, []), no_value,
"Found config from rebar config file.").
-spec find_config_relx(rebar_state:t()) -> [tuple()] | no_value.
find_config_relx(State) ->
case proplists:get_value(sys_config, rebar_state:get(State, relx, [])) of
undefined ->
no_config;
Filename ->
consult_config(State, Filename)
end.
debug_get_value(sys_config, rebar_state:get(State, relx, []), no_value,
"Found config from relx.").
-spec consult_config(rebar_state:t(), string()) -> [tuple()].
consult_config(State, Filename) ->

正在加载...
取消
保存