mad_hooks.erl 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. -module(mad_hooks).
  2. -copyright('Pavel Kozlovsky').
  3. -export([run_hooks/2]).
  4. -spec run_hooks(pre|post, atom()) -> any().
  5. run_hooks(Type, Command) ->
  6. {_Cwd, _ConfigFile, Config} = mad_utils:configs(),
  7. Dir = mad_utils:cwd(),
  8. run_hooks(Dir, Type, Command, Config).
  9. run_hooks(Dir, pre, Command, Config) -> run_hooks(Dir, pre_hooks, Command, Config);
  10. run_hooks(Dir, post, Command, Config) -> run_hooks(Dir, post_hooks, Command, Config);
  11. run_hooks(Dir, Type, Command, Config) ->
  12. MaybeHooks = mad_utils:get_value(Type, Config, []),
  13. apply_hooks(Dir, Command, Config, MaybeHooks).
  14. apply_hooks(_, _, _, []) -> done;
  15. apply_hooks(Dir, Command, Config, Hooks) ->
  16. Env = create_env(Config),
  17. lists:foreach(fun({_, C, _} = Hook) when C =:= Command ->
  18. apply_hook(Dir, Env, Hook);
  19. ({C, _} = Hook) when C =:= Command ->
  20. apply_hook(Dir, Env, Hook);
  21. (_) ->
  22. continue
  23. end, Hooks).
  24. apply_hook(Dir, Env, {Arch, Command, Hook}) ->
  25. case is_arch(Arch) of
  26. true ->
  27. apply_hook(Dir, Env, {Command, Hook});
  28. false ->
  29. ok
  30. end;
  31. apply_hook(Dir, Env, {Command, Hook}) ->
  32. sh(Command, Hook, Dir, Env).
  33. %% Can be expanded
  34. create_env(_Config) -> [].
  35. %% SOURCE:
  36. %% https://github.com/erlang/rebar3/blob/master/src/rebar_utils.erl
  37. is_arch(ArchRegex) ->
  38. case re:run(get_arch(), ArchRegex, [{capture, none}]) of
  39. match ->
  40. true;
  41. nomatch ->
  42. false
  43. end.
  44. get_arch() ->
  45. Words = wordsize(),
  46. otp_release() ++ "-"
  47. ++ erlang:system_info(system_architecture) ++ "-" ++ Words.
  48. wordsize() ->
  49. try erlang:system_info({wordsize, external}) of
  50. Val ->
  51. integer_to_list(8 * Val)
  52. catch
  53. error:badarg ->
  54. integer_to_list(8 * erlang:system_info(wordsize))
  55. end.
  56. otp_release() ->
  57. otp_release1(erlang:system_info(otp_release)).
  58. %% If OTP <= R16, otp_release is already what we want.
  59. otp_release1([$R,N|_]=Rel) when is_integer(N) ->
  60. Rel;
  61. %% If OTP >= 17.x, erlang:system_info(otp_release) returns just the
  62. %% major version number, we have to read the full version from
  63. %% a file. See http://www.erlang.org/doc/system_principles/versions.html
  64. %% Read vsn string from the 'OTP_VERSION' file and return as list without
  65. %% the "\n".
  66. otp_release1(Rel) ->
  67. File = filename:join([code:root_dir(), "releases", Rel, "OTP_VERSION"]),
  68. case file:read_file(File) of
  69. {error, _} ->
  70. Rel;
  71. {ok, Vsn} ->
  72. %% It's fine to rely on the binary module here because we can
  73. %% be sure that it's available when the otp_release string does
  74. %% not begin with $R.
  75. Size = byte_size(Vsn),
  76. %% The shortest vsn string consists of at least two digits
  77. %% followed by "\n". Therefore, it's safe to assume Size >= 3.
  78. case binary:part(Vsn, {Size, -3}) of
  79. <<"**\n">> ->
  80. %% The OTP documentation mentions that a system patched
  81. %% using the otp_patch_apply tool available to licensed
  82. %% customers will leave a '**' suffix in the version as a
  83. %% flag saying the system consists of application versions
  84. %% from multiple OTP versions. We ignore this flag and
  85. %% drop the suffix, given for all intents and purposes, we
  86. %% cannot obtain relevant information from it as far as
  87. %% tooling is concerned.
  88. binary:bin_to_list(Vsn, {0, Size - 3});
  89. _ ->
  90. binary:bin_to_list(Vsn, {0, Size - 1})
  91. end
  92. end.
  93. %% END of SOURCE
  94. sh(Command, Hook, Dir, Env) ->
  95. Port = erlang:open_port({spawn, Hook},
  96. [
  97. stream,
  98. stderr_to_stdout,
  99. binary,
  100. exit_status,
  101. {cd, Dir},
  102. {env, Env}
  103. ]
  104. ),
  105. {done, Status, Out} = sh:sh_loop(Port, binary),
  106. case Status of
  107. 0 ->
  108. mad:info("~s~n", [Out]);
  109. _ ->
  110. mad:info("Failed hook for ~p with ~s~n", [Command, Out]),
  111. exit({error, Out})
  112. end.