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