File Coverage

blib/lib/Alien/Build/Plugin/Download/GitHub.pm
Criterion Covered Total %
statement 86 112 76.7
branch 32 54 59.2
condition 10 21 47.6
subroutine 15 15 100.0
pod 1 1 100.0
total 144 203 70.9


line stmt bran cond sub pod time code
1             package Alien::Build::Plugin::Download::GitHub;
2              
3 2     2   208180 use strict;
  2         8  
  2         52  
4 2     2   8 use warnings;
  2         5  
  2         37  
5 2     2   46 use 5.008001;
  2         6  
6 2     2   10 use Carp qw( croak );
  2         3  
  2         108  
7 2     2   1456 use Path::Tiny qw( path );
  2         20996  
  2         105  
8 2     2   1379 use JSON::PP qw( decode_json );
  2         26289  
  2         231  
9 2     2   1662 use URI;
  2         11307  
  2         76  
10 2     2   1230 use Alien::Build::Plugin;
  2         21335  
  2         15  
11 2     2   1040 use Alien::Build::Plugin::Download::Negotiate;
  2         5416  
  2         19  
12 2     2   857 use Alien::Build::Plugin::Extract::Negotiate;
  2         60945  
  2         15  
13              
14             # ABSTRACT: Alien::Build plugin to download from GitHub
15             our $VERSION = '0.09'; # VERSION
16              
17              
18             has github_user => sub { croak("github_user is required") };
19             has github_repo => sub { croak("github_repo is required") };
20             has include_assets => 0;
21             has version => qr/^v?(.*)$/;
22             has prefer => 0;
23             has tags_only => 0;
24              
25              
26             has asset => 0;
27             has asset_name => qr/\.tar\.gz$/;
28             has asset_format => 'tar.gz';
29             has asset_convert_version => 0;
30              
31             my $once = 1;
32              
33             sub init
34             {
35 7     7 1 175143 my($self, $meta) = @_;
36              
37 7 50       24 croak("Don't set set a start_url with the Download::GitHub plugin") if defined $meta->prop->{start_url};
38 7 50 33     39 croak("cannot use both asset and tag_only") if $self->asset && $self->tags_only;
39              
40 7 50       55 if($self->asset)
41             {
42 0         0 $meta->add_requires('configure' => 'Alien::Build::Plugin::Download::GitHub' => '0.09' );
43             }
44             else
45             {
46 7         47 $meta->add_requires('configure' => 'Alien::Build::Plugin::Download::GitHub' => 0 );
47             }
48              
49 7 100       92 my $endpoint = $self->tags_only ? 'tags' : 'releases' ;
50 7   33     45 $meta->prop->{start_url} ||= "https://api.github.com/repos/@{[ $self->github_user ]}/@{[ $self->github_repo ]}/$endpoint";
  7         40  
  7         54  
51              
52 7         66 $meta->apply_plugin('Download',
53             prefer => $self->prefer,
54             version => $self->version,
55             );
56              
57 7 50 33     56597 if($self->asset && $self->asset_format)
58             {
59 0         0 $meta->apply_plugin('Extract',
60             format => $self->asset_format,
61             );
62             }
63             else
64             {
65 7         58 $meta->apply_plugin('Extract',
66             format => 'tar.gz',
67             );
68             }
69              
70 7         74918 my %gh_fetch_options;
71             my $secret;
72              
73 7         16 foreach my $name (qw( ALIEN_BUILD_GITHUB_TOKEN GITHUB_TOKEN GITHUB_PAT ))
74             {
75 21 50       52 if(defined $ENV{$name})
76             {
77 0         0 $secret = $ENV{$name};
78 0         0 push @{ $gh_fetch_options{http_headers} }, Authorization => "token $secret";
  0         0  
79 0 0       0 Alien::Build->log("using the GitHub Personal Access Token in $name") if $once;
80 0         0 $once = 0;
81 0         0 last;
82             }
83             }
84              
85             $meta->around_hook(
86             fetch => sub {
87 8     8   41240 my $orig = shift;
88 8         24 my($build, $url, @the_rest) = @_;
89              
90             # only do special stuff when talking to GitHub API. In particular, this
91             # avoids leaking the PAT (if specified) to other servers.
92             return $orig->($build, $url, @the_rest)
93 8 50       13 unless do {
94 8   66     46 my $uri = URI->new($url || $build->meta_prop->{start_url});
95 8 50       7400 $uri->host eq 'api.github.com' && $uri->scheme eq 'https';
96             };
97              
98             # Temporarily patch the log method so that we don't log the PAT
99 8         487 my $log = \&Alien::Build::log;
100 2     2   878 no warnings 'redefine';
  2         4  
  2         234  
101             local *Alien::Build::log = sub {
102 0 0       0 if(defined $secret)
103             {
104 0         0 $_[1] =~ s/\Q$secret\E/ '#' x length($secret) /eg;
  0         0  
105             }
106 0         0 goto &$log;
107 8         108 };
108 2     2   20 use warnings;
  2         6  
  2         1210  
109              
110 8         35 my $res = $orig->($build, $url, @the_rest, %gh_fetch_options);
111 8 100 66     1098258 if($res->{type} eq 'file' && $res->{filename} =~ qr{^(?:releases|tags)$})
112             {
113 7         15 my $rel;
114 7 100       26 if($res->{content})
    50          
115             {
116 6         34 $rel = decode_json $res->{content};
117             }
118             elsif($res->{path})
119             {
120 1         7 $rel = decode_json path($res->{path})->slurp;
121             }
122             else
123             {
124 0         0 croak("malformed response object: no content or path");
125             }
126 7 100       104168 my $version_key = $res->{filename} eq 'releases' ? 'tag_name' : 'name';
127              
128 7 50       29 if($ENV{ALIEN_BUILD_PLUGIN_DOWNLOAD_GITHUB_DEBUG})
129             {
130 0         0 require YAML;
131 0   0     0 my $url = $url || $meta->prop->{start_url};
132 0         0 $url = URI->new($url);
133 0         0 $build->log(YAML::Dump({
134             url => $url->path,
135             res => $rel,
136             }));
137             }
138              
139 7         12 my $res2;
140              
141 7 50       33 if($self->asset)
142             {
143 0         0 $res2 = {
144             type => 'list',
145             list => [],
146             };
147              
148 0         0 foreach my $release (@$rel)
149             {
150 0         0 foreach my $asset (@{ $release->{assets} })
  0         0  
151             {
152 0 0       0 if($asset->{name} =~ $self->asset_name)
153             {
154 0         0 push @{ $res2->{list} }, {
155             filename => $asset->{name},
156             url => $asset->{browser_download_url},
157             version => $self->asset_convert_version ? $self->asset_convert_version->($release->{name}) : $release->{name},
158 0 0       0 };
159             }
160             }
161             }
162             }
163             else
164             {
165             $res2 = {
166             type => 'list',
167             list => [
168             map {
169 7         59 my $release = $_;
  18         29  
170 18         45 my($version) = $release->{$version_key} =~ $self->version;
171             my @results = ({
172             filename => $release->{$version_key},
173             url => $release->{tarball_url},
174 18 50       242 defined $version ? (version => $version) : (),
175             });
176              
177 18 100       38 if (my $include = $self->include_assets) {
178 4 100       39 my $filter = ref($include) eq 'Regexp' ? 1 : 0;
179 4 100       5 for my $asset(@{$release->{assets} || []}) {
  4         21  
180             push @results, {
181             asset_url => $asset->{url},
182             filename => $asset->{name},
183             url => $asset->{browser_download_url},
184             defined $version ? (version => $version) : (),
185 4 50 100     26 } if (0 == $filter or $asset->{name} =~ $include);
    100          
186             }
187             }
188 18         119 @results;
189             } @$rel
190             ],
191             };
192             }
193              
194 7 50       21 if($ENV{ALIEN_BUILD_PLUGIN_DOWNLOAD_GITHUB_DEBUG})
195             {
196 0         0 require YAML;
197 0         0 $build->log(YAML::Dump({
198             res2 => $res2,
199             }));
200             }
201              
202 7 50       21 $res2->{protocol} = $res->{protocol} if exists $res->{protocol};
203 7         217 return $res2;
204             }
205             else
206             {
207 1         35 return $res;
208             }
209             },
210 7         56 );
211              
212 7 100       56 unless($self->prefer)
213             {
214             $meta->default_hook(
215             prefer => sub {
216 1     1   271 my($build, $res) = @_;
217 1         3 $res;
218             },
219 6         55 );
220             }
221              
222             }
223              
224             1;
225              
226             __END__