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
|
%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 et
-module(rebar_pkg_resource).
-behaviour(rebar_resource).
-export([lock/2
,download/3
,needs_update/2
,make_vsn/1]).
-include_lib("providers/include/providers.hrl").
-include("rebar.hrl").
-define(DEFAULT_CDN, "https://s3.amazonaws.com/s3.hex.pm/tarballs").
lock(_AppDir, Source) ->
Source.
needs_update(Dir, {pkg, _Name, Vsn}) ->
[AppInfo] = rebar_app_discover:find_apps([Dir], all),
case rebar_app_info:original_vsn(AppInfo) =:= ec_cnv:to_list(Vsn) of
true ->
false;
false ->
true
end.
download(_Dir, {pkg, Name, Vsn}, State) ->
CDN = rebar_state:get(State, rebar_packages_cdn, ?DEFAULT_CDN),
PackageDir = hex_package_dir(CDN, State),
Package = binary_to_list(<<Name/binary, "-", Vsn/binary, ".tar">>),
Path = filename:join(PackageDir, Package),
Url = string:join([CDN, Package], "/"),
case request(Url, etag(Path)) of
{ok, cached} ->
{tarball, Path};
{ok, Binary} ->
file:write_file(Path, Binary),
{tarball, Path};
error ->
case filelib:is_regular(Path) of
true ->
?DEBUG("Download ~s error, using ~s since it exists", [Url, Path]),
{tarball, Path};
false ->
throw(request_failed)
end
end.
make_vsn(_) ->
{error, "Replacing version of type pkg not supported."}.
%% Use the shared hex package directory unless a non-default package repo is used
hex_package_dir(?DEFAULT_CDN, _) ->
filename:join([rebar_dir:home_dir(), ".hex", "packages"]);
hex_package_dir(CDN, State) ->
CacheDir = rebar_dir:global_cache_dir(State),
{ok, {_, _, Host, _, _, _}} = http_uri:parse(CDN),
CDNPath = filename:join(lists:reverse(string:tokens(Host, "."))),
PackageDir = filename:join([CacheDir, "hex", CDNPath, "packages"]),
ok = filelib:ensure_dir(filename:join(PackageDir, "placeholder")),
PackageDir.
request(Url, ETag) ->
case httpc:request(get, {Url, [{"if-none-match", ETag} || ETag =/= false]},
[{relaxed, true}],
[{body_format, binary}]) of
{ok, {{_Version, 200, _Reason}, _Headers, Body}} ->
?DEBUG("Successfully downloaded ~s", [Url]),
{ok, Body};
{ok, {{_Version, 304, _Reason}, _Headers, _Body}} ->
?DEBUG("Cached copy of ~s still valid", [Url]),
{ok, cached};
{ok, {{_Version, Code, _Reason}, _Headers, _Body}} ->
?DEBUG("Request to ~p failed: status code ~p", [Url, Code]),
error;
{error, Reason} ->
?DEBUG("Request to ~p failed: ~p", [Url, Reason]),
error
end.
etag(Path) ->
case file:read_file(Path) of
{ok, Binary} ->
<<X:128/big-unsigned-integer>> = crypto:hash(md5, Binary),
string:to_lower(lists:flatten(io_lib:format("~32.16.0b", [X])));
{error, _} ->
false
end.
|