Browse Source

Add rebar post/pre hooking engine

Pavel Kozlovsky 8 years ago
parent
commit
0cd2544d72
3 changed files with 141 additions and 7 deletions
  1. 1 1
      include/mad.hrl
  2. 125 0
      src/hooks/mad_hooks.erl
  3. 15 6
      src/mad.erl

+ 1 - 1
include/mad.hrl

@@ -1 +1 @@
--define(VERSION,"ed64b0").
+-define(VERSION,"2f1efa").

+ 125 - 0
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.

+ 15 - 6
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';