File Coverage

blib/lib/Dist/Zilla/Plugin/CheckIssues.pm
Criterion Covered Total %
statement 52 77 67.5
branch 14 28 50.0
condition 7 7 100.0
subroutine 11 14 78.5
pod 0 3 0.0
total 84 129 65.1


line stmt bran cond sub pod time code
1 4     4   7661072 use strict;
  4         10  
  4         106  
2 4     4   16 use warnings;
  4         7  
  4         213  
3             package Dist::Zilla::Plugin::CheckIssues; # git description: v0.008-15-g19edd83
4             # ABSTRACT: Retrieve count of outstanding RT and github issues for your distribution
5             # KEYWORDS: plugin bugs issues rt github
6             # vim: set ts=8 sts=4 sw=4 tw=115 et :
7              
8             our $VERSION = '0.009';
9              
10 4     4   18 use Moose;
  4         7  
  4         41  
11             with 'Dist::Zilla::Role::BeforeRelease';
12 4     4   25200 use List::Util 1.33 'none';
  4         103  
  4         315  
13 4     4   20774 use Term::ANSIColor 3.00 'colored';
  4         41303  
  4         1861  
14 4     4   37 use Encode ();
  4         19  
  4         79  
15 4     4   20 use namespace::autoclean;
  4         7  
  4         47  
16              
17             has [qw(rt github colour)] => (
18             is => 'ro', isa => 'Bool',
19             default => 1,
20             );
21              
22             has repo_url => (
23             is => 'rw', isa => 'Str',
24             lazy => 1,
25             default => sub {
26             my $self = shift;
27              
28             my $url;
29             if ($self->zilla->built_in)
30             {
31             # we've already done a build, so distmeta is available
32             my $distmeta = $self->zilla->distmeta;
33             $url = (($distmeta->{resources} || {})->{repository} || {})->{url} || '';
34             }
35             else
36             {
37             # no build (we're probably running the command): we cannot simply
38             # call ->distmeta because various plugins will cause side-effects
39             # with invalid assumptions (no files have been gathered, etc) --
40             # so we just query a short list of plugins that we know can
41             # provide repository resource metadata
42             foreach my $plugin (@{ $self->zilla->plugins_with(-MetaProvider) })
43             {
44             next if none { $plugin->isa('Dist::Zilla::Plugin::' . $_) }
45             qw(MetaResources AutoMetaResources GithubMeta GitHub::Meta Repository);
46              
47             $self->log_debug([ 'calling metadata for %s', $plugin->plugin_name ]);
48             my $plugin_meta = $plugin->metadata;
49             $url = (($plugin_meta->{resources} || {})->{repository} || {})->{url} || '';
50             last if $url;
51             }
52             }
53             $url;
54             },
55             );
56              
57             # owner_name, repo_name
58             has _github_owner_repo => (
59             isa => 'ArrayRef[Str]',
60             init_arg => undef,
61             lazy => 1,
62             default => sub {
63             my $self = shift;
64              
65             if (my $url = $self->repo_url)
66             {
67             $self->log_debug([ 'getting issue data for %s...', $url ]);
68             my ($owner_name, $repo_name) = $url =~ m{github\.com[:/]([^/]+)/([^/]+?)(?:/|\.git|$)};
69             return [ $owner_name, $repo_name ] if $owner_name and $repo_name;
70             }
71              
72             $self->log('failed to find a github repo in metadata');
73             [];
74             },
75             traits => ['Array'],
76             handles => { _github_owner_repo => 'elements' },
77             );
78              
79 3     3 0 2239879 sub mvp_aliases { +{ color => 'colour' } }
80              
81             # metaconfig is unimportant for this distribution since it does not alter the
82             # built distribution in any way
83             # around dump_config => sub...
84              
85             sub get_issues
86             {
87 5     5 0 25 my $self = shift;
88              
89 5         334 my $dist_name = $self->zilla->name;
90              
91 5         341 my @issues;
92              
93 5 100       352 if ($self->rt)
94             {
95 4         44 my %rt_data = $self->_rt_data_for_dist($dist_name);
96              
97             my $colour = $rt_data{open} ? 'bright_red'
98 4 50       43 : $rt_data{stalled} ? 'yellow'
    100          
99             : 'green';
100              
101             my @text = (
102             'Issues on RT (https://rt.cpan.org/Public/Dist/Display.html?Name=' . $dist_name . '):',
103 4   100     166 ' open: ' . ($rt_data{open} || 0) . ' stalled: ' . ($rt_data{stalled} || 0),
      100        
104             );
105              
106 4 50       267 @text = map { colored($_, $colour) } @text if $self->colour;
  0         0  
107 4         29 push @issues, @text;
108             }
109              
110 5 100 100     603 if ($self->github
111             and my ($owner_name, $repo_name) = $self->_github_owner_repo)
112             {
113 3         20 my $issue_count = $self->_github_issue_count($owner_name, $repo_name);
114 3 50       95 if (defined $issue_count)
115             {
116 3 50       16 my $colour = $issue_count ? 'bright_red' : 'green';
117              
118 3         19 my @text = (
119             'Issues on github (https://github.com/' . $owner_name . '/' . $repo_name . '):',
120             ' open: ' . $issue_count,
121             );
122              
123 3 50       134 @text = map { colored($_, $colour) } @text if $self->colour;
  0         0  
124 3         22 push @issues, @text;
125             }
126             }
127              
128 5         59 return @issues;
129             }
130              
131             sub before_release
132             {
133 3     3 0 948865 my $self = shift;
134              
135 3         45 $self->log($_) foreach $self->get_issues;
136             }
137              
138             sub _rt_data_for_dist
139             {
140 4     4   28 my ($self, $dist_name) = @_;
141              
142 4         57 my $json = $self->_rt_data_raw;
143 4 50       251 return if not $json;
144              
145 4         73 require JSON::MaybeXS; JSON::MaybeXS->VERSION('1.001000');
  4         300  
146 4         81 my $all_data = JSON::MaybeXS->new(utf8 => 0)->decode($json);
147 4 100       429 return if not $all_data->{$dist_name};
148              
149 3         9 my %rt_data;
150             $rt_data{open} = $all_data->{$dist_name}{counts}{active}
151 3         124 - $all_data->{$dist_name}{counts}{stalled};
152 3         17 $rt_data{stalled} = $all_data->{$dist_name}{counts}{stalled};
153 3         37 return %rt_data;
154             }
155              
156             sub _rt_data_raw
157             {
158 0     0     my $self = shift;
159              
160 0           $self->log_debug('fetching RT bug data...');
161 0           my $data = $self->_fetch('https://rt.cpan.org/Public/bugs-per-dist.json');
162 0 0         $self->log('could not fetch RT data?'), return if not $data;
163 0           return $data;
164             }
165              
166             sub _github_issue_count
167             {
168 0     0     my ($self, $owner_name, $repo_name) = @_;
169              
170 0           $self->log_debug('fetching github issues data...');
171              
172 0           my $json = $self->_fetch('https://api.github.com/repos/' . $owner_name . '/' . $repo_name);
173 0 0         $self->log('could not fetch github data?'), return if not $json;
174              
175 0           require JSON::MaybeXS; JSON::MaybeXS->VERSION('1.001000');
  0            
176 0           my $data = JSON::MaybeXS->new(utf8 => 0)->decode($json);
177 0           $data->{open_issues_count};
178             }
179              
180             sub _fetch
181             {
182 0     0     my ($self, $url) = @_;
183              
184 0           require HTTP::Tiny;
185 0           my $res = HTTP::Tiny->new->get($url);
186 0 0         return if not $res->{success};
187              
188 0           my $data = $res->{content};
189              
190 0           require HTTP::Headers;
191 0 0         if (my $charset = HTTP::Headers->new(%{ $res->{headers} })->content_type_charset)
  0            
192             {
193 0           $data = Encode::decode($charset, $data, Encode::FB_CROAK);
194             }
195              
196 0           return $data;
197             }
198              
199             __PACKAGE__->meta->make_immutable;
200              
201             __END__
202              
203             =pod
204              
205             =encoding UTF-8
206              
207             =head1 NAME
208              
209             Dist::Zilla::Plugin::CheckIssues - Retrieve count of outstanding RT and github issues for your distribution
210              
211             =head1 VERSION
212              
213             version 0.009
214              
215             =head1 SYNOPSIS
216              
217             In your F<dist.ini>:
218              
219             [CheckIssues]
220             rt = 1 ; default
221             github = 1 ; default
222             colour = 1 ; default
223              
224             [ConfirmRelease]
225              
226             =head1 DESCRIPTION
227              
228             This is a L<Dist::Zilla> plugin that retrieves the RT and/or github issue
229             counts for your distribution before release. Place it immediately before
230             C<[ConfirmRelease]> in your F<dist.ini> to give you an opportunity to abort the
231             release if you forgot to fix a bug or merge a pull request.
232              
233             =for Pod::Coverage mvp_aliases before_release get_issues
234              
235             =head1 CONFIGURATION OPTIONS
236              
237             =head2 C<rt>
238              
239             Checks your distribution's queue at L<https://rt.cpan.org/>. Defaults to true.
240             (You should leave this enabled even if you have your main issue list on github,
241             as sometimes tickets still end up on RT.)
242              
243             =head2 C<github>
244              
245             Checks the issue list on L<github|https://github.com> for your distribution; does
246             nothing if your distribution is not hosted on L<github|https://github.com>, as
247             listed in your distribution's metadata. Defaults to true.
248              
249             =head2 C<colour> or C<color>
250              
251             Uses L<Term::ANSIColor> to colour-code the results according to severity.
252             Defaults to true.
253              
254             =head2 C<repo_url>
255              
256             The URL of the github repository. This is fetched from the C<resources> field
257             in metadata, so it should not normally be specified manually.
258              
259             =head1 FUTURE FEATURES, MAYBE
260              
261             If I can find the right APIs to call, it would be nice to have a C<verbose>
262             option which fetches the actual titles of the open issues. Advice or patches welcome!
263              
264             Possibly other issue trackers? Does anyone even use any other issue trackers
265             anymore? :)
266              
267             =head1 ACKNOWLEDGEMENTS
268              
269             =for stopwords Ricardo Signes codereview
270              
271             Some code was liberally stolen from Ricardo Signes's
272             L<codereview tool|https://github.com/rjbs/misc/blob/master/code-review>.
273              
274             =head1 SEE ALSO
275              
276             =over 4
277              
278             =item *
279              
280             L<Dist::Zilla::Plugin::MetaResources> - manually add resource information (such as git repository) to metadata
281              
282             =item *
283              
284             L<Dist::Zilla::Plugin::GithubMeta> - automatically detect and add github repository information to metadata
285              
286             =item *
287              
288             L<Dist::Zilla::Plugin::AutoMetaResources> - configuration-based resource metadata provider
289              
290             =back
291              
292             =head1 SUPPORT
293              
294             Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Dist-Zilla-Plugin-CheckIssues>
295             (or L<bug-Dist-Zilla-Plugin-CheckIssues@rt.cpan.org|mailto:bug-Dist-Zilla-Plugin-CheckIssues@rt.cpan.org>).
296              
297             There is also a mailing list available for users of this distribution, at
298             L<http://dzil.org/#mailing-list>.
299              
300             There is also an irc channel available for users of this distribution, at
301             irc://irc.perl.org/#distzilla.
302              
303             I am also usually active on irc, as 'ether' at C<irc.perl.org>.
304              
305             =head1 AUTHOR
306              
307             Karen Etheridge <ether@cpan.org>
308              
309             =head1 COPYRIGHT AND LICENCE
310              
311             This software is copyright (c) 2014 by Karen Etheridge.
312              
313             This is free software; you can redistribute it and/or modify it under
314             the same terms as the Perl 5 programming language system itself.
315              
316             =cut