From 20f348b900ff682e416897758afc095970776d68 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Fri, 25 Aug 2023 20:56:24 +0100 Subject: [PATCH 1/3] Add command `rebar3 alias` `rebar3 alias` will list your rebar.config -defined aliases as e.g: static=eunit,dialyzer doc=compile,ex_doc ci=eunit,ct --verbose,cover just like zsh's alias would, for example --- apps/rebar/src/rebar_prv_alias.erl | 62 +++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/apps/rebar/src/rebar_prv_alias.erl b/apps/rebar/src/rebar_prv_alias.erl index c9a8943b0..43fe17d64 100644 --- a/apps/rebar/src/rebar_prv_alias.erl +++ b/apps/rebar/src/rebar_prv_alias.erl @@ -6,21 +6,62 @@ %%% years of stability. Only some error checks were added -module(rebar_prv_alias). --export([init/1]). +-behaviour(provider). + +-export([init/1, do/1, format_error/1]). -include("rebar.hrl"). +-define(PROVIDER, alias). +-define(CREATED_ALIASES_KEY, '_created_aliases'). + %% =================================================================== %% Public API %% =================================================================== -spec init(rebar_state:t()) -> {ok, rebar_state:t()}. init(State) -> Aliases = rebar_state:get(State, alias, []), - lists:foldl(fun({Alias, Cmds}, {ok, StateAcc}) -> - case validate_provider(Alias, Cmds, State) of - true -> init_alias(Alias, Cmds, StateAcc); - false -> {ok, State} - end - end, {ok, State}, Aliases). + {StateWithAliases, AliasesDefs} + = lists:foldl( + fun({Alias, Cmds}, {StateAcc, AliasesDefsAcc} = Acc) -> + case validate_provider(Alias, Cmds, State) of + true -> + StateWithAlias = init_alias(Alias, Cmds, StateAcc), + AliasesDefsWithAlias = [{Alias, Cmds} | AliasesDefsAcc], + {StateWithAlias, AliasesDefsWithAlias}; + false -> + Acc + end + end, + {State, []}, + Aliases + ), + AliasProvider = providers:create([{name, ?PROVIDER}, + {module, ?MODULE}, + {bare, true}, + {deps, []}, + {example, "rebar3 alias"}, + {short_desc, "List aliases' definitions."}, + {desc, "List aliases' definitions."}, + {opts, []}]), + StateWithProvider = rebar_state:add_provider(StateWithAliases, AliasProvider), + StateWithAliasesDefs = rebar_state:set(StateWithProvider, ?CREATED_ALIASES_KEY, AliasesDefs), + {ok, StateWithAliasesDefs}. + +-spec do(rebar_state:t()) -> {ok, rebar_state:t()}. +do(State) -> + lists:foreach( + fun ({Alias, Cmds}) -> + AliasStr = atom_to_list(Alias), + CmdsStr = cmds_string(Cmds), + ?CONSOLE("~ts=~ts", [AliasStr, CmdsStr]) + end, + rebar_state:get(State, ?CREATED_ALIASES_KEY, []) + ), + {ok, State}. + +-spec format_error(any()) -> iolist(). +format_error(Reason) -> + io_lib:format("~p", [Reason]). -dialyzer([{no_opaque, init_alias/3}, {no_return, init_alias/3}]). % warnings relate to use of opaque structures in :forms init_alias(Alias, Cmds, State) -> @@ -43,7 +84,7 @@ init_alias(Alias, Cmds, State) -> {short_desc, desc(Cmds)}, {desc, desc(Cmds)} ]), - {ok, rebar_state:add_provider(State, Provider)}. + rebar_state:add_provider(State, Provider). validate_provider(Alias, Cmds, State) -> %% This would be caught and prevented anyway, but the warning @@ -73,7 +114,10 @@ example(Alias) -> -dialyzer({no_unused, desc/1}). % required since we suppress warnings for init_alias/3 desc(Cmds) -> "Equivalent to running: rebar3 do " - ++ rebar_string:join(lists:map(fun to_desc/1, Cmds), ","). + ++ cmds_string(Cmds). + +cmds_string(Cmds) -> + rebar_string:join(lists:map(fun to_desc/1, Cmds), ","). to_desc({Cmd, Args}) when is_list(Args) -> atom_to_list(Cmd) ++ " " ++ Args; From 975316a380e57c5a68a4a9347ca369d66353236c Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sat, 26 Aug 2023 13:27:10 +0100 Subject: [PATCH 2/3] Act on review comment: reduce chances of collision --- apps/rebar/src/rebar_prv_alias.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/rebar/src/rebar_prv_alias.erl b/apps/rebar/src/rebar_prv_alias.erl index 43fe17d64..41f71ac30 100644 --- a/apps/rebar/src/rebar_prv_alias.erl +++ b/apps/rebar/src/rebar_prv_alias.erl @@ -12,7 +12,7 @@ -include("rebar.hrl"). -define(PROVIDER, alias). --define(CREATED_ALIASES_KEY, '_created_aliases'). +-define(CREATED_ALIASES_KEY, '_rebar_prv_alias_created_aliases'). %% =================================================================== %% Public API From 6c20c5a189a8840c14ecec627701836d41930f77 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Sat, 26 Aug 2023 14:56:27 +0100 Subject: [PATCH 3/3] Test command `rebar3 alias`'s implementation --- apps/rebar/test/rebar_alias_SUITE.erl | 43 ++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/apps/rebar/test/rebar_alias_SUITE.erl b/apps/rebar/test/rebar_alias_SUITE.erl index 4356496ad..ef7bc32b4 100644 --- a/apps/rebar/test/rebar_alias_SUITE.erl +++ b/apps/rebar/test/rebar_alias_SUITE.erl @@ -14,7 +14,7 @@ end_per_testcase(_, _Config) -> ok. all() -> [command, args, many, override_default, no_circular, release, - check_namespaces, create_lib]. + check_namespaces, create_lib, command_console]. command() -> [{doc, "Runs multiple regular commands as one alias"}]. @@ -163,3 +163,44 @@ create_lib(Config) -> "../../../../shouldexist/src/shouldexist.app.src"), ?assert(filelib:is_file(AppFile)), ok. + +command_console() -> + [{doc, "Test console output as per `rebar3 alias`"}]. +command_console(Config) -> + State = ?config(state, Config), + RebarConfig + = [{alias, [ + {test, [compile, {unlock,"-a"}]}, + {test, [{eunit,"-c"}, cover]}, + {nolock, [compile, {unlock,"-a"}]}, + {compile1, [help]}, + {test1, [help, {test,"-a"}, compile]}, + {the_rel1, [clean, {release, "-n the_release"}]}, + {the_rel2, [clean, {release, "--relname=the_release"}]}, + {test, [{eunit,"-c"}, {plugins, list}]}, + {test, [compile, {do, "new lib shouldexist"}]} + ]}], + AppDir = ?config(apps, Config), + + %% validate output as =..., as this format is considered part of the interface, + %% for consumption; order is not important though + ct:capture_start(), + rebar3:run(rebar_state:new(State, RebarConfig, AppDir), ["alias"]), + ct:capture_stop(), + AllCaptured = ct:capture_get(), + + Aliases = proplists:get_keys(proplists:get_value(alias, RebarConfig)), + [] = lists:foldl( + fun (Captured, AliasesAcc) -> + Match = re:run(Captured, "^([^=]+)=.*", [{capture, all_but_first}]), + case Match of + no_match -> + AliasesAcc; + {match, [{Start, End}]} -> + CapturedPref = string:sub_string(Captured, Start+1, End), + lists:delete(list_to_existing_atom(CapturedPref), AliasesAcc) + end + end, + Aliases, + AllCaptured + ).