You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

556 lines
24 KiB

support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 years ago
support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 years ago
support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 years ago
support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 years ago
support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 years ago
support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 years ago
  1. -module(rebar_test_utils).
  2. -include_lib("common_test/include/ct.hrl").
  3. -include_lib("eunit/include/eunit.hrl").
  4. -export([init_rebar_state/1, init_rebar_state/2, run_and_check/3, run_and_check/4, check_results/3]).
  5. -export([expand_deps/2, flat_deps/1, top_level_deps/1]).
  6. -export([create_app/4, create_plugin/4, create_eunit_app/4, create_empty_app/4,
  7. create_config/2, create_config/3, package_app/4]).
  8. -export([create_random_name/1, create_random_vsn/0, write_src_file/2,
  9. random_element/1]).
  10. %% Pick the right random module
  11. -ifdef(rand_only).
  12. -define(random, rand).
  13. -else.
  14. -define(random, random).
  15. -endif.
  16. %%%%%%%%%%%%%%
  17. %%% Public %%%
  18. %%%%%%%%%%%%%%
  19. %% @doc {@see init_rebar_state/2}
  20. init_rebar_state(Config) -> init_rebar_state(Config, "apps_dir1_").
  21. %% @doc Takes a common test config and a name (string) and sets up
  22. %% a basic OTP app directory with a pre-configured rebar state to
  23. %% run tests with.
  24. init_rebar_state(Config, Name) ->
  25. application:load(rebar),
  26. DataDir = ?config(priv_dir, Config),
  27. AppsDir = filename:join([DataDir, create_random_name(Name)]),
  28. CheckoutsDir = filename:join([AppsDir, "_checkouts"]),
  29. ok = ec_file:mkdir_p(AppsDir),
  30. ok = ec_file:mkdir_p(CheckoutsDir),
  31. Verbosity = rebar3:log_level(),
  32. rebar_log:init(command_line, Verbosity),
  33. GlobalDir = filename:join([DataDir, "cache"]),
  34. Repos = proplists:get_value(repos, Config, []),
  35. State = rebar_state:new([{base_dir, filename:join([AppsDir, "_build"])}
  36. ,{global_rebar_dir, GlobalDir}
  37. ,{hex, [{repos, [#{name => R} || R <- Repos]}]}
  38. ,{root_dir, AppsDir}]),
  39. [{apps, AppsDir}, {checkouts, CheckoutsDir}, {state, State} | Config].
  40. %% @doc Takes common test config, a rebar config ([] if empty), a command to
  41. %% run ("install_deps", "compile", etc.), and a list of expected applications
  42. %% and/or dependencies to be present, and verifies whether they are all in
  43. %% place.
  44. %%
  45. %% The expectation list takes elements of the form:
  46. %% - `{app, Name :: string()}': checks that the app is properly built.
  47. %% - `{dep, Name :: string()}': checks that the dependency has been fetched.
  48. %% Ignores the build status of the dependency.
  49. %% - `{dep, Name :: string(), Vsn :: string()}': checks that the dependency
  50. %% has been fetched, and that a given version has been chosen. Useful to
  51. %% test for conflict resolution. Also ignores the build status of the
  52. %% dependency.
  53. %%
  54. %% This function assumes `init_rebar_state/1-2' has run before, in order to
  55. %% fetch the `apps' and `state' values from the CT config.
  56. run_and_check(Config, RebarConfig, Command, Expect) ->
  57. %% Assumes init_rebar_state has run first
  58. AppDir = ?config(apps, Config),
  59. State = ?config(state, Config),
  60. try
  61. Res = rebar3:run(rebar_state:new(State, RebarConfig, AppDir), Command),
  62. case Expect of
  63. {error, Reason} ->
  64. ?assertEqual({error, Reason}, Res);
  65. {ok, Expected} ->
  66. {ok, _} = Res,
  67. check_results(AppDir, Expected, "*"),
  68. Res;
  69. {ok, Expected, ProfileRun} ->
  70. {ok, _} = Res,
  71. check_results(AppDir, Expected, ProfileRun),
  72. Res;
  73. return ->
  74. Res
  75. end
  76. catch
  77. rebar_abort when Expect =:= rebar_abort -> rebar_abort
  78. end.
  79. run_and_check(Config, Command, Expect) ->
  80. %% Assumes init_rebar_state has run first
  81. AppDir = ?config(apps, Config),
  82. {ok, Cwd} = file:get_cwd(),
  83. try
  84. ok = file:set_cwd(AppDir),
  85. Res = rebar3:run(Command),
  86. case Expect of
  87. {error, Reason} ->
  88. ?assertEqual({error, Reason}, Res);
  89. {ok, Expected} ->
  90. {ok, _} = Res,
  91. check_results(AppDir, Expected, "*"),
  92. Res;
  93. {ok, Expected, ProfileRun} ->
  94. {ok, _} = Res,
  95. check_results(AppDir, Expected, ProfileRun),
  96. Res;
  97. return ->
  98. Res
  99. end
  100. catch
  101. rebar_abort when Expect =:= rebar_abort -> rebar_abort
  102. after
  103. ok = file:set_cwd(Cwd)
  104. end.
  105. %% @doc Creates a dummy application including:
  106. %% - src/<file>.erl
  107. %% - src/<file>.app.src
  108. %% And returns a `rebar_app_info' object.
  109. create_app(AppDir, Name, Vsn, Deps) ->
  110. write_src_file(AppDir, Name ++ ".erl"),
  111. write_src_file(AppDir, "not_a_real_src_" ++ Name ++ ".erl"),
  112. write_app_src_file(AppDir, Name, Vsn, Deps),
  113. rebar_app_info:new(Name, Vsn, AppDir, Deps).
  114. %% @doc Creates a dummy plugin including:
  115. %% - src/<file>.erl
  116. %% - src/<file>.app.src
  117. %% And returns a `rebar_app_info' object.
  118. create_plugin(AppDir, Name, Vsn, Deps) ->
  119. write_plugin_file(AppDir, Name ++ ".erl"),
  120. write_src_file(AppDir, "not_a_real_src_" ++ Name ++ ".erl"),
  121. write_app_src_file(AppDir, Name, Vsn, Deps),
  122. rebar_app_info:new(Name, Vsn, AppDir, Deps).
  123. %% @doc Creates a dummy application including:
  124. %% - src/<file>.erl
  125. %% - src/<file>.app.src
  126. %% - test/<file>_tests.erl
  127. %% And returns a `rebar_app_info' object.
  128. create_eunit_app(AppDir, Name, Vsn, Deps) ->
  129. write_eunitized_src_file(AppDir, Name),
  130. write_eunit_suite_file(AppDir, Name),
  131. write_app_src_file(AppDir, Name, Vsn, Deps),
  132. rebar_app_info:new(Name, Vsn, AppDir, Deps).
  133. %% @doc Creates a dummy application including:
  134. %% - ebin/<file>.app
  135. %% And returns a `rebar_app_info' object.
  136. create_empty_app(AppDir, Name, Vsn, Deps) ->
  137. write_app_file(AppDir, Name, Vsn, Deps),
  138. rebar_app_info:new(Name, Vsn, AppDir, Deps).
  139. %% @doc Creates a rebar.config file. The function accepts a list of terms,
  140. %% each of which will be dumped as a consult file. For example, the list
  141. %% `[a, b, c]' will return the consult file `a. b. c.'.
  142. create_config(AppDir, Contents) ->
  143. ConfFilename = filename:join([AppDir, "rebar.config"]),
  144. create_config(AppDir, ConfFilename, Contents).
  145. create_config(_AppDir, ConfFilename, Contents) ->
  146. ok = filelib:ensure_dir(ConfFilename),
  147. Config = lists:flatten([io_lib:fwrite("~p.~n", [Term]) || Term <- Contents]),
  148. ok = ec_file:write(ConfFilename, Config),
  149. ConfFilename.
  150. %% @doc Util to create a random variation of a given name.
  151. create_random_name(Name) ->
  152. random_seed(),
  153. Name ++ erlang:integer_to_list(?random:uniform(1000000)).
  154. %% @doc Util to create a random variation of a given version.
  155. create_random_vsn() ->
  156. random_seed(),
  157. lists:flatten([erlang:integer_to_list(?random:uniform(100)),
  158. ".", erlang:integer_to_list(?random:uniform(100)),
  159. ".", erlang:integer_to_list(?random:uniform(100))]).
  160. -ifdef(rand_only).
  161. random_seed() ->
  162. %% the rand module self-seeds
  163. ok.
  164. -else.
  165. random_seed() ->
  166. <<A:32, B:32, C:32>> = crypto:rand_bytes(12),
  167. random:seed({A,B,C}).
  168. -endif.
  169. expand_deps(_, []) -> [];
  170. expand_deps(git_subdir, [{Name, Deps} | Rest]) ->
  171. Dep = {Name, {git_subdir, "https://example.org/user/"++Name++".git", {branch, "master"}, filename:join("appsubdir", Name)}},
  172. [{Dep, expand_deps(git_subdir, Deps)} | expand_deps(git_subdir, Rest)];
  173. expand_deps(git_subdir, [{Name, Vsn, Deps} | Rest]) ->
  174. Dep = {Name, Vsn, {git_subdir, "https://example.org/user/"++Name++".git", {tag, Vsn}, filename:join("appsubdir", Name)}},
  175. [{Dep, expand_deps(git_subdir, Deps)} | expand_deps(git_subdir, Rest)];
  176. expand_deps(git, [{Name, Deps} | Rest]) ->
  177. Dep = {Name, ".*", {git, "https://example.org/user/"++Name++".git", "master"}},
  178. [{Dep, expand_deps(git, Deps)} | expand_deps(git, Rest)];
  179. expand_deps(git, [{Name, Vsn, Deps} | Rest]) ->
  180. Dep = {Name, Vsn, {git, "https://example.org/user/"++Name++".git", {tag, Vsn}}},
  181. [{Dep, expand_deps(git, Deps)} | expand_deps(git, Rest)];
  182. expand_deps(pkg, [{Name, Deps} | Rest]) ->
  183. Dep = {pkg, Name, "0.0.0", undefined, undefined},
  184. [{Dep, expand_deps(pkg, Deps)} | expand_deps(pkg, Rest)];
  185. expand_deps(pkg, [{Name, Vsn, Deps} | Rest]) ->
  186. Dep = {pkg, Name, Vsn, undefined, undefined},
  187. [{Dep, expand_deps(pkg, Deps)} | expand_deps(pkg, Rest)];
  188. expand_deps(mixed, [{Name, Deps} | Rest]) ->
  189. Dep = if hd(Name) >= $a, hd(Name) =< $z ->
  190. {pkg, rebar_string:uppercase(Name), "0.0.0", undefined, undefined}
  191. ; hd(Name) >= $A, hd(Name) =< $Z ->
  192. {Name, ".*", {git, "https://example.org/user/"++Name++".git", "master"}}
  193. end,
  194. [{Dep, expand_deps(mixed, Deps)} | expand_deps(mixed, Rest)];
  195. expand_deps(mixed, [{Name, Vsn, Deps} | Rest]) ->
  196. Dep = if hd(Name) >= $a, hd(Name) =< $z ->
  197. {pkg, rebar_string:uppercase(Name), Vsn, undefined, undefined}
  198. ; hd(Name) >= $A, hd(Name) =< $Z ->
  199. {Name, Vsn, {git, "https://example.org/user/"++Name++".git", {tag, Vsn}}}
  200. end,
  201. [{Dep, expand_deps(mixed, Deps)} | expand_deps(mixed, Rest)].
  202. %% Source deps can depend on both source and package dependencies;
  203. %% package deps can only depend on package deps.
  204. %% For things to work we have to go down the dep tree and find all
  205. %% lineages of pkg deps and return them, whereas the source deps
  206. %% can be left as is.
  207. flat_deps(Deps) -> flat_deps(Deps, [], []).
  208. flat_deps([], Src, Pkg) -> {Src, Pkg};
  209. flat_deps([{{pkg, Name, Vsn, undefined, undefined}, PkgDeps} | Rest], Src, Pkg) ->
  210. Current = {{iolist_to_binary(Name), iolist_to_binary(Vsn)},
  211. top_level_deps(PkgDeps)},
  212. {[], FlatPkgDeps} = flat_deps(PkgDeps),
  213. flat_deps(Rest,
  214. Src,
  215. Pkg ++ [Current | FlatPkgDeps]);
  216. flat_deps([{{Name,_Vsn,Ref}, Deps} | Rest], Src, Pkg) ->
  217. Current = {{Name,vsn_from_ref(Ref)}, top_level_deps(Deps)},
  218. {FlatDeps, FlatPkgDeps} = flat_deps(Deps),
  219. flat_deps(Rest,
  220. Src ++ [Current | FlatDeps],
  221. Pkg ++ FlatPkgDeps);
  222. flat_deps([{{Name,Ref}, Deps} | Rest], Src, Pkg) ->
  223. Current = {{Name,vsn_from_ref(Ref)}, top_level_deps(Deps)},
  224. {FlatDeps, FlatPkgDeps} = flat_deps(Deps),
  225. flat_deps(Rest,
  226. Src ++ [Current | FlatDeps],
  227. Pkg ++ FlatPkgDeps).
  228. vsn_from_ref({git, _, {_, Vsn}}) -> Vsn;
  229. vsn_from_ref({git, _, Vsn}) -> Vsn;
  230. vsn_from_ref({git_subdir, _, {_, Vsn}, _}) -> Vsn;
  231. vsn_from_ref({git_subdir, _, Vsn, _}) -> Vsn.
  232. top_level_deps([]) -> [];
  233. top_level_deps([{{pkg, Name, Vsn, undefined, undefined}, _} | Deps]) ->
  234. [{list_to_atom(Name), Vsn} | top_level_deps(Deps)];
  235. top_level_deps([{{Name, Vsn, Ref}, _} | Deps]) ->
  236. [{list_to_atom(Name), Vsn, Ref} | top_level_deps(Deps)].
  237. %%%%%%%%%%%%%%%
  238. %%% Helpers %%%
  239. %%%%%%%%%%%%%%%
  240. check_results(AppDir, Expected, ProfileRun) ->
  241. BuildDirs = filelib:wildcard(filename:join([AppDir, "_build", ProfileRun, "lib", "*"])),
  242. BuildSubDirs = [D || D <- filelib:wildcard(filename:join([AppDir, "_build", ProfileRun, "lib", "*", "*", "*"])),
  243. filelib:is_dir(D)],
  244. PluginDirs = filelib:wildcard(filename:join([AppDir, "_build", ProfileRun, "plugins", "*"])),
  245. GlobalPluginDirs = filelib:wildcard(filename:join([AppDir, "global", "plugins", "*"])),
  246. CheckoutsDirs = filelib:wildcard(filename:join([AppDir, "_build", ProfileRun, "checkouts", "*"])),
  247. LockFile = filename:join([AppDir, "rebar.lock"]),
  248. Locks = lists:flatten(rebar_config:consult_lock_file(LockFile)),
  249. InvalidApps = rebar_app_discover:find_apps(BuildDirs, invalid),
  250. ValidApps = rebar_app_discover:find_apps(BuildDirs, valid),
  251. InvalidDepsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- InvalidApps],
  252. ValidDepsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- ValidApps],
  253. Deps = rebar_app_discover:find_apps(BuildDirs, all),
  254. SubDeps = rebar_app_discover:find_apps(BuildSubDirs, all),
  255. DepsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- Deps],
  256. SubDirDepsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- SubDeps],
  257. Checkouts = rebar_app_discover:find_apps(CheckoutsDirs, all),
  258. CheckoutsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- Checkouts],
  259. Plugins = rebar_app_discover:find_apps(PluginDirs, all),
  260. PluginsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- Plugins],
  261. GlobalPlugins = rebar_app_discover:find_apps(GlobalPluginDirs, all),
  262. GlobalPluginsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- GlobalPlugins],
  263. lists:foreach(
  264. fun({app, Name}) ->
  265. ct:pal("App Name: ~p", [Name]),
  266. case lists:keyfind(Name, 1, DepsNames) of
  267. false ->
  268. error({app_not_found, Name});
  269. {Name, _App} ->
  270. ok
  271. end
  272. ; ({app, Name, invalid}) ->
  273. ct:pal("Invalid Name: ~p", [Name]),
  274. case lists:keyfind(Name, 1, InvalidDepsNames) of
  275. false ->
  276. error({app_not_found, Name});
  277. {Name, _App} ->
  278. ok
  279. end
  280. ; ({app, Name, valid}) ->
  281. ct:pal("Valid Name: ~p", [Name]),
  282. case lists:keyfind(Name, 1, ValidDepsNames) of
  283. false ->
  284. error({app_not_found, Name});
  285. {Name, _App} ->
  286. ok
  287. end
  288. ; ({dep_not_exist, Name}) ->
  289. ct:pal("Dep Not Exist Name: ~p", [Name]),
  290. case lists:keyfind(Name, 1, DepsNames) of
  291. false ->
  292. ok;
  293. {Name, _App} ->
  294. error({app_found, Name})
  295. end
  296. ; ({app_not_exist, Name}) ->
  297. ct:pal("App Not Exist Name: ~p", [Name]),
  298. case lists:keyfind(Name, 1, DepsNames) of
  299. false ->
  300. ok;
  301. {Name, _App} ->
  302. error({app_found, Name})
  303. end
  304. ; ({checkout, Name}) ->
  305. ct:pal("Checkout Name: ~p", [Name]),
  306. ?assertNotEqual(false, lists:keyfind(Name, 1, CheckoutsNames))
  307. ; ({dep, Name}) ->
  308. ct:pal("Dep Name: ~p", [Name]),
  309. ?assertNotEqual(false, lists:keyfind(Name, 1, DepsNames))
  310. ; ({dep, Name, Vsn}) ->
  311. ct:pal("Dep Name: ~p, Vsn: ~p", [Name, Vsn]),
  312. case lists:keyfind(Name, 1, DepsNames) of
  313. false ->
  314. error({dep_not_found, Name});
  315. {Name, App} ->
  316. ?assertEqual(iolist_to_binary(Vsn),
  317. iolist_to_binary(rebar_app_info:original_vsn(App)))
  318. end
  319. ; ({subdir_dep, Name}) ->
  320. ct:pal("Subdir Dep Name: ~p", [Name]),
  321. ?assertNotEqual(false, lists:keyfind(Name, 1, SubDirDepsNames))
  322. ; ({subdir_dep, Name, Vsn}) ->
  323. ct:pal("Subdir Dep Name: ~p, Vsn: ~p", [Name, Vsn]),
  324. case lists:keyfind(Name, 1, SubDirDepsNames) of
  325. false ->
  326. error({dep_not_found, Name});
  327. {Name, App} ->
  328. ?assertEqual(iolist_to_binary(Vsn),
  329. iolist_to_binary(rebar_app_info:original_vsn(App)))
  330. end
  331. ; ({plugin, Name}) ->
  332. ct:pal("Plugin Name: ~p", [Name]),
  333. ?assertNotEqual(false, lists:keyfind(Name, 1, PluginsNames))
  334. ; ({plugin, Name, Vsn}) ->
  335. ct:pal("Plugin Name: ~p, Vsn: ~p", [Name, Vsn]),
  336. case lists:keyfind(Name, 1, PluginsNames) of
  337. false ->
  338. error({plugin_not_found, Name});
  339. {Name, App} ->
  340. ?assertEqual(iolist_to_binary(Vsn),
  341. iolist_to_binary(rebar_app_info:original_vsn(App)))
  342. end
  343. ; ({global_plugin, Name}) ->
  344. ct:pal("Global Plugin Name: ~p", [Name]),
  345. ?assertNotEqual(false, lists:keyfind(Name, 1, GlobalPluginsNames))
  346. ; ({global_plugin, Name, Vsn}) ->
  347. ct:pal("Global Plugin Name: ~p, Vsn: ~p", [Name, Vsn]),
  348. case lists:keyfind(Name, 1, GlobalPluginsNames) of
  349. false ->
  350. error({global_plugin_not_found, Name});
  351. {Name, App} ->
  352. ?assertEqual(iolist_to_binary(Vsn),
  353. iolist_to_binary(rebar_app_info:original_vsn(App)))
  354. end
  355. ; ({lock, Name}) ->
  356. ct:pal("Lock Name: ~p", [Name]),
  357. ?assertNotEqual(false, lists:keyfind(iolist_to_binary(Name), 1, Locks))
  358. ; ({lock, Name, Vsn}) ->
  359. ct:pal("Lock Name: ~p, Vsn: ~p", [Name, Vsn]),
  360. case lists:keyfind(iolist_to_binary(Name), 1, Locks) of
  361. false ->
  362. error({lock_not_found, Name});
  363. {_LockName, {pkg, _, LockVsn, _InnerHash, OuterHash}, _} ->
  364. ?assertEqual(iolist_to_binary(Vsn),
  365. iolist_to_binary(LockVsn)),
  366. ?assertNotEqual(undefined, OuterHash);
  367. {_LockName, {_, _, {ref, LockVsn}}, _} ->
  368. ?assertEqual(iolist_to_binary(Vsn),
  369. iolist_to_binary(LockVsn))
  370. end
  371. ; ({lock, pkg, Name, Vsn}) ->
  372. ct:pal("Pkg Lock Name: ~p, Vsn: ~p", [Name, Vsn]),
  373. case lists:keyfind(iolist_to_binary(Name), 1, Locks) of
  374. false ->
  375. error({lock_not_found, Name});
  376. {_LockName, {pkg, _, LockVsn, _InnerHash, OuterHash}, _} ->
  377. ?assertEqual(iolist_to_binary(Vsn),
  378. iolist_to_binary(LockVsn)),
  379. ?assertNotEqual(undefined, OuterHash);
  380. {_LockName, {_, _, {ref, LockVsn}}, _} ->
  381. error({source_lock, {Name, LockVsn}})
  382. end
  383. ; ({lock, src, Name, Vsn}) ->
  384. ct:pal("Src Lock Name: ~p, Vsn: ~p", [Name, Vsn]),
  385. case lists:keyfind(iolist_to_binary(Name), 1, Locks) of
  386. false ->
  387. error({lock_not_found, Name});
  388. {_LockName, {pkg, _, LockVsn, _}, _} ->
  389. error({pkg_lock, {Name, LockVsn}});
  390. {_LockName, {_, _, {ref, LockVsn}}, _} ->
  391. ?assertEqual(iolist_to_binary(Vsn),
  392. iolist_to_binary(LockVsn))
  393. end
  394. ; ({release, Name, Vsn, ExpectedDevMode}) ->
  395. ct:pal("Release: ~p-~ts", [Name, Vsn]),
  396. {ok, Cwd} = file:get_cwd(),
  397. try
  398. file:set_cwd(AppDir),
  399. [ReleaseDir] = filelib:wildcard(filename:join([AppDir, "_build", "*", "rel"])),
  400. LibDir = filename:join([ReleaseDir, Name, "lib"]),
  401. {ok, RelLibs} = rebar_utils:list_dir(LibDir),
  402. IsSymLinkFun =
  403. fun(X) ->
  404. ec_file:is_symlink(filename:join(LibDir, X))
  405. end,
  406. DevMode = lists:all(IsSymLinkFun, RelLibs),
  407. ?assertEqual(ExpectedDevMode, DevMode),
  408. ?assert(ec_file:exists(filename:join([ReleaseDir, Name, "releases", Vsn]))),
  409. %% throws not_found if it doesn't exist
  410. ok
  411. catch
  412. _ ->
  413. ct:fail(release_not_found)
  414. after
  415. file:set_cwd(Cwd)
  416. end
  417. ; ({tar, Name, Vsn}) ->
  418. ct:pal("Tarball: ~ts-~ts", [Name, Vsn]),
  419. Tarball = filename:join([AppDir, "_build", "rel", Name, Name++"-"++Vsn++".tar.gz"]),
  420. ?assertNotEqual([], filelib:is_file(Tarball))
  421. ; ({file, Filename}) ->
  422. ct:pal("Filename: ~ts", [Filename]),
  423. ?assert(filelib:is_file(Filename))
  424. ; ({dir, Dirname}) ->
  425. ct:pal("Directory: ~ts", [Dirname]),
  426. ?assert(filelib:is_dir(Dirname))
  427. end, Expected).
  428. write_plugin_file(Dir, Name) ->
  429. Erl = filename:join([Dir, "src", Name]),
  430. ok = filelib:ensure_dir(Erl),
  431. ok = ec_file:write(Erl, plugin_src_file(Name)).
  432. write_src_file(Dir, Name) ->
  433. Erl = filename:join([Dir, "src", Name]),
  434. ok = filelib:ensure_dir(Erl),
  435. ok = ec_file:write(Erl, erl_src_file(Name)).
  436. write_eunitized_src_file(Dir, Name) ->
  437. Erl = filename:join([Dir, "src", "not_a_real_src_" ++ Name ++ ".erl"]),
  438. ok = filelib:ensure_dir(Erl),
  439. ok = ec_file:write(Erl, erl_eunitized_src_file("not_a_real_src_" ++ Name ++ ".erl")).
  440. write_eunit_suite_file(Dir, Name) ->
  441. Erl = filename:join([Dir, "test", "not_a_real_src_" ++ Name ++ "_tests.erl"]),
  442. ok = filelib:ensure_dir(Erl),
  443. ok = ec_file:write(Erl, erl_eunit_suite_file("not_a_real_src_" ++ Name ++ ".erl")).
  444. write_app_file(Dir, Name, Version, Deps) ->
  445. Filename = filename:join([Dir, "ebin", Name ++ ".app"]),
  446. ok = filelib:ensure_dir(Filename),
  447. ok = ec_file:write_term(Filename, get_app_metadata(ec_cnv:to_list(Name), Version, Deps)).
  448. write_app_src_file(Dir, Name, Version, Deps) ->
  449. Filename = filename:join([Dir, "src", Name ++ ".app.src"]),
  450. ok = filelib:ensure_dir(Filename),
  451. ok = ec_file:write_term(Filename, get_app_metadata(ec_cnv:to_list(Name), Version, Deps)).
  452. erl_src_file(Name) ->
  453. io_lib:format("-module('~s').\n"
  454. "-export([main/0]).\n"
  455. "main() -> ok.\n", [filename:basename(Name, ".erl")]).
  456. plugin_src_file(Name) ->
  457. io_lib:format("-module('~s').\n"
  458. "-export([init/1, do/1]).\n"
  459. "init(State) -> \n"
  460. "Provider = providers:create([\n"
  461. "{name, '~s'},\n"
  462. "{module, '~s'}\n"
  463. "]),\n"
  464. "{ok, rebar_state:add_provider(State, Provider)}.\n"
  465. "do(State) -> {ok, State}.\n", [filename:basename(Name, ".erl"),
  466. filename:basename(Name, ".erl"),
  467. filename:basename(Name, ".erl")]).
  468. erl_eunitized_src_file(Name) ->
  469. io_lib:format("-module('~s').\n"
  470. "-export([main/0]).\n"
  471. "main() -> ok.\n"
  472. "-ifdef(TEST).\n"
  473. "-include_lib(\"eunit/include/eunit.hrl\").\n"
  474. "some_test_() -> ?_assertEqual(ok, main()).\n"
  475. "-endif.\n", [filename:basename(Name, ".erl")]).
  476. erl_eunit_suite_file(Name) ->
  477. BaseName = filename:basename(Name, ".erl"),
  478. io_lib:format("-module('~s_tests').\n"
  479. "-compile(export_all).\n"
  480. "-ifndef(some_define).\n"
  481. "-define(some_define, false).\n"
  482. "-endif.\n"
  483. "-ifdef(TEST).\n"
  484. "-include_lib(\"eunit/include/eunit.hrl\").\n"
  485. "some_test_() -> ?_assertEqual(ok, ~s:main()).\n"
  486. "define_test_() -> ?_assertEqual(true, ?some_define).\n"
  487. "-endif.\n", [BaseName, BaseName]).
  488. get_app_metadata(Name, Vsn, Deps) ->
  489. {application, erlang:list_to_atom(Name),
  490. [{description, ""},
  491. {vsn, Vsn},
  492. {modules, []},
  493. {included_applications, []},
  494. {registered, []},
  495. {applications, Deps}]}.
  496. package_app(AppDir, DestDir, PkgName, PkgVsn) ->
  497. AppSrc = filename:join(AppDir, "src"),
  498. {ok, Fs} = rebar_utils:list_dir(AppSrc),
  499. Files = lists:zip([filename:join("src", F) || F <- Fs], [filename:join(AppSrc,F) || F <- Fs]),
  500. Metadata = #{<<"app">> => list_to_binary(PkgName),
  501. <<"version">> => list_to_binary(PkgVsn)},
  502. {ok, #{tarball := Tarball, outer_checksum := <<Checksum:256/big-unsigned-integer>>}} = r3_hex_tarball:create(Metadata, Files),
  503. Name = PkgName++"-"++PkgVsn++".tar",
  504. Archive = filename:join(DestDir, Name),
  505. file:write_file(Archive, Tarball),
  506. <<E:128/big-unsigned-integer>> = crypto:hash(md5, Tarball),
  507. Checksum1 = list_to_binary(
  508. rebar_string:uppercase(
  509. lists:flatten(io_lib:format("~64.16.0b", [Checksum])))),
  510. {Checksum1, E}.
  511. random_element(Repos) ->
  512. Index = ?random:uniform(length(Repos)),
  513. lists:nth(Index, Repos).