File Coverage

blib/lib/Alien/Build/Plugin/Download/GitHub.pm
Criterion Covered Total %
statement 87 113 76.9
branch 33 56 58.9
condition 10 21 47.6
subroutine 15 15 100.0
pod 1 1 100.0
total 146 206 70.8


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