File Coverage

blib/lib/GitHub/MergeVelocity.pm
Criterion Covered Total %
statement 85 105 80.9
branch 8 20 40.0
condition 1 3 33.3
subroutine 21 22 95.4
pod 0 2 0.0
total 115 152 75.6


line stmt bran cond sub pod time code
1              
2             use Moo 1.007000;
3 1     1   150439  
  1         11324  
  1         7  
4             our $VERSION = '0.000009';
5              
6             use CLDR::Number::Format::Percent ();
7 1     1   2009 use File::HomeDir ();
  1         202689  
  1         35  
8 1     1   1660 use GitHub::MergeVelocity::Repository ();
  1         5649  
  1         30  
9 1     1   491 use Module::Runtime qw( require_module use_module );
  1         5  
  1         34  
10 1     1   7 use MooX::HandlesVia;
  1         2  
  1         10  
11 1     1   469 use MooX::Options;
  1         642  
  1         5  
12 1     1   541 use MooX::StrictConstructor;
  1         1619  
  1         5  
13 1     1   67411 use Path::Tiny qw( path );
  1         2  
  1         8  
14 1     1   1461 use Text::SimpleTable::AutoWidth ();
  1         10187  
  1         48  
15 1     1   428 use Pithub::PullRequests ();
  1         6530  
  1         32  
16 1     1   398 use Pithub::Repos ();
  1         72217  
  1         25  
17 1     1   417 use Types::Standard qw( ArrayRef Bool HashRef InstanceOf Int Str );
  1         29332  
  1         32  
18 1     1   8 use WWW::Mechanize::GZip ();
  1         2  
  1         13  
19 1     1   1505 use List::Util qw( uniq );
  1         85198  
  1         33  
20 1     1   11  
  1         4  
  1         1039  
21             option debug_useragent => (
22             is => 'ro',
23             isa => Int,
24             format => 'i',
25             default => 0,
26             documentation => 'Print a _lot_ of debugging info about LWP requests',
27             );
28              
29             my $token_help = <<'EOF';
30             https://help.github.com/articles/creating-an-access-token-for-command-line-use for instructions on how to get your own GitHub access token
31             EOF
32              
33             option cache_requests => (
34             is => 'ro',
35             isa => Bool,
36             documentation => 'Try to cache GET requests',
37             );
38              
39             option github_token => (
40             is => 'ro',
41             isa => Str,
42             required => 0,
43             format => 's',
44             documentation => $token_help,
45             );
46              
47             option github_user => (
48             is => 'ro',
49             isa => Str,
50             required => 0,
51             format => 's',
52             documentation => 'The username of your GitHub account',
53             );
54              
55             option url => (
56             is => 'ro',
57             isa => ArrayRef,
58             format => 's@',
59             required => 0,
60             documentation =>
61             'Full Github repo url or shorthand of username/repository. You can pass multiple url args.',
62             );
63              
64             option org => (
65             is => 'ro',
66             isa => ArrayRef,
67             format => 's@',
68             required => 0,
69             documentation => 'An organization. You can pass multiple url args.',
70             );
71              
72             has _report => (
73             is => 'ro',
74             isa => HashRef,
75             handles_via => 'Hash',
76             init_arg => undef,
77             handles => { _repository_for_url => 'get', _report_urls => 'keys', },
78             lazy => 1,
79             builder => '_build_report',
80             );
81              
82             has _github_client => (
83             is => 'ro',
84             isa => InstanceOf ['Pithub::PullRequests'],
85             lazy => 1,
86             builder => '_build_github_client'
87             );
88              
89             has _mech => (
90             is => 'ro',
91             isa => InstanceOf ['LWP::UserAgent'],
92             lazy => 1,
93             builder => '_build_mech',
94             );
95              
96             has _percent_formatter => (
97             is => 'ro',
98             isa => InstanceOf ['CLDR::Number::Format::Percent'],
99             handles => { '_format_percent' => 'format' },
100             lazy => 1,
101             default => sub { CLDR::Number::Format::Percent->new( locale => 'en' ) },
102             );
103              
104             my $self = shift;
105             return Pithub::PullRequests->new(
106 1     1   14 $self->cache_requests
107 1 50 33     42 || $self->debug_useragent ? ( ua => $self->_mech ) : (),
    50          
    50          
108             $self->github_user ? ( user => $self->github_user ) : (),
109             $self->github_token ? ( token => $self->github_token ) : (),
110             );
111             }
112              
113             my $self = shift;
114              
115             my $mech;
116 0     0   0  
117             if ( $self->cache_requests ) {
118 0         0 my $dir = path( File::HomeDir->my_home );
119             $dir->child('.github-mergevelocity-cache')->mkpath;
120 0 0       0  
121 0         0 require_module('CHI');
122 0         0 $mech = use_module( 'WWW::Mechanize::Cached', 1.45 )->new(
123             cache => CHI->new(
124 0         0 driver => 'File',
125 0         0 root_dir => $dir->stringify,
126             )
127             );
128             }
129             else {
130             $mech = WWW::Mechanize::GZip->new;
131             }
132             if ( $self->debug_useragent ) {
133 0         0 use_module( 'LWP::ConsoleLogger::Easy', 0.000013 );
134             LWP::ConsoleLogger::Easy::debug_ua( $mech, $self->debug_useragent );
135 0 0       0 }
136 0         0 return $mech;
137 0         0 }
138              
139 0         0 my $self = shift;
140              
141             my %report;
142              
143 1     1   43 # Where we put all urls (from --url AND/OR --org)
144             my @urls = ();
145 1         2  
146             # Where will go urls found from --org
147             my @org_urls = ();
148 1         3 if ( $self->org ) {
149             foreach my $org ( @{ $self->org } ) {
150             my $repos = Pithub::Repos->new;
151 1         2 my $result = $repos->list( org => $org );
152 1 50       7  
153 0         0 $result->auto_pagination(1);
  0         0  
154 0         0  
155 0         0 while ( my $row = $result->next ) {
156             push @org_urls, $row->{full_name};
157 0         0 }
158             }
159 0         0 }
160 0         0  
161             # Merge --org urls with --url urls and clean dups
162             push @urls, @org_urls;
163             if ( $self->url ) {
164             push @urls, @{ $self->url };
165             }
166 1         3 @urls = uniq @urls;
167 1 50       5  
168 1         2 foreach my $url (@urls) {
  1         4  
169             my $repo = GitHub::MergeVelocity::Repository->new(
170 1         6 github_client => $self->_github_client,
171             url => $url,
172 1         3 );
173 1         22 $report{$url} = $repo;
174             }
175              
176             return \%report;
177 1         14398 }
178              
179             # workaround for init_arg being ignored
180 1         18 # https://rt.cpan.org/Ticket/Display.html?id=97849
181              
182             my $self = shift;
183             return $self->_report;
184             }
185              
186             my $self = shift;
187 1     1 0 5922  
188 1         21 my $table = Text::SimpleTable::AutoWidth->new;
189             my @cols = (
190             'user', 'repo', 'velocity', 'PRs',
191             'merged', 'merge days', 'closed', 'close days',
192 1     1 0 587 'open', 'open days',
193             );
194 1         9 $table->captions( \@cols );
195 1         1180  
196             my @repos = map { $self->_repository_for_url($_) } $self->_report_urls;
197              
198             return unless @repos;
199              
200 1         6 foreach my $repository (
201             sort { $b->report->average_velocity <=> $a->report->average_velocity }
202 1         19 @repos
  1         93  
203             )
204 1 50       116 {
205             my $report = $repository->report;
206 1         4 $table->row(
207 0         0 $repository->user,
208             $repository->name,
209             $report->average_velocity,
210             $report->pull_request_count,
211 1         18 map { $self->_columns_for_state( $report, $_ ) }
212             ( 'merged', 'closed', 'open' ),
213             );
214             }
215              
216             binmode( STDOUT, ':encoding(UTF-8)' );
217 1         5633 print $table->draw;
  3         211  
218             return;
219             }
220              
221             my $self = shift;
222 1     1   13 my $report = shift;
  1         3  
  1         11  
  1         108  
223 1         1572 my $state = shift;
224 1         1825 my $age = $state . '_age';
225              
226             return (
227             $report->$state
228 3     3   7 ? sprintf( '%s (%i)',
229 3         6 $self->_format_percent( $report->percentage_in_state($state) ),
230 3         7 $report->$state )
231 3         9 : 0,
232             $report->$age ? sprintf( '%s/PR (%i)',
233             $report->average_age_for_state($state),
234 3 50       23 $report->$age ) : 0,
    50          
235             );
236             }
237              
238             1;
239              
240             =pod
241              
242             =encoding UTF-8
243              
244             =head1 NAME
245              
246             GitHub::MergeVelocity - Determine how quickly your pull request might get merged
247              
248             =head1 VERSION
249              
250             version 0.000009
251              
252             =head1 SYNOPSIS
253              
254             use strict;
255             use warnings;
256              
257             use GitHub::MergeVelocity;
258              
259             my $velocity = GitHub::MergeVelocity->new(
260             url => [
261             'https://github.com/neilbowers/PAUSE-Permissions',
262             'https://github.com/oalders/html-restrict',
263             ]
264             );
265              
266             my $report = $velocity->report;
267              
268             $velocity->print_report; # prints a tabular report
269              
270             =head1 CAVEATS
271              
272             This module cannot (yet) distinguish between pull requests which were closed
273             because they were rejected and pull requests which were closed because the
274             patches were applied outside of GitHub's merge mechanism.
275              
276             =head1 AUTHOR
277              
278             Olaf Alders <olaf@wundercounter.com>
279              
280             =head1 COPYRIGHT AND LICENSE
281              
282             This software is copyright (c) 2015 by Olaf Alders.
283              
284             This is free software; you can redistribute it and/or modify it under
285             the same terms as the Perl 5 programming language system itself.
286              
287             =cut
288              
289              
290             # ABSTRACT: Determine how quickly your pull request might get merged
291