File Coverage

blib/lib/Dist/Zilla/Plugin/GitHub.pm
Criterion Covered Total %
statement 40 70 57.1
branch 4 22 18.1
condition 2 11 18.1
subroutine 13 14 92.8
pod n/a
total 59 117 50.4


line stmt bran cond sub pod time code
1             package Dist::Zilla::Plugin::GitHub; # git description: v0.48-3-g3b5901f
2             # ABSTRACT: Plugins to integrate Dist::Zilla with GitHub
3 1     1   153342 use strict;
  1         7  
  1         23  
4 1     1   4 use warnings;
  1         2  
  1         65  
5              
6             our $VERSION = '0.49';
7              
8 1     1   6 use JSON::MaybeXS;
  1         2  
  1         54  
9 1     1   438 use Moose;
  1         392211  
  1         7  
10 1     1   6190 use Try::Tiny;
  1         2  
  1         58  
11 1     1   688 use HTTP::Tiny;
  1         34987  
  1         37  
12 1     1   418 use Git::Wrapper;
  1         20653  
  1         37  
13 1     1   8 use Class::Load qw(try_load_class);
  1         2  
  1         1006  
14              
15             has remote => (
16             is => 'ro',
17             isa => 'Maybe[Str]',
18             default => 'origin'
19             );
20              
21             has repo => (
22             is => 'ro',
23             isa => 'Maybe[Str]'
24             );
25              
26             has api => (
27             is => 'ro',
28             isa => 'Str',
29             default => 'https://api.github.com'
30             );
31              
32             has prompt_2fa => (
33             is => 'rw',
34             isa => 'Bool',
35             default => 0
36             );
37              
38             has _login => (
39             is => 'ro',
40             isa => 'Maybe[Str]',
41             lazy => 1,
42             builder => '_build_login',
43             );
44              
45             has _credentials => (
46             is => 'ro',
47             isa => 'HashRef',
48             lazy => 1,
49             builder => '_build_credentials',
50             );
51              
52             #pod =head1 DESCRIPTION
53             #pod
54             #pod B<Dist-Zilla-Plugin-GitHub> is a set of plugins for L<Dist::Zilla> intended
55             #pod to more easily integrate L<GitHub|https://github.com> in the C<dzil> workflow.
56             #pod
57             #pod The following is the list of the plugins shipped in this distribution:
58             #pod
59             #pod =over 4
60             #pod
61             #pod =item * L<Dist::Zilla::Plugin::GitHub::Create> Create GitHub repo on C<dzil new>
62             #pod
63             #pod =item * L<Dist::Zilla::Plugin::GitHub::Update> Update GitHub repo info on release
64             #pod
65             #pod =item * L<Dist::Zilla::Plugin::GitHub::Meta> Add GitHub repo info to F<META.{yml,json}>
66             #pod
67             #pod =back
68             #pod
69             #pod This distribution also provides a plugin bundle, L<Dist::Zilla::PluginBundle::GitHub>,
70             #pod which provides L<GitHub::Meta|Dist::Zilla::Plugin::GitHub::Meta> and
71             #pod L<[GitHub::Update|Dist::Zilla::Plugin::GitHub::Update> together in one convenient bundle.
72             #pod
73             #pod This distribution also provides an additional C<dzil> command (L<dzil
74             #pod gh|Dist::Zilla::App::Command::gh>) and a L<plugin
75             #pod bundle|Dist::Zilla::PluginBundle::GitHub>.
76             #pod
77             #pod =cut
78              
79             sub _build_login {
80 0     0   0 my $self = shift;
81              
82 0         0 my ($login);
83              
84 0 0       0 my %identity = Config::Identity::GitHub->load
85             if try_load_class('Config::Identity::GitHub');
86              
87 0 0       0 if (%identity) {
88 0         0 $login = $identity{login};
89             } else {
90 0         0 $login = `git config github.user`; chomp $login;
  0         0  
91             }
92              
93 0 0       0 if (!$login) {
94 0 0       0 my $error = %identity ?
95             "Err: missing value 'user' in ~/.github" :
96             "Err: Missing value 'github.user' in git config";
97              
98 0         0 $self->log($error);
99 0         0 return undef;
100             }
101              
102 0         0 return $login;
103             }
104              
105             sub _build_credentials {
106             my $self = shift;
107              
108             my ($login, $pass, $token);
109              
110             $login = $self->_login;
111              
112             if (!$login) {
113             return {};
114             }
115              
116             my %identity = Config::Identity::GitHub->load
117             if try_load_class('Config::Identity::GitHub');
118              
119             if (%identity) {
120             $token = $identity{token};
121             $pass = $identity{password};
122             } else {
123             $token = `git config github.token`; chomp $token;
124             $pass = `git config github.password`; chomp $pass;
125             }
126              
127             if (!$pass and !$token) {
128             $pass = $self->zilla->chrome->prompt_str(
129             "GitHub password for '$login'", { noecho => 1 },
130             );
131             }
132              
133             return { login => $login, pass => $pass, token => $token };
134             }
135              
136             sub _has_credentials {
137 1     1   5 my $self = shift;
138 1         5 return keys %{$self->_credentials};
  1         52  
139             }
140              
141             sub _auth_headers {
142 1     1   88 my $self = shift;
143              
144 1         52 my $credentials = $self->_credentials;
145              
146 1         12 my %headers = ( Accept => 'application/vnd.github.v3+json' );
147 1 50       10 if ($credentials->{pass}) {
    50          
148 0         0 require MIME::Base64;
149 0         0 my $basic = MIME::Base64::encode_base64("$credentials->{login}:$credentials->{pass}", '');
150 0         0 $headers{Authorization} = "Basic $basic";
151             }
152             elsif ($credentials->{token}) {
153 0         0 $headers{Authorization} = "token $credentials->{token}";
154             }
155              
156             # This can't be done at object creation because we autodetect the
157             # need for 2FA when GitHub says we need it, so we won't know to
158             # prompt at object creation time.
159 1 50       36 if ($self->prompt_2fa) {
160 0         0 my $otp = $self->zilla->chrome->prompt_str(
161             "GitHub two-factor authentication code for '$credentials->{login}'",
162             { noecho => 1 },
163             );
164              
165 0         0 $headers{'X-GitHub-OTP'} = $otp;
166 0         0 $self->log([ "Using two-factor authentication" ]);
167             }
168              
169 1         12 return \%headers;
170             }
171              
172             sub _get_repo_name {
173             my ($self, $login) = @_;
174              
175             my $repo;
176             my $git = Git::Wrapper->new('./');
177              
178             $repo = $self->repo if $self->repo;
179              
180             my $url;
181             {
182             local $ENV{LANG}='C';
183             ($url) = map /Fetch URL: (.*)/,
184             $git->remote('show', '-n', $self->remote);
185             }
186              
187             $url =~ /github\.com.*?[:\/](.*)\.git$/;
188             $repo = $1 unless $repo and not $1;
189              
190             $repo = $self->zilla->name unless $repo;
191              
192             if ($repo !~ /.*\/.*/) {
193             $login = $self->_login;
194             if (defined $login) {
195             $repo = "$login/$repo";
196             }
197             }
198              
199             return $repo;
200             }
201              
202             sub _check_response {
203 2     2   6 my ($self, $response) = @_;
204              
205             try {
206 2     2   111 my $json_text = decode_json($response->{content});
207              
208 0 0       0 if (!$response->{success}) {
209             return 'redo' if (($response->{status} eq '401') and
210 0 0 0     0 (($response->{headers}{'x-github-otp'} // '') =~ /^required/));
      0        
211              
212 0 0       0 if ($response->{status} eq '404') {
213 0         0 $self->log($response->{reason}.' (insufficient permissions to edit this resource?)');
214 0         0 return;
215             }
216              
217 0         0 require Data::Dumper;
218 0         0 $self->log("Err: ", Data::Dumper->new([ $response ])->Indent(2)->Terse(1)->Sortkeys(1)->Dump);
219 0         0 return;
220             }
221              
222 0         0 return $json_text;
223             } catch {
224 2     2   33 $self->log("Error: $_");
225 2 50 33     474 if ($response and !$response->{success} and
      33        
226             $response->{status} eq '599') {
227             #possibly HTTP::Tiny error
228 0         0 $self->log("Err: ", $response->{content});
229 0         0 return;
230             }
231              
232 2         11 $self->log("Error communicating with GitHub: $_");
233              
234 2         453 return;
235 2         52 };
236             }
237              
238             __PACKAGE__->meta->make_immutable;
239             1; # End of Dist::Zilla::Plugin::GitHub
240              
241             __END__
242              
243             =pod
244              
245             =encoding UTF-8
246              
247             =head1 NAME
248              
249             Dist::Zilla::Plugin::GitHub - Plugins to integrate Dist::Zilla with GitHub
250              
251             =head1 VERSION
252              
253             version 0.49
254              
255             =head1 DESCRIPTION
256              
257             B<Dist-Zilla-Plugin-GitHub> is a set of plugins for L<Dist::Zilla> intended
258             to more easily integrate L<GitHub|https://github.com> in the C<dzil> workflow.
259              
260             The following is the list of the plugins shipped in this distribution:
261              
262             =over 4
263              
264             =item * L<Dist::Zilla::Plugin::GitHub::Create> Create GitHub repo on C<dzil new>
265              
266             =item * L<Dist::Zilla::Plugin::GitHub::Update> Update GitHub repo info on release
267              
268             =item * L<Dist::Zilla::Plugin::GitHub::Meta> Add GitHub repo info to F<META.{yml,json}>
269              
270             =back
271              
272             This distribution also provides a plugin bundle, L<Dist::Zilla::PluginBundle::GitHub>,
273             which provides L<GitHub::Meta|Dist::Zilla::Plugin::GitHub::Meta> and
274             L<[GitHub::Update|Dist::Zilla::Plugin::GitHub::Update> together in one convenient bundle.
275              
276             This distribution also provides an additional C<dzil> command (L<dzil
277             gh|Dist::Zilla::App::Command::gh>) and a L<plugin
278             bundle|Dist::Zilla::PluginBundle::GitHub>.
279              
280             =head1 SUPPORT
281              
282             Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Dist-Zilla-Plugin-GitHub>
283             (or L<bug-Dist-Zilla-Plugin-GitHub@rt.cpan.org|mailto:bug-Dist-Zilla-Plugin-GitHub@rt.cpan.org>).
284              
285             There is also a mailing list available for users of this distribution, at
286             L<http://dzil.org/#mailing-list>.
287              
288             There is also an irc channel available for users of this distribution, at
289             L<C<#distzilla> on C<irc.perl.org>|irc://irc.perl.org/#distzilla>.
290              
291             =head1 AUTHOR
292              
293             Alessandro Ghedini <alexbio@cpan.org>
294              
295             =head1 CONTRIBUTORS
296              
297             =for stopwords Alessandro Ghedini Karen Etheridge Dave Rolsky Mike Friedman Jeffrey Ryan Thalhammer Joelle Maslak Doherty Rafael Kitover Alexandr Ciornii Brian Phillips Chris Weyl Ioan Rogers Jose Luis Perez Diez Mohammad S Anwar Paul Cochrane Ricardo Signes Vyacheslav Matyukhin
298              
299             =over 4
300              
301             =item *
302              
303             Alessandro Ghedini <alessandro@ghedini.me>
304              
305             =item *
306              
307             Karen Etheridge <ether@cpan.org>
308              
309             =item *
310              
311             Dave Rolsky <autarch@urth.org>
312              
313             =item *
314              
315             Mike Friedman <mike.friedman@10gen.com>
316              
317             =item *
318              
319             Jeffrey Ryan Thalhammer <jeff@imaginative-software.com>
320              
321             =item *
322              
323             Joelle Maslak <jmaslak@antelope.net>
324              
325             =item *
326              
327             Mike Doherty <doherty@cs.dal.ca>
328              
329             =item *
330              
331             Rafael Kitover <rkitover@cpan.org>
332              
333             =item *
334              
335             Alexandr Ciornii <alexchorny@gmail.com>
336              
337             =item *
338              
339             Brian Phillips <bphillips@cpan.org>
340              
341             =item *
342              
343             Chris Weyl <cweyl@alumni.drew.edu>
344              
345             =item *
346              
347             Ioan Rogers <ioan.rogers@gmail.com>
348              
349             =item *
350              
351             Jose Luis Perez Diez <jluis@escomposlinux.org>
352              
353             =item *
354              
355             Mohammad S Anwar <mohammad.anwar@yahoo.com>
356              
357             =item *
358              
359             Paul Cochrane <paul.cochrane@posteo.de>
360              
361             =item *
362              
363             Ricardo Signes <rjbs@cpan.org>
364              
365             =item *
366              
367             Vyacheslav Matyukhin <mmcleric@yandex-team.ru>
368              
369             =back
370              
371             =head1 COPYRIGHT AND LICENSE
372              
373             This software is copyright (c) 2011 by Alessandro Ghedini.
374              
375             This is free software; you can redistribute it and/or modify it under
376             the same terms as the Perl 5 programming language system itself.
377              
378             =cut