mad_hooks.erl 4.1 KB

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