File Coverage

blib/lib/App/CISetup/Travis/ConfigFile.pm
Criterion Covered Total %
statement 169 189 89.4
branch 43 58 74.1
condition 25 44 56.8
subroutine 31 33 93.9
pod n/a
total 268 324 82.7


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