Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

254 строки
11 KiB

10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
  1. %% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
  2. %% ex: ts=4 sw=4 et
  3. -module(rebar_prv_update).
  4. -behaviour(provider).
  5. -export([init/1,
  6. do/1,
  7. format_error/1]).
  8. -export([hex_to_index/1]).
  9. -ifdef(TEST).
  10. -export([cmp_/6, cmpl_/6, valid_vsn/1]).
  11. -endif.
  12. -include("rebar.hrl").
  13. -include_lib("providers/include/providers.hrl").
  14. -define(PROVIDER, update).
  15. -define(DEPS, []).
  16. %% ===================================================================
  17. %% Public API
  18. %% ===================================================================
  19. -spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
  20. init(State) ->
  21. State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
  22. {module, ?MODULE},
  23. {bare, true},
  24. {deps, ?DEPS},
  25. {example, "rebar3 update"},
  26. {short_desc, "Update package index."},
  27. {desc, "Update package index."},
  28. {opts, []}])),
  29. {ok, State1}.
  30. -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
  31. do(State) ->
  32. try
  33. case rebar_packages:registry_dir(State) of
  34. {ok, RegistryDir} ->
  35. filelib:ensure_dir(filename:join(RegistryDir, "dummy")),
  36. HexFile = filename:join(RegistryDir, "registry"),
  37. ?INFO("Updating package registry...", []),
  38. TmpDir = ec_file:insecure_mkdtemp(),
  39. TmpFile = filename:join(TmpDir, "packages.gz"),
  40. CDN = rebar_state:get(State, rebar_packages_cdn, ?DEFAULT_CDN),
  41. case rebar_utils:url_append_path(CDN, ?REMOTE_REGISTRY_FILE) of
  42. {ok, Url} ->
  43. ?DEBUG("Fetching registry from ~p", [Url]),
  44. case httpc:request(get, {Url, [{"User-Agent", rebar_utils:user_agent()}]},
  45. [], [{stream, TmpFile}, {sync, true}],
  46. rebar) of
  47. {ok, saved_to_file} ->
  48. {ok, Data} = file:read_file(TmpFile),
  49. Unzipped = zlib:gunzip(Data),
  50. ok = file:write_file(HexFile, Unzipped),
  51. ?INFO("Writing registry to ~s", [HexFile]),
  52. hex_to_index(State),
  53. {ok, State};
  54. _ ->
  55. ?PRV_ERROR(package_index_download)
  56. end;
  57. _ ->
  58. ?PRV_ERROR({package_parse_cdn, CDN})
  59. end;
  60. {uri_parse_error, CDN} ->
  61. ?PRV_ERROR({package_parse_cdn, CDN})
  62. end
  63. catch
  64. _E:C ->
  65. ?DEBUG("Error creating package index: ~p ~p", [C, erlang:get_stacktrace()]),
  66. throw(?PRV_ERROR(package_index_write))
  67. end.
  68. -spec format_error(any()) -> iolist().
  69. format_error({package_parse_cdn, Uri}) ->
  70. io_lib:format("Failed to parse CDN url: ~p", [Uri]);
  71. format_error(package_index_download) ->
  72. "Failed to download package index.";
  73. format_error(package_index_write) ->
  74. "Failed to write package index.".
  75. is_supported(<<"make">>) -> true;
  76. is_supported(<<"rebar">>) -> true;
  77. is_supported(<<"rebar3">>) -> true;
  78. is_supported(_) -> false.
  79. hex_to_index(State) ->
  80. {ok, RegistryDir} = rebar_packages:registry_dir(State),
  81. HexFile = filename:join(RegistryDir, "registry"),
  82. try ets:file2tab(HexFile) of
  83. {ok, Registry} ->
  84. try
  85. PackageIndex = filename:join(RegistryDir, "packages.idx"),
  86. ?INFO("Generating package index...", []),
  87. (catch ets:delete(?PACKAGE_TABLE)),
  88. ets:new(?PACKAGE_TABLE, [named_table, public]),
  89. ets:foldl(fun({{Pkg, PkgVsn}, [Deps, Checksum, BuildTools | _]}, _) when is_list(BuildTools) ->
  90. case lists:any(fun is_supported/1, BuildTools) of
  91. true ->
  92. DepsList = update_deps_list(Pkg, PkgVsn, Deps, Registry, State),
  93. ets:insert(?PACKAGE_TABLE, {{Pkg, PkgVsn}, DepsList, Checksum});
  94. false ->
  95. true
  96. end;
  97. (_, _) ->
  98. true
  99. end, true, Registry),
  100. ets:foldl(fun({Pkg, [[]]}, _) when is_binary(Pkg) ->
  101. true;
  102. ({Pkg, [Vsns=[_Vsn | _Rest]]}, _) when is_binary(Pkg) ->
  103. %% Verify the package is of the right build tool by checking if the first
  104. %% version exists in the table from the foldl above
  105. case [V || V <- Vsns, ets:member(?PACKAGE_TABLE, {Pkg, V})] of
  106. [] ->
  107. true;
  108. Vsns1 ->
  109. ets:insert(?PACKAGE_TABLE, {Pkg, Vsns1})
  110. end;
  111. (_, _) ->
  112. true
  113. end, true, Registry),
  114. ets:insert(?PACKAGE_TABLE, {package_index_version, ?PACKAGE_INDEX_VERSION}),
  115. ?INFO("Writing index to ~s", [PackageIndex]),
  116. ets:tab2file(?PACKAGE_TABLE, PackageIndex),
  117. true
  118. after
  119. catch ets:delete(Registry)
  120. end;
  121. {error, Reason} ->
  122. ?DEBUG("Error loading package registry: ~p", [Reason]),
  123. false
  124. catch
  125. _:_ ->
  126. fail
  127. end.
  128. update_deps_list(Pkg, PkgVsn, Deps, HexRegistry, State) ->
  129. lists:foldl(fun([Dep, DepVsn, false, _AppName | _], DepsListAcc) ->
  130. Dep1 = {Pkg, PkgVsn, Dep},
  131. case {valid_vsn(DepVsn), DepVsn} of
  132. %% Those are all not perfectly implemented!
  133. %% and doubled since spaces seem not to be
  134. %% enforced
  135. {false, Vsn} ->
  136. ?WARN("[~s:~s], Bad dependency version for ~s: ~s.",
  137. [Pkg, PkgVsn, Dep, Vsn]),
  138. DepsListAcc;
  139. {_, <<"~>", Vsn/binary>>} ->
  140. highest_matching(Dep1, rm_ws(Vsn), HexRegistry,
  141. State, DepsListAcc);
  142. {_, <<">=", Vsn/binary>>} ->
  143. cmp(Dep1, rm_ws(Vsn), HexRegistry, State,
  144. DepsListAcc, fun ec_semver:gte/2);
  145. {_, <<">", Vsn/binary>>} ->
  146. cmp(Dep1, rm_ws(Vsn), HexRegistry, State,
  147. DepsListAcc, fun ec_semver:gt/2);
  148. {_, <<"<=", Vsn/binary>>} ->
  149. cmpl(Dep1, rm_ws(Vsn), HexRegistry, State,
  150. DepsListAcc, fun ec_semver:lte/2);
  151. {_, <<"<", Vsn/binary>>} ->
  152. cmpl(Dep1, rm_ws(Vsn), HexRegistry, State,
  153. DepsListAcc, fun ec_semver:lt/2);
  154. {_, <<"==", Vsn/binary>>} ->
  155. [{Dep, Vsn} | DepsListAcc];
  156. {_, Vsn} ->
  157. [{Dep, Vsn} | DepsListAcc]
  158. end;
  159. ([_Dep, _DepVsn, true, _AppName | _], DepsListAcc) ->
  160. DepsListAcc
  161. end, [], Deps).
  162. rm_ws(<<" ", R/binary>>) ->
  163. rm_ws(R);
  164. rm_ws(R) ->
  165. R.
  166. valid_vsn(Vsn) ->
  167. %% Regepx from https://github.com/sindresorhus/semver-regex/blob/master/index.js
  168. SemVerRegExp = "v?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*))?"
  169. "(-[0-9a-z-]+(\\.[0-9a-z-]+)*)?(\\+[0-9a-z-]+(\\.[0-9a-z-]+)*)?",
  170. SupportedVersions = "^(>=?|<=?|~>|==)?\\s*" ++ SemVerRegExp ++ "$",
  171. re:run(Vsn, SupportedVersions) =/= nomatch.
  172. highest_matching({Pkg, PkgVsn, Dep}, Vsn, HexRegistry, State, DepsListAcc) ->
  173. case rebar_packages:find_highest_matching(Pkg, PkgVsn, Dep, Vsn, HexRegistry, State) of
  174. {ok, HighestDepVsn} ->
  175. [{Dep, HighestDepVsn} | DepsListAcc];
  176. none ->
  177. ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`",
  178. [Pkg, PkgVsn, Dep]),
  179. DepsListAcc
  180. end.
  181. cmp({_Pkg, _PkgVsn, Dep} = Dep1, Vsn, HexRegistry, State, DepsListAcc, CmpFun) ->
  182. {ok, Vsns} = rebar_packages:find_all(Dep, HexRegistry, State),
  183. cmp_(undefined, Vsn, Vsns, DepsListAcc, Dep1, CmpFun).
  184. cmp_(undefined, _MinVsn, [], DepsListAcc, {Pkg, PkgVsn, Dep}, _CmpFun) ->
  185. ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`",
  186. [Pkg, PkgVsn, Dep]),
  187. DepsListAcc;
  188. cmp_(HighestDepVsn, _MinVsn, [], DepsListAcc, {_Pkg, _PkgVsn, Dep}, _CmpFun) ->
  189. [{Dep, HighestDepVsn} | DepsListAcc];
  190. cmp_(BestMatch, MinVsn, [Vsn | R], DepsListAcc, Dep, CmpFun) ->
  191. case CmpFun(Vsn, MinVsn) of
  192. true ->
  193. cmp_(Vsn, Vsn, R, DepsListAcc, Dep, CmpFun);
  194. false ->
  195. cmp_(BestMatch, MinVsn, R, DepsListAcc, Dep, CmpFun)
  196. end.
  197. %% We need to treat this differently since we want a version that is LOWER but
  198. %% the higest possible one.
  199. cmpl({_Pkg, _PkgVsn, Dep} = Dep1, Vsn, HexRegistry, State, DepsListAcc, CmpFun) ->
  200. {ok, Vsns} = rebar_packages:find_all(Dep, HexRegistry, State),
  201. cmpl_(undefined, Vsn, Vsns, DepsListAcc, Dep1, CmpFun).
  202. cmpl_(undefined, _MaxVsn, [], DepsListAcc, {Pkg, PkgVsn, Dep}, _CmpFun) ->
  203. ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`",
  204. [Pkg, PkgVsn, Dep]),
  205. DepsListAcc;
  206. cmpl_(HighestDepVsn, _MaxVsn, [], DepsListAcc, {_Pkg, _PkgVsn, Dep}, _CmpFun) ->
  207. [{Dep, HighestDepVsn} | DepsListAcc];
  208. cmpl_(undefined, MaxVsn, [Vsn | R], DepsListAcc, Dep, CmpFun) ->
  209. case CmpFun(Vsn, MaxVsn) of
  210. true ->
  211. cmpl_(Vsn, MaxVsn, R, DepsListAcc, Dep, CmpFun);
  212. false ->
  213. cmpl_(undefined, MaxVsn, R, DepsListAcc, Dep, CmpFun)
  214. end;
  215. cmpl_(BestMatch, MaxVsn, [Vsn | R], DepsListAcc, Dep, CmpFun) ->
  216. case CmpFun(Vsn, MaxVsn) of
  217. true ->
  218. case ec_semver:gte(Vsn, BestMatch) of
  219. true ->
  220. cmpl_(Vsn, MaxVsn, R, DepsListAcc, Dep, CmpFun);
  221. false ->
  222. cmpl_(BestMatch, MaxVsn, R, DepsListAcc, Dep, CmpFun)
  223. end;
  224. false ->
  225. cmpl_(BestMatch, MaxVsn, R, DepsListAcc, Dep, CmpFun)
  226. end.