diff --git a/include/mad.hrl b/include/mad.hrl index b5f9335..c6e1aba 100644 --- a/include/mad.hrl +++ b/include/mad.hrl @@ -1 +1,5 @@ +<<<<<<< HEAD -define(VERSION,"77005f"). +======= +-define(VERSION,"2f1efa"). +>>>>>>> 0cd2544... Add rebar post/pre hooking engine diff --git a/src/compile/mad_hooks.erl b/src/compile/mad_hooks.erl deleted file mode 100644 index 00985bb..0000000 --- a/src/compile/mad_hooks.erl +++ /dev/null @@ -1,85 +0,0 @@ --module(mad_hooks). --compile(export_all). - -%% --- hooks -- %% -apply_hooks(Mode, Config, Cwd, Name) -> - Hooks = mad_utils:get_value(Mode, Config, []), - Default = filename:join(Cwd, "deps"), - DepsDir = filename:join(Cwd, mad_utils:get_value(deps_dir, Config, [Default])), - DepPath = filename:join(DepsDir, Name), - Env = [{"REBAR_DEPS_DIR",DepsDir}], - lists:foreach(fun apply_hook/1, [{Env, DepPath, H}||H<-Hooks]). - -apply_hook({Env, DepPath, {Arch, Command, Hook}}) -> - case is_arch(Arch) of - true -> - apply_hook({Env, DepPath, {Command, Hook}}); - false -> - ok - end; -apply_hook({Env, DepPath, {Command, Hook}}) -> - {_,Status,X} = sh:run("/bin/sh", ["-c", Hook], binary, DepPath, Env), - case Status == 0 of - true -> skip; - false -> mad:info("Shell Error ~p: ~s~n\r", - [Command, binary_to_list(X)]), exit({error,X}) - end. - -wordsize() -> - try erlang:system_info({wordsize, external}) of - Val -> - integer_to_list(8 * Val) - catch - error:badarg -> - integer_to_list(8 * erlang:system_info(wordsize)) - end. - -is_arch(ArchRegex) -> - case re:run(get_arch(), ArchRegex, [{capture, none}]) of - match -> - true; - nomatch -> - false - end. - -get_arch() -> - Words = wordsize(), - otp_release() ++ "-" - ++ erlang:system_info(system_architecture) ++ "-" ++ Words. - - -otp_release() -> - otp_release1(erlang:system_info(otp_release)). - -%% If OTP <= R16, otp_release is already what we want. -otp_release1([$R,N|_]=Rel) when is_integer(N) -> - Rel; -%% If OTP >= 17.x, erlang:system_info(otp_release) returns just the -%% major version number, we have to read the full version from -%% a file. See http://www.erlang.org/doc/system_principles/versions.html -%% Read vsn string from the 'OTP_VERSION' file and return as list without -%% the "\n". -otp_release1(Rel) -> - File = filename:join([code:root_dir(), "releases", Rel, "OTP_VERSION"]), - {ok, Vsn} = file:read_file(File), - - %% It's fine to rely on the binary module here because we can - %% be sure that it's available when the otp_release string does - %% not begin with $R. - Size = byte_size(Vsn), - %% The shortest vsn string consists of at least two digits - %% followed by "\n". Therefore, it's safe to assume Size >= 3. - case binary:part(Vsn, {Size, -3}) of - <<"**\n">> -> - %% The OTP documentation mentions that a system patched - %% using the otp_patch_apply tool available to licensed - %% customers will leave a '**' suffix in the version as a - %% flag saying the system consists of application versions - %% from multiple OTP versions. We ignore this flag and - %% drop the suffix, given for all intents and purposes, we - %% cannot obtain relevant information from it as far as - %% tooling is concerned. - binary:bin_to_list(Vsn, {0, Size - 3}); - _ -> - binary:bin_to_list(Vsn, {0, Size - 1}) - end. diff --git a/src/hooks/mad_hooks.erl b/src/hooks/mad_hooks.erl new file mode 100644 index 0000000..5312d32 --- /dev/null +++ b/src/hooks/mad_hooks.erl @@ -0,0 +1,125 @@ +-module(mad_hooks). +-export([run_hooks/2]). + +-spec run_hooks(pre|post, atom()) -> any(). +run_hooks(Type, Command) -> + {_Cwd, _ConfigFile, Config} = mad_utils:configs(), + Dir = mad_utils:cwd(), + run_hooks(Dir, Type, Command, Config). + +run_hooks(Dir, pre, Command, Config) -> + run_hooks(Dir, pre_hooks, Command, Config); +run_hooks(Dir, post, Command, Config) -> + run_hooks(Dir, post_hooks, Command, Config); +run_hooks(Dir, Type, Command, Config) -> + MaybeHooks = mad_utils:get_value(Type, Config, []), + apply_hooks(Dir, Command, Config, MaybeHooks). + +apply_hooks(_, _, _, []) -> + done; +apply_hooks(Dir, Command, Config, Hooks) -> + Env = create_env(Config), + lists:foreach(fun({_, C, _} = Hook) when C =:= Command -> + apply_hook(Dir, Env, Hook); + ({C, _} = Hook) when C =:= Command -> + apply_hook(Dir, Env, Hook); + (_) -> + continue + end, Hooks). + +apply_hook(Dir, Env, {Arch, Command, Hook}) -> + case is_arch(Arch) of + true -> + apply_hook(Dir, Env, {Command, Hook}); + false -> + ok + end; +apply_hook(Dir, Env, {Command, Hook}) -> + sh(Command, Hook, Dir, Env). + +%% Can be expanded +create_env(_Config) -> []. + +%% SOURCE: +%% https://github.com/erlang/rebar3/blob/master/src/rebar_utils.erl +is_arch(ArchRegex) -> + case re:run(get_arch(), ArchRegex, [{capture, none}]) of + match -> + true; + nomatch -> + false + end. + +get_arch() -> + Words = wordsize(), + otp_release() ++ "-" + ++ erlang:system_info(system_architecture) ++ "-" ++ Words. + +wordsize() -> + try erlang:system_info({wordsize, external}) of + Val -> + integer_to_list(8 * Val) + catch + error:badarg -> + integer_to_list(8 * erlang:system_info(wordsize)) + end. + +otp_release() -> + otp_release1(erlang:system_info(otp_release)). + +%% If OTP <= R16, otp_release is already what we want. +otp_release1([$R,N|_]=Rel) when is_integer(N) -> + Rel; +%% If OTP >= 17.x, erlang:system_info(otp_release) returns just the +%% major version number, we have to read the full version from +%% a file. See http://www.erlang.org/doc/system_principles/versions.html +%% Read vsn string from the 'OTP_VERSION' file and return as list without +%% the "\n". +otp_release1(Rel) -> + File = filename:join([code:root_dir(), "releases", Rel, "OTP_VERSION"]), + case file:read_file(File) of + {error, _} -> + Rel; + {ok, Vsn} -> + %% It's fine to rely on the binary module here because we can + %% be sure that it's available when the otp_release string does + %% not begin with $R. + Size = byte_size(Vsn), + %% The shortest vsn string consists of at least two digits + %% followed by "\n". Therefore, it's safe to assume Size >= 3. + case binary:part(Vsn, {Size, -3}) of + <<"**\n">> -> + %% The OTP documentation mentions that a system patched + %% using the otp_patch_apply tool available to licensed + %% customers will leave a '**' suffix in the version as a + %% flag saying the system consists of application versions + %% from multiple OTP versions. We ignore this flag and + %% drop the suffix, given for all intents and purposes, we + %% cannot obtain relevant information from it as far as + %% tooling is concerned. + binary:bin_to_list(Vsn, {0, Size - 3}); + _ -> + binary:bin_to_list(Vsn, {0, Size - 1}) + end + end. +%% END of SOURCE + +sh(Command, Hook, Dir, Env) -> + Port = erlang:open_port({spawn, Hook}, + [ + stream, + stderr_to_stdout, + binary, + exit_status, + {cd, Dir}, + {env, Env} + ] + ), + {done, Status, Out} = sh:sh_loop(Port, binary), + case Status of + 0 -> + mad:info("~s~n", [Out]); + _ -> + mad:info("Failed hook for ~p with ~s~n", [Command, Out]), + exit({error, Out}) + end. \ No newline at end of file diff --git a/src/mad.erl b/src/mad.erl index c8f90fe..7258ae2 100644 --- a/src/mad.erl +++ b/src/mad.erl @@ -12,12 +12,21 @@ main(Params) -> (X,{C,R}) -> {[X|C],R} end, {[],[]}, lists:map(fun atomize/1, Params)), - return(lists:any(fun({error,_}) -> true; - (_) -> false end, - lists:flatten( - lists:foldl( - fun ({Fun,Arg},[]) -> errors((profile()):Fun(Arg)); - ({_,_},Err) -> errors(Invalid), {return,Err} end, [], Valid)))). + return( + lists:any(fun({error,_}) -> true; (_) -> false end, + lists:flatten( + lists:foldl(fun({Fun,Arg},[]) -> + mad_hooks:run_hooks(pre, Fun), + Errors = errors((profile()):Fun(Arg)), + mad_hooks:run_hooks(post, Fun), + Errors; + ({_,_},Err) -> + errors(Invalid), {return,Err} + end, + [], Valid) + ) + ) + ). atomize("static") -> 'static'; atomize("deploy") -> 'deploy';