diff options
| -rw-r--r-- | .travis.yml | 2 | ||||
| -rw-r--r-- | CONTRIBUTING.md | 4 | ||||
| -rw-r--r-- | README.md | 10 | ||||
| -rwxr-xr-x | bootstrap | 4 | ||||
| -rw-r--r-- | priv/templates/app.erl | 3 | ||||
| -rw-r--r-- | rebar.config | 4 | ||||
| -rw-r--r-- | rebar.lock | 4 | ||||
| -rw-r--r-- | src/rebar.hrl | 2 | ||||
| -rw-r--r-- | src/rebar_agent.erl | 17 | ||||
| -rw-r--r-- | src/rebar_dist_utils.erl | 89 | ||||
| -rw-r--r-- | src/rebar_git_resource.erl | 2 | ||||
| -rw-r--r-- | src/rebar_prv_common_test.erl | 22 | ||||
| -rw-r--r-- | src/rebar_prv_dialyzer.erl | 2 | ||||
| -rw-r--r-- | src/rebar_prv_edoc.erl | 8 | ||||
| -rw-r--r-- | src/rebar_prv_eunit.erl | 27 | ||||
| -rw-r--r-- | src/rebar_prv_packages.erl | 32 | ||||
| -rw-r--r-- | src/rebar_prv_path.erl | 8 | ||||
| -rw-r--r-- | src/rebar_prv_report.erl | 2 | ||||
| -rw-r--r-- | src/rebar_prv_shell.erl | 68 | ||||
| -rw-r--r-- | test/rebar_dist_utils_SUITE.erl | 74 | 
20 files changed, 299 insertions, 85 deletions
| diff --git a/.travis.yml b/.travis.yml index 630beb2..ab2d28d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ cache:  before_deploy: "rm -rf !(rebar3)"  deploy:    on: -    branch: master +    tags: true      condition: $TRAVIS_OTP_RELEASE = R16B03-1    provider: s3    access_key_id: AKIAJAPYAQEFYCYSNL7Q diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f175cc2..8077813 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,7 +23,7 @@ All files without specific headers can safely be assumed to be under Apache  ## Submitting a Bug -Bugs can be submitted to the [Github issue page](https://github.com/rebar/rebar3/issues). +Bugs can be submitted to the [Github issue page](https://github.com/erlang/rebar3/issues).  Rebar3 is not perfect software and will be buggy. When submitting a bug, be  careful to know the following: @@ -58,7 +58,7 @@ Before requesting or implementing a new feature, please do the following:  - Take a look at our [list of plugins](http://www.rebar3.org/docs/using-available-plugins)    to know if the feature isn't already supported by the community. -- Verify in existing [tickets](https://github.com/rebar/rebar3/issues) whether +- Verify in existing [tickets](https://github.com/erlang/rebar3/issues) whether    the feature might already is in the works, has been moved to a plugin, or    has already been rejected. @@ -1,6 +1,6 @@  # Rebar3 -[](https://travis-ci.org/rebar/rebar3) [](https://ci.appveyor.com/project/TristanSloughter/rebar3) +[](https://travis-ci.org/erlang/rebar3) [](https://ci.appveyor.com/project/TristanSloughter/rebar3)  1. [What is Rebar3?](#what-is-rebar3)  2. [Why Rebar3?](#why-rebar3) @@ -64,12 +64,12 @@ $ wget https://s3.amazonaws.com/rebar3/rebar3 && chmod +x rebar3  From Source (assuming you have a full Erlang install):  ```bash -$ git clone https://github.com/rebar/rebar3.git +$ git clone https://github.com/erlang/rebar3.git  $ cd rebar3  $ ./bootstrap  ``` -Stable versions can be obtained from the [releases page](https://github.com/rebar/rebar3/releases). +Stable versions can be obtained from the [releases page](https://github.com/erlang/rebar3/releases).  The rebar3 escript can also extract itself with a run script under the user's home directory: @@ -159,13 +159,13 @@ quick feedback, you can try the #rebar channel on  asking about things with well known answers.  For bug reports, roadmaps, and issues, visit the [github issues -page](https://github.com/rebar/rebar3/issues). +page](https://github.com/erlang/rebar3/issues).  General rebar community resources and links:  - [Rebar Mailing List](http://lists.basho.com/pipermail/rebar_lists.basho.com/)  - #rebar on [irc.freenode.net](http://freenode.net/) -- [issues](https://github.com/rebar/rebar3/issues) +- [issues](https://github.com/erlang/rebar3/issues)  - [Documentation](http://www.rebar3.org/v3.0/docs)  To contribute to rebar3, please refer to [CONTRIBUTING](CONTRIBUTING.md). @@ -24,7 +24,7 @@ main(_) ->      bootstrap_rebar3(),      %% Build rebar.app from rebar.app.src -    {ok, App} = rebar_app_info:new(rebar, "3.0.0", filename:absname("_build/default/lib/rebar/")), +    {ok, App} = rebar_app_info:new(rebar, "3.1.0", filename:absname("_build/default/lib/rebar/")),      rebar_otp_app:compile(rebar_state:new(), App),      %% Because we are compiling files that are loaded already we want to silence @@ -92,7 +92,7 @@ fetch({pkg, Name, Vsn}, App) ->      Dir = filename:join([filename:absname("_build/default/lib/"), App]),      case filelib:is_dir(Dir) of          false -> -            CDN = "https://s3.amazonaws.com/s3.hex.pm/tarballs", +            CDN = "https://repo.hex.pm/tarballs",              Package = binary_to_list(<<Name/binary, "-", Vsn/binary, ".tar">>),              Url = string:join([CDN, Package], "/"),              case request(Url) of diff --git a/priv/templates/app.erl b/priv/templates/app.erl index 83eb9a3..62d2ddf 100644 --- a/priv/templates/app.erl +++ b/priv/templates/app.erl @@ -8,8 +8,7 @@  -behaviour(application).  %% Application callbacks --export([start/2 -        ,stop/1]). +-export([start/2, stop/1]).  %%====================================================================  %% API diff --git a/rebar.config b/rebar.config index 575c616..5d3b310 100644 --- a/rebar.config +++ b/rebar.config @@ -1,13 +1,13 @@  %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-  %% ex: ts=4 sw=4 ft=erlang et -{deps, [{erlware_commons,     "0.19.0"}, +{deps, [{erlware_commons,     "0.20.0"},          {ssl_verify_hostname, "1.0.5"},          {certifi,             "0.4.0"},          {providers,           "1.6.0"},          {getopt,              "0.8.2"},          {bbmustache,          "1.0.4"}, -        {relx,                "3.17.0"}, +        {relx,                "3.19.0"},          {cf,                  "0.2.1"},          {cth_readable,        "1.2.2"},          {eunit_formatters,    "0.3.1"}]}. @@ -2,9 +2,9 @@   {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0},   {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0},   {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.2">>},0}, - {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.19.0">>},0}, + {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.20.0">>},0},   {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0},   {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0},   {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0}, - {<<"relx">>,{pkg,<<"relx">>,<<"3.17.0">>},0}, + {<<"relx">>,{pkg,<<"relx">>,<<"3.19.0">>},0},   {<<"ssl_verify_hostname">>,{pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>},0}]. diff --git a/src/rebar.hrl b/src/rebar.hrl index f4e7f5e..0b7f0b1 100644 --- a/src/rebar.hrl +++ b/src/rebar.hrl @@ -22,7 +22,7 @@  -define(DEFAULT_TEST_DEPS_DIR, "test/lib").  -define(DEFAULT_RELEASE_DIR, "rel").  -define(DEFAULT_CONFIG_FILE, "rebar.config"). --define(DEFAULT_CDN, "https://s3.amazonaws.com/s3.hex.pm/"). +-define(DEFAULT_CDN, "https://repo.hex.pm/").  -define(REMOTE_PACKAGE_DIR, "tarballs").  -define(REMOTE_REGISTRY_FILE, "registry.ets.gz").  -define(LOCK_FILE, "rebar.lock"). diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl index 2b69812..95818d8 100644 --- a/src/rebar_agent.erl +++ b/src/rebar_agent.erl @@ -86,6 +86,8 @@ refresh_paths(RState) ->                       || App <- rebar_state:project_apps(RState)]                  %% make sure to never reload self; halt()s the VM                  ) -- [filename:dirname(code:which(?MODULE))], +    %% Modules from apps we can't reload without breaking functionality +    Blacklist = [ec_cmd_log, providers, cf, cth_readable],      %% Similar to rebar_utils:update_code/1, but also forces a reload      %% of used modules. Also forces to reload all of ebin/ instead      %% of just the modules in the .app file, because 'extra_src_dirs' @@ -102,11 +104,16 @@ refresh_paths(RState) ->                  undefined ->                      code:add_patha(Path),                      ok; -                {ok, _} -> -                    ?DEBUG("reloading ~p from ~s", [Modules, Path]), -                    code:replace_path(App, Path), -                    [begin code:purge(M), code:delete(M), code:load_file(M) end -                    || M <- Modules] +                {ok, Mods} -> +                    case {length(Mods), length(Mods -- Blacklist)} of +                        {X,X} -> +                            ?DEBUG("reloading ~p from ~s", [Modules, Path]), +                            code:replace_path(App, Path), +                            [begin code:purge(M), code:delete(M), code:load_file(M) end +                             || M <- Modules]; +                        {_,_} -> +                            ?DEBUG("skipping app ~p, stable copy required", [App]) +                    end              end          end, ToRefresh). diff --git a/src/rebar_dist_utils.erl b/src/rebar_dist_utils.erl new file mode 100644 index 0000000..f462826 --- /dev/null +++ b/src/rebar_dist_utils.erl @@ -0,0 +1,89 @@ +%%% Common functions to boot/stop distributed setups for +%%% the rebar3 script. +-module(rebar_dist_utils). +-export([either/3, short/2, long/2, find_options/1]). +-include("rebar.hrl"). + +%%%%%%%%%%%%%%%%%% +%%% PUBLIC API %%% +%%%%%%%%%%%%%%%%%% +-spec either(Name::atom(), SName::atom(), Opts::[{setcookie,term()}]) -> atom(). +either(undefined, undefined, _) -> +    'nonode@nohost'; +either(Name, undefined, Opts) -> +    long(Name, Opts), +    node(); +either(undefined, SName, Opts) -> +    short(SName, Opts), +    node(); +either(_, _, _) -> +    ?ABORT("Cannot have both short and long node names defined", []). + +short(Name, Opts) -> +    start(Name, shortnames, Opts). + +long(Name, Opts) -> +    start(Name, longnames, Opts). + +-spec find_options(rebar_state:state()) -> {Long, Short, Opts} when +      Long :: atom(), +      Short :: atom(), +      Opts :: [{setcookie,term()}]. +find_options(State) -> +    {Long, Short} = find_name_options(State), +    case find_cookie_option(State) of +        nocookie -> +            {Long, Short, []}; +        Cookie -> +            {Long, Short, [{setcookie, Cookie}]} +    end. + +%%%%%%%%%%%%%%% +%%% PRIVATE %%% +%%%%%%%%%%%%%%% +start(Name, Type, Opts) -> +    check_epmd(net_kernel:start([Name, Type])), +    setup_cookie(Opts). + +check_epmd({error,{{shutdown, {_,net_kernel,{'EXIT',nodistribution}}},_}}) -> +    ?ERROR("Erlang Distribution failed, falling back to nonode@nohost. " +           "Verify that epmd is running and try again.",[]); +check_epmd(_) -> +    ok. + +setup_cookie(Opts) -> +    case {node(), proplists:get_value(setcookie, Opts, nocookie)} of +        {'nonode@nohost', _} -> nocookie; +        {_, nocookie} -> nocookie; +        {Node, Name} -> erlang:set_cookie(Node, Name) +    end. + +find_name_options(State) -> +    {Opts, _} = rebar_state:command_parsed_args(State), +    %% First try the CLI +    case {proplists:get_value(name, Opts), proplists:get_value(sname, Opts)} of +        {undefined, undefined} -> +            %% Else try the config file +            DistOpts = rebar_state:get(State, dist_node, []), +            %% Pick the first one seen to support profile merges +            find_first_name(DistOpts); +        Res -> +            Res +    end. + +find_first_name([]) -> {undefined, undefined}; +find_first_name([{sname,Val}|_]) -> {undefined, Val}; +find_first_name([{name,Val}|_]) -> {Val, undefined}; +find_first_name([_|Opts]) -> find_first_name(Opts). + +find_cookie_option(State) -> +    {Opts, _} = rebar_state:command_parsed_args(State), +    %% First try the CLI +    case proplists:get_value(setcookie, Opts) of +        undefined -> +            %% Else try the config file +            DistOpts = rebar_state:get(State, dist_node, []), +            proplists:get_value(setcookie, DistOpts, nocookie); +        Res -> +            Res +    end. diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl index 876d047..5a6a5ef 100644 --- a/src/rebar_git_resource.erl +++ b/src/rebar_git_resource.erl @@ -201,7 +201,7 @@ parse_tags(Dir) ->          {error, _} ->              {undefined, "0.0.0"};          {ok, Line} -> -            case re:run(Line, "(\\(|\\s)(HEAD,\\s)tag:\\s(v?([^,\\)]+))", [{capture, [3, 4], list}]) of +            case re:run(Line, "(\\(|\\s)(HEAD[^,]*,\\s)tag:\\s(v?([^,\\)]+))", [{capture, [3, 4], list}]) of                  {match,[Tag, Vsn]} ->                      {Tag, Vsn};                  nomatch -> diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index 6b94f79..5712fbf 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -37,6 +37,7 @@ init(State) ->  -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.  do(State) -> +    setup_name(State),      Tests = prepare_tests(State),      case compile(State, Tests) of          %% successfully compiled apps @@ -105,6 +106,10 @@ format_error({multiple_errors, Errors}) ->  %% Internal functions  %% =================================================================== +setup_name(State) -> +    {Long, Short, Opts} = rebar_dist_utils:find_options(State), +    rebar_dist_utils:either(Long, Short, Opts). +  prepare_tests(State) ->      %% command line test options      CmdOpts = cmdopts(State), @@ -189,8 +194,10 @@ ensure_opts([{suite, Suites}|Rest], Acc) ->      ensure_opts(Rest, [NewSuites|Acc]);  ensure_opts([{K, V}|Rest], Acc) ->      ensure_opts(Rest, [{K, V}|Acc]); -ensure_opts([V|_Rest], _Acc) -> -    ?PRV_ERROR({badconfig, {"Member `~p' of option `~p' must be a 2-tuple", {V, ct_opts}}}). +%% pass through other options, in case of things like config terms +%% in `ct_opts` +ensure_opts([V|Rest], Acc) -> +    ensure_opts(Rest, [V|Acc]).  add_hooks(Opts, State) ->      case {readable(State), lists:keyfind(ct_hooks, 1, Opts)} of @@ -637,7 +644,10 @@ ct_opts(_State) ->       {create_priv_dir, undefined, "create_priv_dir", string, help(create_priv_dir)},       {include, undefined, "include", string, help(include)},       {readable, undefined, "readable", boolean, help(readable)}, -     {verbose, $v, "verbose", boolean, help(verbose)} +     {verbose, $v, "verbose", boolean, help(verbose)}, +     {name, undefined, "name", atom, help(name)}, +     {sname, undefined, "sname", atom, help(sname)}, +     {setcookie, undefined, "setcookie", atom, help(setcookie)}      ].  help(dir) -> @@ -692,5 +702,11 @@ help(readable) ->      "Shows test case names and only displays logs to shell on failures";  help(verbose) ->      "Verbose output"; +help(name) -> +    "Gives a long name to the node"; +help(sname) -> +    "Gives a short name to the node"; +help(setcookie) -> +    "Sets the cookie if the node is distributed";  help(_) ->      "". diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl index 834eb98..622ee60 100644 --- a/src/rebar_prv_dialyzer.erl +++ b/src/rebar_prv_dialyzer.erl @@ -97,7 +97,7 @@ do(State) ->      end.  %% This is used to workaround dialyzer quirk discussed here -%% https://github.com/rebar/rebar3/pull/489#issuecomment-107953541 +%% https://github.com/erlang/rebar3/pull/489#issuecomment-107953541  %% Dialyzer gets default plt location wrong way by peeking HOME environment  %% variable which usually is not defined on Windows.  maybe_fix_env() -> diff --git a/src/rebar_prv_edoc.erl b/src/rebar_prv_edoc.erl index e7048b6..6cefe14 100644 --- a/src/rebar_prv_edoc.erl +++ b/src/rebar_prv_edoc.erl @@ -32,13 +32,19 @@ init(State) ->  do(State) ->      code:add_pathsa(rebar_state:code_paths(State, all_deps)),      ProjectApps = rebar_state:project_apps(State), +    Providers = rebar_state:providers(State),      EDocOpts = rebar_state:get(State, edoc_opts, []), +    Cwd = rebar_state:dir(State), +    rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State),      lists:foreach(fun(AppInfo) -> +                          rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, AppInfo, State),                            AppName = ec_cnv:to_list(rebar_app_info:name(AppInfo)),                            ?INFO("Running edoc for ~s", [AppName]),                            AppDir = rebar_app_info:dir(AppInfo), -                          ok = edoc:application(list_to_atom(AppName), AppDir, EDocOpts) +                          ok = edoc:application(list_to_atom(AppName), AppDir, EDocOpts), +                          rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, AppInfo, State)                    end, ProjectApps), +    rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State),      rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),      {ok, State}. diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index 46ea48d..942fd10 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -51,6 +51,7 @@ do(State) ->  do(State, Tests) ->      ?INFO("Performing EUnit tests...", []), +    setup_name(State),      rebar_utils:update_code(rebar_state:code_paths(State, all_deps), [soft_purge]),      %% Run eunit provider prehooks @@ -106,6 +107,10 @@ format_error({error, Error}) ->  %% Internal functions  %% =================================================================== +setup_name(State) -> +    {Long, Short, Opts} = rebar_dist_utils:find_options(State), +    rebar_dist_utils:either(Long, Short, Opts). +  prepare_tests(State) ->      %% parse and translate command line tests      CmdTests = resolve_tests(State), @@ -479,11 +484,17 @@ eunit_opts(_State) ->       {file, $f, "file", string, help(file)},       {module, $m, "module", string, help(module)},       {suite, $s, "suite", string, help(module)}, -     {verbose, $v, "verbose", boolean, help(verbose)}]. - -help(app)     -> "Comma separated list of application test suites to run. Equivalent to `[{application, App}]`."; -help(cover)   -> "Generate cover data. Defaults to false."; -help(dir)     -> "Comma separated list of dirs to load tests from. Equivalent to `[{dir, Dir}]`."; -help(file)    -> "Comma separated list of files to load tests from. Equivalent to `[{file, File}]`."; -help(module)  -> "Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`."; -help(verbose) -> "Verbose output. Defaults to false.". +     {verbose, $v, "verbose", boolean, help(verbose)}, +     {name, undefined, "name", atom, help(name)}, +     {sname, undefined, "sname", atom, help(sname)}, +     {setcookie, undefined, "setcookie", atom, help(setcookie)}]. + +help(app)       -> "Comma separated list of application test suites to run. Equivalent to `[{application, App}]`."; +help(cover)     -> "Generate cover data. Defaults to false."; +help(dir)       -> "Comma separated list of dirs to load tests from. Equivalent to `[{dir, Dir}]`."; +help(file)      -> "Comma separated list of files to load tests from. Equivalent to `[{file, File}]`."; +help(module)    -> "Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`."; +help(verbose)   -> "Verbose output. Defaults to false."; +help(name)      -> "Gives a long name to the node"; +help(sname)     -> "Gives a short name to the node"; +help(setcookie) -> "Sets the cookie if the node is distributed". diff --git a/src/rebar_prv_packages.erl b/src/rebar_prv_packages.erl index 998320c..7217ab8 100644 --- a/src/rebar_prv_packages.erl +++ b/src/rebar_prv_packages.erl @@ -28,22 +28,19 @@ init(State) ->  -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.  do(State) ->      rebar_packages:packages(State), -    print_packages(), +    case rebar_state:command_args(State) of +        [Name] -> +            print_packages(get_packages(iolist_to_binary(Name))); +        _ -> +            print_packages(sort_packages()) +    end,      {ok, State}.  -spec format_error(any()) -> iolist().  format_error(load_registry_fail) ->      "Failed to load package regsitry. Try running 'rebar3 update' to fix". -print_packages() -> -    SortedPkgs = ets:foldl(fun({package_index_version, _}, Acc) -> -                                   Acc; -                              ({Pkg, Vsns}, Acc) -> -                                   orddict:store(Pkg, Vsns, Acc); -                              (_, Acc) -> -                                   Acc -                           end, orddict:new(), ?PACKAGE_TABLE), - +print_packages(Pkgs) ->      orddict:map(fun(Name, Vsns) ->                          SortedVsns = lists:sort(fun(A, B) ->                                                          ec_semver:lte(ec_semver:parse(A) @@ -51,7 +48,20 @@ print_packages() ->                                                  end, Vsns),                          VsnStr = join(SortedVsns, <<", ">>),                          ?CONSOLE("~s:~n    Versions: ~s~n", [Name, VsnStr]) -                end, SortedPkgs). +                end, Pkgs). + +sort_packages() -> +    ets:foldl(fun({package_index_version, _}, Acc) -> +                      Acc; +                 ({Pkg, Vsns}, Acc) -> +                      orddict:store(Pkg, Vsns, Acc); +                 (_, Acc) -> +                      Acc +              end, orddict:new(), ?PACKAGE_TABLE). + +get_packages(Name) -> +    ets:lookup(?PACKAGE_TABLE, Name). +  -spec join([binary()], binary()) -> binary().  join([Bin], _Sep) -> diff --git a/src/rebar_prv_path.erl b/src/rebar_prv_path.erl index 37c9834..4e88496 100644 --- a/src/rebar_prv_path.erl +++ b/src/rebar_prv_path.erl @@ -95,10 +95,14 @@ print_paths_if_exist(Paths, State) ->  project_deps(State) ->      Profiles = rebar_state:current_profiles(State), -    List = lists:foldl(fun(Profile, Acc) -> rebar_state:get(State, {deps, Profile}, []) ++ Acc end, [], Profiles), -    Deps = [normalize(Name) || {Name, _} <- List], +    DepList = lists:foldl(fun(Profile, Acc) -> rebar_state:get(State, {deps, Profile}, []) ++ Acc end, [], Profiles), +    LockList = lists:foldl(fun(Profile, Acc) -> rebar_state:get(State, {locks, Profile}, []) ++ Acc end, [], Profiles), +    Deps = [normalize(name(Dep)) || Dep <- DepList++LockList],      lists:usort(Deps). +name(App) when is_tuple(App) -> element(1, App); +name(Name) when is_binary(Name); is_list(Name); is_atom(Name) -> Name. +  normalize(AppName) when is_list(AppName) -> AppName;  normalize(AppName) when is_atom(AppName) -> atom_to_list(AppName);  normalize(AppName) when is_binary(AppName) -> binary_to_list(AppName). diff --git a/src/rebar_prv_report.erl b/src/rebar_prv_report.erl index 587fad7..d6c8b60 100644 --- a/src/rebar_prv_report.erl +++ b/src/rebar_prv_report.erl @@ -13,7 +13,7 @@  -define(PROVIDER, report).  -define(DEPS, []). --define(ISSUES_URL, "https://github.com/rebar/rebar3/issues"). +-define(ISSUES_URL, "https://github.com/erlang/rebar3/issues").  %% ===================================================================  %% Public API diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index 430d1e8..0ede495 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -133,11 +133,25 @@ kill_old_user() ->      %% fully die      [P] = [P || P <- element(2,process_info(whereis(user), links)), is_port(P)],      user ! {'EXIT', P, normal}, % pretend the port died, then the port can die! +    exit(P, kill), +    wait_for_port_death(1000, P),      OldUser. +wait_for_port_death(N, _) when N < 0 -> +    %% This risks displaying a warning! +    whatever; +wait_for_port_death(N, P) -> +    case erlang:port_info(P) of +        undefined -> +            ok; +        _ -> +            timer:sleep(10), +            wait_for_port_death(N-10, P) +    end. +  setup_new_shell() -> -    %% terminate the current user supervision structure -    ok = supervisor:terminate_child(kernel_sup, user), +    %% terminate the current user supervision structure, if any +    _ = supervisor:terminate_child(kernel_sup, user),      %% start a new shell (this also starts a new user under the correct group)      _ = user_drv:start(),      %% wait until user_drv and user have been registered (max 3 seconds) @@ -178,7 +192,9 @@ rewrite_leaders(OldUser, NewUser) ->          %% disable the simple error_logger (which may have been added multiple          %% times). removes at most the error_logger added by init and the          %% error_logger added by the tty handler -        remove_error_handler(3) +        remove_error_handler(3), +        %% reset the tty handler once more for remote shells +        error_logger:swap_handler(tty)      catch          E:R -> % may fail with custom loggers              ?DEBUG("Logger changes failed for ~p:~p (~p)", [E,R,erlang:get_stacktrace()]), @@ -255,33 +271,8 @@ simulate_proc_lib() ->      put('$initial_call', {rebar_agent, init, 1}).  setup_name(State) -> -    {Opts, _} = rebar_state:command_parsed_args(State), -    case {proplists:get_value(name, Opts), proplists:get_value(sname, Opts)} of -        {undefined, undefined} -> -            ok; -        {Name, undefined} -> -            check_epmd(net_kernel:start([Name, longnames])), -            setup_cookie(Opts); -        {undefined, SName} -> -            check_epmd(net_kernel:start([SName, shortnames])), -            setup_cookie(Opts); -        {_, _} -> -            ?ABORT("Cannot have both short and long node names defined", []) -    end. - -check_epmd({error,{{shutdown, {_,net_kernel,{'EXIT',nodistribution}}},_}}) -> -    ?ERROR("Erlang Distribution failed, falling back to nonode@nohost. " -           "Verify that epmd is running and try again.",[]); -check_epmd(_) -> -    ok. - -setup_cookie(Opts) -> -    case {node(), proplists:get_value(setcookie, Opts, nocookie)} of -        {'nonode@nohost', _} -> nocookie; -        {_, nocookie} -> nocookie; -        {Node, Name} -> erlang:set_cookie(Node, Name) -    end. - +    {Long, Short, Opts} = rebar_dist_utils:find_options(State), +    rebar_dist_utils:either(Long, Short, Opts).  find_apps_to_boot(State) ->      %% Try the shell_apps option @@ -339,7 +330,8 @@ reread_config(State) ->          ConfigList ->              try                  [application:set_env(Application, Key, Val) -                  || {Application, Items} <- ConfigList, +                  || Config <- ConfigList, +                     {Application, Items} <- Config,                       {Key, Val} <- Items]              catch _:_ ->                  ?ERROR("The configuration file submitted could not be read " @@ -402,7 +394,7 @@ add_test_paths(State) ->      ok.  % First try the --config flag, then try the relx sys_config --spec find_config(rebar_state:t()) -> [tuple()] | no_config. +-spec find_config(rebar_state:t()) -> [[tuple()]] | no_config.  find_config(State) ->      case first_value([fun find_config_option/1,                        fun find_config_rebar/1, @@ -450,11 +442,17 @@ find_config_relx(State) ->      debug_get_value(sys_config, rebar_state:get(State, relx, []), no_value,                      "Found config from relx."). --spec consult_config(rebar_state:t(), string()) -> [tuple()]. +-spec consult_config(rebar_state:t(), string()) -> [[tuple()]].  consult_config(State, Filename) ->      Fullpath = filename:join(rebar_dir:root_dir(State), Filename),      ?DEBUG("Loading configuration from ~p", [Fullpath]), -    case rebar_file_utils:try_consult(Fullpath) of +    Config = case rebar_file_utils:try_consult(Fullpath) of          [T] -> T;          [] -> [] -    end. +    end, +    SubConfigs = [consult_config(State, Entry ++ ".config") || +                  Entry <- Config, is_list(Entry) +                 ], + +    [Config | lists:merge(SubConfigs)]. + diff --git a/test/rebar_dist_utils_SUITE.erl b/test/rebar_dist_utils_SUITE.erl new file mode 100644 index 0000000..e190b94 --- /dev/null +++ b/test/rebar_dist_utils_SUITE.erl @@ -0,0 +1,74 @@ +%%% This suite currently only tests for options parsing since we do +%%% not know if epmd will be running to actually boot nodes. +-module(rebar_dist_utils_SUITE). +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-compile(export_all). + +all() -> [from_config, from_cli, overlap, from_config_profile]. + +init_per_testcase(_, Config0) -> +    Config = rebar_test_utils:init_rebar_state(Config0), +    AppDir = ?config(apps, Config), +    Name = rebar_test_utils:create_random_name("app_"), +    Vsn = rebar_test_utils:create_random_vsn(), +    rebar_test_utils:create_app(filename:join([AppDir,"apps",Name]), Name, Vsn, [kernel, stdlib]), +    Config. + + +end_per_testcase(_, _) -> +    ok. + +from_config(Config) -> +    ShortConfig = [{dist_node, [{sname, 'a@localhost'}, {setcookie, abc}]}], +    LongConfig = [{dist_node, [{name, 'a@localhost.x'}, {setcookie, abc}]}], +    BothConfig = [{dist_node, [{sname, 'a@localhost'}, {name, 'a@localhost.x'}, {setcookie,abc}]}], +    NoConfig = [], +    CookieConfig = [{dist_node, [{setcookie, def}]}], +    NoCookie = [{dist_node, [{sname, 'a@localhost'}]}], +    {ok, State0} = rebar_test_utils:run_and_check(Config, ShortConfig, ["version"], return), +    {undefined, 'a@localhost', [{setcookie, abc}]} = rebar_dist_utils:find_options(State0), +    {ok, State1} = rebar_test_utils:run_and_check(Config, LongConfig, ["version"], return), +    {'a@localhost.x', undefined, [{setcookie, abc}]} = rebar_dist_utils:find_options(State1), +    %% only support the first name found, side-effect of wanting profile support +    {ok, State2} = rebar_test_utils:run_and_check(Config, BothConfig, ["version"], return), +    {undefined, 'a@localhost', [{setcookie, abc}]} = rebar_dist_utils:find_options(State2), +    {ok, State3} = rebar_test_utils:run_and_check(Config, NoConfig, ["version"], return), +    {undefined, undefined, []} = rebar_dist_utils:find_options(State3), +    {ok, State4} = rebar_test_utils:run_and_check(Config, CookieConfig, ["version"], return), +    {undefined, undefined, [{setcookie, def}]} = rebar_dist_utils:find_options(State4), +    {ok, State5} = rebar_test_utils:run_and_check(Config, NoCookie, ["version"], return), +    {undefined, 'a@localhost', []} = rebar_dist_utils:find_options(State5), +    ok. + +from_cli(Config) -> +    {ok, State0} = rebar_test_utils:run_and_check(Config, [], ["version"], return), +    {undefined, undefined, []} = rebar_dist_utils:find_options(State0), +    State1 = rebar_state:command_parsed_args(State0, {[{sname, 'a@localhost'}, {setcookie,abc}], []}), +    {undefined, 'a@localhost', [{setcookie, abc}]} = rebar_dist_utils:find_options(State1), +    State2 = rebar_state:command_parsed_args(State0, {[{name, 'a@localhost.x'}, {setcookie,abc}], []}), +    {'a@localhost.x', undefined, [{setcookie, abc}]} = rebar_dist_utils:find_options(State2), +    State3 = rebar_state:command_parsed_args(State0, {[{sname, 'a@localhost'}, {name, 'a@localhost.x'}, {setcookie,abc}], []}), +    {'a@localhost.x', 'a@localhost', [{setcookie, abc}]} = rebar_dist_utils:find_options(State3), +    State4 = rebar_state:command_parsed_args(State0, {[{setcookie,def}], []}), +    {undefined, undefined, [{setcookie, def}]} = rebar_dist_utils:find_options(State4), +    State5 = rebar_state:command_parsed_args(State0, {[{sname, 'a@localhost'}], []}), +    {undefined, 'a@localhost', []} = rebar_dist_utils:find_options(State5), +    ok. + +overlap(Config) -> +    %% Make sure that CLI config takes over rebar config without clash for names, though +    %% cookies can pass through +    RebarConfig = [{dist_node, [{sname, 'a@localhost'}, {setcookie, abc}]}], +    {ok, State0} = rebar_test_utils:run_and_check(Config, RebarConfig, ["version"], return), +    State1 = rebar_state:command_parsed_args(State0, {[{name, 'b@localhost.x'}], []}), +    {'b@localhost.x', undefined, [{setcookie, abc}]} = rebar_dist_utils:find_options(State1), +    ok. + +from_config_profile(Config) -> +    %% running as a profile does not create name clashes +    RebarConfig = [{dist_node, [{sname, 'a@localhost'}, {setcookie, abc}]}, +                   {profiles, [ {fake, [{dist_node, [{name, 'a@localhost.x'}]}]} ]}], +    {ok, State0} = rebar_test_utils:run_and_check(Config, RebarConfig, ["as","fake","version"], return), +    {'a@localhost.x', undefined, [{setcookie, abc}]} = rebar_dist_utils:find_options(State0), +    ok. | 
