1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
-module(rebar_prv_update).
-behaviour(provider).
-export([init/1,
do/1,
format_error/1]).
-include("rebar.hrl").
-include_lib("providers/include/providers.hrl").
-define(PROVIDER, update).
-define(DEPS, []).
%% Ignore warning of digraph opaque type when running dialyzer
-dialyzer({no_opaque, do/1}).
-dialyzer({no_opaque, write_registry/3}).
%% Ignoring the opaque type warning won't stop dialyzer from warning of
%% no return for functions that had the opaque type warnings
-dialyzer({no_return, do/1}).
-dialyzer({no_return, write_registry/3}).
%% ===================================================================
%% Public API
%% ===================================================================
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
{bare, true},
{deps, ?DEPS},
{example, "rebar3 update"},
{short_desc, "Update package index."},
{desc, "Update package index."},
{opts, []}])),
{ok, State1}.
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
?INFO("Updating package index...", []),
try
RegistryDir = rebar_packages:package_dir(State),
filelib:ensure_dir(filename:join(RegistryDir, "dummy")),
HexFile = filename:join(RegistryDir, "registry"),
TmpDir = ec_file:insecure_mkdtemp(),
TmpFile = filename:join(TmpDir, "packages.gz"),
Url = rebar_state:get(State, rebar_packages_cdn, "https://s3.amazonaws.com/s3.hex.pm/registry.ets.gz"),
{ok, _RequestId} = httpc:request(get, {Url, []},
[], [{stream, TmpFile}, {sync, true}],
rebar),
{ok, Data} = file:read_file(TmpFile),
Unzipped = zlib:gunzip(Data),
ok = file:write_file(HexFile, Unzipped),
{Dict, Graph} = hex_to_graph(HexFile),
write_registry(Dict, Graph, State),
ok
catch
_E:C ->
?DEBUG("Error creating package index: ~p ~p", [C, erlang:get_stacktrace()]),
throw(?PRV_ERROR(package_index_write))
end,
{ok, State}.
-spec format_error(any()) -> iolist().
format_error(package_index_write) ->
"Failed to write package index.".
-spec write_registry(rebar_dict(), {digraph, ets:tid(), ets:tid(), ets:tid(), any()}, rebar_state:t()) -> ok | {error, atom()}.
write_registry(Dict, {digraph, Edges, Vertices, Neighbors, _}, State) ->
RegistryDir = rebar_packages:package_dir(State),
filelib:ensure_dir(filename:join(RegistryDir, "dummy")),
ets:tab2file(Edges, filename:join(RegistryDir, "edges")),
ets:tab2file(Vertices, filename:join(RegistryDir, "vertices")),
ets:tab2file(Neighbors, filename:join(RegistryDir, "neighbors")),
file:write_file(filename:join(RegistryDir, "dict"), term_to_binary(Dict)).
is_supported(<<"make">>) -> true;
is_supported(<<"rebar">>) -> true;
is_supported(_) -> false.
hex_to_graph(Filename) ->
{ok, T} = ets:file2tab(Filename),
Graph = digraph:new(),
ets:foldl(fun({Pkg, [Versions]}, ok) when is_binary(Pkg), is_list(Versions) ->
lists:foreach(fun(Version) ->
digraph:add_vertex(Graph, {Pkg, Version}, 1)
end, Versions);
(_, ok) ->
ok
end, ok, T),
Dict1 = ets:foldl(fun({{Pkg, PkgVsn}, [Deps, _, BuildTools | _]}, Dict) when is_list(BuildTools) ->
case lists:any(fun is_supported/1, BuildTools) of
true ->
DepsList = update_graph(Pkg, PkgVsn, Deps, T, Graph),
dict:store({Pkg, PkgVsn}, DepsList, Dict);
false ->
Dict
end;
(_, Dict) ->
Dict
end, dict:new(), T),
{Dict1, Graph}.
update_graph(Pkg, PkgVsn, Deps, HexRegistry, Graph) ->
lists:foldl(fun([Dep, DepVsn, false, _AppName | _], DepsListAcc) ->
case DepVsn of
<<"~> ", Vsn/binary>> ->
case rebar_packages:find_highest_matching(Dep, Vsn, HexRegistry) of
{ok, HighestDepVsn} ->
digraph:add_edge(Graph, {Pkg, PkgVsn}, {Dep, HighestDepVsn}),
[{Dep, DepVsn} | DepsListAcc];
none ->
DepsListAcc
end;
Vsn ->
digraph:add_edge(Graph, {Pkg, PkgVsn}, {Dep, Vsn}),
[{Dep, Vsn} | DepsListAcc]
end;
([_Dep, _DepVsn, true, _AppName | _], DepsListAcc) ->
DepsListAcc
end, [], Deps).
|