File Coverage

blib/lib/App/CISetup/Travis/ConfigFile.pm
Criterion Covered Total %
statement 169 189 89.4
branch 42 56 75.0
condition 25 44 56.8
subroutine 31 33 93.9
pod n/a
total 267 322 82.9


line stmt bran cond sub pod time code
1             package App::CISetup::Travis::ConfigFile;
2              
3 1     1   51366 use strict;
  1         3  
  1         30  
4 1     1   6 use warnings;
  1         2  
  1         24  
5 1     1   7 use namespace::autoclean;
  1         3  
  1         14  
6 1     1   73 use autodie qw( :all );
  1         2  
  1         11  
7              
8             our $VERSION = '0.17';
9              
10 1     1   6179 use App::CISetup::Types qw( Bool File Str );
  1         3  
  1         19  
11 1     1   9128 use File::pushd;
  1         20113  
  1         57  
12 1     1   485 use File::Which qw( which );
  1         981  
  1         55  
13 1     1   545 use IPC::Run3 qw( run3 );
  1         3475  
  1         63  
14 1     1   640 use List::AllUtils qw( first first_index uniq );
  1         3824  
  1         87  
15 1     1   742 use Path::Iterator::Rule;
  1         12419  
  1         40  
16 1     1   8 use Try::Tiny;
  1         1  
  1         57  
17 1     1   8 use YAML qw( Dump );
  1         4  
  1         50  
18              
19 1     1   7 use Moose;
  1         3  
  1         11  
20 1     1   8750 use MooseX::StrictConstructor;
  1         17065  
  1         5  
21              
22             has email_address => (
23             is => 'ro',
24             isa => Str, # todo, better type
25             predicate => 'has_email_address',
26             );
27              
28             has force_threaded_perls => (
29             is => 'ro',
30             isa => Bool,
31             default => 0,
32             );
33              
34             has perl_caching => (
35             is => 'ro',
36             isa => Bool,
37             default => 1,
38             );
39              
40             has github_user => (
41             is => 'ro',
42             isa => Str,
43             predicate => 'has_github_user',
44             );
45              
46             has slack_key => (
47             is => 'ro',
48             isa => Str,
49             predicate => 'has_slack_key',
50             );
51              
52             with 'App::CISetup::Role::ConfigFile';
53              
54             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
55             sub _create_config {
56 5     5   14 my $self = shift;
57              
58 5         33 return $self->_update_config( { language => 'perl' }, 1 );
59             }
60              
61             sub _update_config {
62 10     10   26 my $self = shift;
63 10         18 my $travis = shift;
64 10         16 my $create = shift;
65              
66 10         47 $self->_maybe_update_travis_perl_usage( $travis, $create );
67 10         33 $self->_maybe_remove_sudo($travis);
68 10         34 $self->_update_packages($travis);
69 10         41 $self->_update_coverity_email($travis);
70 10         40 $self->_update_notifications($travis);
71              
72 10         44 return $travis;
73             }
74             ## use critic
75              
76             sub _maybe_update_travis_perl_usage {
77 10     10   23 my $self = shift;
78 10         16 my $travis = shift;
79 10         18 my $create = shift;
80              
81             return
82             unless $create
83             || ( $travis->{before_install}
84 5         80 && grep {/perl-travis-helper|travis-perl/}
85 10 50 33     47 @{ $travis->{before_install} } );
  5   66     20  
86              
87 10         50 $self->_maybe_add_cache_block($travis);
88 10         48 $self->_fixup_helpers_usage($travis);
89 10         50 $self->_rewrite_perl_block($travis);
90 10         41 $self->_update_perl_matrix($travis);
91 10         31 $self->_update_env_vars($travis);
92              
93 10         18 return;
94             }
95              
96             sub _maybe_add_cache_block {
97 10     10   18 my $self = shift;
98 10         19 my $travis = shift;
99              
100 10 100       354 return unless $self->perl_caching;
101 9 100       37 return if exists $travis->{cache};
102              
103 8         39 $travis->{cache} = { directories => ['$HOME/perl5'] };
104              
105 8         16 return;
106             }
107              
108             sub _fixup_helpers_usage {
109 10     10   19 my $self = shift;
110 10         23 my $travis = shift;
111              
112 10 50 33     25 if (
      0        
      33        
      33        
113 10   50     111 ( @{ $travis->{script} // [] } && @{ $travis->{script} } > 3 )
  0         0  
114             || (
115             $travis->{install}
116 0         0 && ( grep { !/cpan-install/ } @{ $travis->{install} }
117             || @{ $travis->{install} } > 2 )
118             )
119             ) {
120              
121             my $i = (
122 0     0   0 first_index {/travis-perl|haarg/}
123 0   0     0 @{ $travis->{before_install} }
  0         0  
124             ) // 0;
125 0         0 $travis->{before_install}->[$i]
126             = 'git clone git://github.com/travis-perl/helpers ~/travis-perl-helpers';
127 0         0 $travis->{before_install}->[ $i + 1 ]
128             = 'source ~/travis-perl-helpers/init';
129             }
130             else {
131 10         20 delete $travis->{install};
132 10         23 delete $travis->{script};
133              
134 10   100     43 $travis->{before_install} //= [];
135 5     5   43 my $i = first_index {/travis-perl|haarg/}
136 10         53 @{ $travis->{before_install} };
  10         68  
137 10 100       48 $i = 0 if $i < 0;
138              
139 10         23 my $auto = 'eval $(curl https://travis-perl.github.io/init) --auto';
140 10 100       320 $auto .= ' --always-upgrade-modules' if $self->perl_caching;
141              
142 10         29 $travis->{before_install}[$i] = $auto;
143 0         0 splice( @{ $travis->{before_install} }, $i + 1, 0 )
144 10 50       18 if @{ $travis->{before_install} } > 1;
  10         37  
145             }
146              
147 10         24 return;
148             }
149              
150             my @Perls = qw(
151             blead
152             dev
153             5.28
154             5.26
155             5.24
156             5.22
157             5.20
158             5.18
159             5.16
160             5.14
161             5.12
162             5.10
163             5.8
164             );
165              
166             # XXX - if a build is intentionally excluding Perls besides 5.8 this will add
167             # those Perls back. Not sure how best to deal with this. We want to test on
168             # all Perls for most modules, and any manually generated file might forget to
169             # include some of them.
170             sub _rewrite_perl_block {
171 10     10   29 my $self = shift;
172 10         26 my $travis = shift;
173              
174 10         60 my @perls = @Perls;
175 10         42 for my $perl (qw( 5.8 5.10 5.12 )) {
176             pop @perls
177 30 50       46 unless grep {/\Q$perl/} @{ $travis->{perl} };
  42         239  
  30         82  
178             }
179              
180 10         102 my $has_xs
181             = defined Path::Iterator::Rule->new->file->name(qr/\.xs/)
182             ->iter( $self->file->parent )->();
183              
184 10 100 100     7383 if ( $self->force_threaded_perls || $has_xs ) {
185 2         7 $travis->{perl} = [ map { ( $_, $_ . '-thr' ) } @perls ];
  20         46  
186             }
187             else {
188 8         31 $travis->{perl} = \@perls;
189             }
190              
191 10         33 return;
192             }
193              
194             sub _update_perl_matrix {
195 10     10   25 my $self = shift;
196 10         20 my $travis = shift;
197              
198 10         26 my @bleads = 'blead';
199             push @bleads, 'blead-thr'
200 10 100       19 if grep { $_ eq 'blead-thr' } @{ $travis->{perl} };
  120         209  
  10         32  
201              
202 10     30   75 my $latest = first {/^5/} @Perls;
  30         74  
203 10   100     32 my @include = @{ $travis->{matrix}{include} // [] };
  10         63  
204             push @include, {
205             perl => $latest,
206             env => 'COVERAGE=1',
207             }
208 10 50       94 unless grep { $_->{perl} eq $latest && $_->{env} eq 'COVERAGE=1' }
  1 100       11  
209             @include;
210              
211 10   100     25 my @allow_failures = @{ $travis->{matrix}{allow_failures} // [] };
  10         99  
212 10         28 for my $blead (@bleads) {
213             push @allow_failures, { perl => $blead }
214 12 100       47 unless grep { $_->{perl} eq $blead } @allow_failures;
  3         16  
215             }
216              
217             $travis->{matrix} = {
218 10         57 fast_finish => 1,
219             include => \@include,
220             allow_failures => \@allow_failures,
221             };
222              
223 10         29 return;
224             }
225              
226             sub _update_env_vars {
227 10     10   23 my $self = shift;
228 10         18 my $travis = shift;
229              
230 10   100     50 $travis->{env} //= {};
231             $travis->{env}{global} = [
232             uniq(
233 10   100     22 sort @{ $travis->{env}{global} // [] },
  10         134  
234             qw(
235             RELEASE_TESTING=1
236             AUTHOR_TESTING=1
237             ),
238             )
239             ];
240              
241 10         34 return;
242             }
243              
244             sub _maybe_remove_sudo {
245 10     10   22 my $self = shift;
246 10         22 my $travis = shift;
247              
248 10         25 delete $travis->{sudo};
249              
250 10         18 return;
251             }
252              
253             sub _update_packages {
254 10     10   23 my $self = shift;
255 10         20 my $travis = shift;
256              
257             my @addons
258             = $travis->{addons}
259             && $travis->{addons}{apt} && $travis->{addons}{apt}{packages}
260 10 100 66     52 ? @{ $travis->{addons}{apt}{packages} }
  1         4  
261             : ();
262             push @addons, qw( aspell aspell-en )
263 10 50       47 if $travis->{perl};
264 10 50       80 $travis->{addons}{apt}{packages} = [ sort { $a cmp $b } uniq(@addons) ]
  10         52  
265             if @addons;
266              
267 10         28 return;
268             }
269              
270             sub _update_coverity_email {
271 10     10   20 my $self = shift;
272 10         17 my $travis = shift;
273              
274 10 100       420 return unless $self->has_email_address;
275 3 100 66     25 return unless $travis->{addons} && $travis->{addons}{coverity_scan};
276             $travis->{addons}{coverity_scan}{notification_email}
277 1         33 = $self->email_address;
278             }
279              
280             sub _update_notifications {
281 10     10   19 my $self = shift;
282 10         16 my $travis = shift;
283              
284 10 100       362 if ( $self->has_email_address ) {
285             $travis->{notifications}{email} = {
286 3         93 recipients => [ $self->email_address ],
287             on_success => 'change',
288             on_failure => 'always',
289             };
290             }
291              
292 10 50 33     344 if ( $self->has_slack_key && $self->has_github_user ) {
293 0         0 my $slack = $travis->{notifications}{slack}{rooms}{secure};
294              
295             # travis encrypt will make a new encrypted version every time it's given
296             # the same input so we don't want to run it unless we have to, otherwise
297             # we end up with pointless updates.
298 0 0       0 unless ($slack) {
299 0         0 my $pushed = pushd( $self->file->parent );
300 0         0 my $stdout;
301             my $stderr;
302              
303 0 0       0 my $exe = which('travis')
304             or die 'Cannot find a travis command in the PATH';
305 0         0 $self->_run3(
306             [
307             $exe, 'encrypt', '--no-interactive',
308             '-R',
309             $self->github_user . '/' . $self->file->parent->basename,
310             $self->slack_key
311             ],
312             \undef,
313             \$stdout,
314             \$stderr,
315             );
316 0 0       0 die $stderr if $stderr;
317 0         0 $slack = $stdout =~ s/^\"|\"$//gr;
318             }
319              
320             $travis->{notifications}{slack} = {
321 0         0 rooms => { secure => $slack },
322             };
323             }
324              
325 10         21 return;
326             }
327              
328             # This is broken out so we can replace it in test code.
329             sub _run3 {
330 0     0   0 shift;
331 0         0 run3(@_);
332 0         0 return;
333             }
334              
335             my @BlocksOrder = qw(
336             sudo
337             dist
338             addons
339             language
340             compiler
341             go
342             jdk
343             perl
344             php
345             python
346             cache
347             solution
348             matrix
349             fast_finish
350             env
351             branches
352             services
353             before_install
354             install
355             before_script
356             script
357             after_script
358             after_success
359             after_failure
360             notifications
361             );
362              
363             my %KnownBlocks = map { $_ => 1 } @BlocksOrder;
364              
365             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
366             sub _fix_up_yaml {
367 20     20   45 my $self = shift;
368 20         32 my $yaml = shift;
369              
370 20         99 $yaml =~ s/sudo: 0/sudo: false/g;
371              
372 20         88 return $self->_reorder_yaml_blocks( $yaml, \@BlocksOrder );
373             }
374              
375             sub _reorder_addons_block {
376 20     20   41 my $self = shift;
377 20         35 my $block = shift;
378              
379 20 100       104 return $block unless $block =~ /coverity_scan:\n(.+)(?=\S|\z)/ms;
380              
381 2         5 my %chunks;
382 2         10 for my $line ( split /\n/, $1 ) {
383 2         11 my ($name) = $line =~ / +([^:]+):/;
384 2         9 $chunks{$name} = $line;
385             }
386              
387 2         8 my $reordered = join q{}, map {"$chunks{$_}\n"}
388 2         7 grep { $chunks{$_} }
  14         25  
389             qw(
390             project
391             description
392             name
393             notification_email
394             build_command_prepend
395             build_command
396             branch_pattern
397             );
398              
399 2         24 return $block
400             =~ s/coverity_scan:\n.+(?=\S|\z)/coverity_scan:\n$reordered/msr;
401             }
402              
403             sub _cisetup_flags {
404 10     10   21 my $self = shift;
405              
406 10 100       416 my %flags = (
    100          
407             force_threaded_perls => $self->force_threaded_perls ? 1 : 0,
408             perl_caching => $self->perl_caching ? 1 : 0,
409             );
410              
411 10 100       363 $flags{email_address} = $self->email_address
412             if $self->has_email_address;
413 10 100       354 $flags{github_user} = $self->github_user
414             if $self->has_github_user;
415              
416 10         45 return \%flags;
417             }
418             ## use critic
419              
420             __PACKAGE__->meta->make_immutable;
421              
422             1;