File Coverage

blib/lib/App/GhaInstall.pm
Criterion Covered Total %
statement 20 172 11.6
branch 0 110 0.0
condition 0 12 0.0
subroutine 7 21 33.3
pod 0 14 0.0
total 27 329 8.2


line stmt bran cond sub pod time code
1 1     1   578 use 5.006;
  1         3  
2 1     1   5 use strict;
  1         1  
  1         15  
3 1     1   4 use warnings;
  1         1  
  1         81  
4              
5             package App::GhaInstall;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.004';
9              
10             our $ALLOW_FAIL = 0;
11             our $DRY_RUN = 0;
12              
13             sub SHOULD_INSTALL_OPTIONAL_DEPS () {
14 1     1   5 no warnings;
  1         1  
  1         106  
15             $ENV{GHA_TESTING_COVER} =~ /^(true|1)$/i
16 0 0   0 0   or $ENV{GHA_INSTALL_OPTIONAL} =~ /^(true|1)$/i
17             }
18              
19             sub SHOULD_INSTALL_COVERAGE_DEPS () {
20 1     1   6 no warnings;
  1         1  
  1         72  
21 0     0 0   $ENV{GHA_TESTING_COVER} =~ /^(true|1)$/i
22             }
23              
24             sub SHOULD_INSTALL_GITHUB_DEPS () {
25 1     1   5 no warnings;
  1         2  
  1         1393  
26             !! $ENV{CI}
27 0     0 0   }
28              
29             my $installer;
30             sub INSTALLER () {
31 0 0   0 0   return $installer if defined $installer;
32 0 0         if ( $ENV{GHA_INSTALL_BACKEND} ) {
    0          
33 0           $installer = $ENV{GHA_INSTALL_BACKEND};
34             }
35             elsif ( $] lt '5.008001' ) {
36 0           $installer = 'cpan';
37             }
38             else {
39 0           my $output = `cpanm --version`;
40 0 0         if ( $output =~ /cpanminus/ ) {
41 0           $installer = 'cpanm';
42             }
43             else {
44 0           $output = `cpm --help`;
45 0 0         if ( $output =~ /install/ ) {
46 0           $installer = 'cpm';
47             }
48             else {
49 0           $installer = 'cpan';
50             }
51             }
52             }
53 0 0         ensure_configured_cpan() if $installer eq 'cpan';
54 0           return $installer;
55             }
56              
57             sub go {
58 0     0 0   shift;
59            
60 0           my @modules;
61            
62 0           foreach ( @_ ) {
63 0 0         if ( /--allow-fail/ ) {
    0          
    0          
    0          
    0          
64 0           $ALLOW_FAIL = 1;
65             }
66             elsif ( /--dry-run/ ) {
67 0           $DRY_RUN = 1;
68             }
69             elsif ( /--bootstrap/ ) {
70 0           install_module( __PACKAGE__ );
71 0           local $ALLOW_FAIL = 1;
72 0 0         install_module( 'CPAN' ) if INSTALLER eq 'cpan';
73             }
74             elsif ( /--configure/ ) {
75 0           install_configure_dependencies();
76             }
77             elsif ( /--auto/ ) {
78 0           install_dependencies();
79             }
80             else {
81 0           push @modules, $_;
82             }
83             }
84            
85 0 0         if ( @modules ) {
86 0           install_modules( @modules );
87             }
88            
89 0           return 0;
90             }
91              
92             sub slurp {
93 0     0 0   my $file = shift;
94 0 0         open my $fh, '<', $file
95             or die "$file exists but cannot be read\n";
96 0           local $/;
97 0           return <$fh>;
98             }
99              
100             sub read_json {
101 0     0 0   my $file = shift;
102 0 0         return unless -f $file;
103            
104 0           my $hash;
105            
106 0 0         if ( eval { require JSON::MaybeXS; 1 } ) {
  0 0          
  0            
107 0           my $json = 'JSON::MaybeXS'->new;
108 0           $hash = $json->decode( slurp($file) );
109             }
110 0           elsif ( eval { require JSON::PP; 1 } ) {
  0            
111 0           my $json = 'JSON::PP'->new;
112 0           $hash = $json->decode( slurp($file) );
113             }
114             else {
115             # Bundled version of JSON::Tiny
116 0           require App::GhaInstall::JSON;
117 0           $hash = JSON::Tiny::decode_json( slurp($file) );
118             }
119            
120 0 0         return unless ref($hash) eq 'HASH';
121            
122 0           $hash->{metatype} = 'JSON';
123 0           return $hash;
124             }
125              
126             sub read_yaml {
127 0     0 0   my $file = shift;
128 0 0         return unless -f $file;
129            
130 0           my $hash;
131            
132 0 0         if ( eval { require JSON::XS; 1 } ) {
  0            
  0            
133 0           $hash = YAML::XS::Load( slurp($file) );
134             }
135             else {
136             # Bundled version of YAML::Tiny
137 0           require App::GhaInstall::YAML;
138 0           $hash = YAML::Tiny::Load( slurp($file) );
139             }
140            
141 0 0         return unless ref($hash) eq 'HASH';
142            
143 0           $hash->{metatype} = 'YAML';
144 0           return $hash;
145             }
146              
147             sub install_configure_dependencies {
148 0 0 0 0 0   my $meta =
149             read_json('META.json') || read_yaml('META.yml')
150             or die("Cannot read META.json or META.yml");
151            
152 0           my ( @need, @want );
153            
154 0 0         if ( $meta->{metatype} eq 'JSON' ) {
155 0           for my $phase ( qw( configure build ) ) {
156 0 0         push @need, keys %{ $meta->{prereqs}{$phase}{requires} or {} };
  0            
157 0 0         push @want, keys %{ $meta->{prereqs}{$phase}{recommends} or {} };
  0            
158 0 0         push @want, keys %{ $meta->{prereqs}{$phase}{suggests} or {} };
  0            
159             }
160             }
161             else {
162 0 0         push @need, keys %{ $meta->{configure_requires} or {} };
  0            
163 0 0         push @need, keys %{ $meta->{build_requires} or {} };
  0            
164             }
165            
166 0 0         if ( @need ) {
167 0           install_modules( @need );
168             }
169            
170 0 0 0       if ( @want and SHOULD_INSTALL_OPTIONAL_DEPS ) {
171 0           local $ALLOW_FAIL = 1;
172 0           install_modules( @want );
173             }
174            
175 0           return;
176             }
177              
178             sub install_dependencies {
179 0 0 0 0 0   my $meta =
180             read_json('MYMETA.json') || read_yaml('MYMETA.yml') || read_json('META.json') || read_yaml('META.yml')
181             or die("Cannot read MYMETA.json or MYMETA.yml");
182            
183 0           my ( @need, @want );
184            
185 0 0         if ( $meta->{metatype} eq 'JSON' ) {
186 0           for my $phase ( qw( configure build runtime test ) ) {
187 0 0         push @need, keys %{ $meta->{prereqs}{$phase}{requires} or {} };
  0            
188 0 0         push @want, keys %{ $meta->{prereqs}{$phase}{recommends} or {} };
  0            
189 0 0         push @want, keys %{ $meta->{prereqs}{$phase}{suggests} or {} };
  0            
190             }
191             }
192             else {
193 0 0         push @need, keys %{ $meta->{configure_requires} or {} };
  0            
194 0 0         push @need, keys %{ $meta->{build_requires} or {} };
  0            
195 0 0         push @need, keys %{ $meta->{requires} or {} };
  0            
196 0 0         push @need, keys %{ $meta->{test_requires} or {} };
  0            
197 0 0         push @want, keys %{ $meta->{recommends} or {} };
  0            
198             }
199            
200 0 0         if ( SHOULD_INSTALL_GITHUB_DEPS ) {
201 0           push @need, 'App::GhaProve';
202             }
203            
204 0 0         if ( SHOULD_INSTALL_COVERAGE_DEPS ) {
205 0           push @need, 'Devel::Cover';
206 0           push @need, 'Devel::Cover::Report::Coveralls';
207 0           push @need, 'Devel::Cover::Report::Codecov';
208             }
209            
210 0 0         if ( @need ) {
211 0           install_modules( @need );
212             }
213            
214 0 0 0       if ( @want and SHOULD_INSTALL_OPTIONAL_DEPS ) {
215 0           local $ALLOW_FAIL = 1;
216 0           install_modules( @want );
217             }
218            
219 0           return;
220             }
221              
222             sub maybe_die {
223 0     0 0   my $exit = shift;
224 0 0         if ( $exit ) {
225 0 0         if ( $ALLOW_FAIL ) {
226 0           warn "Failed, but continuing anyway...\n";
227             }
228             else {
229 0           die "Failed; stopping!\n";
230             }
231             }
232 0           return;
233             }
234              
235             sub install_modules {
236 0     0 0   my @modules = grep $_ ne 'perl', @_;
237            
238 0 0         if ( $DRY_RUN ) {
239 0           warn "install: $_\n" for @modules;
240 0           return;
241             }
242              
243 0 0         if ( INSTALLER eq 'cpanm' ) {
244 0           return maybe_die system 'cpanm', '-n', @modules;
245             }
246            
247 0 0         if ( INSTALLER eq 'cpm' ) {
248 0           return maybe_die system 'cpm', 'install', '-g', @modules;
249             }
250              
251 0           install_module($_) for @_;
252             }
253              
254             sub install_module {
255 0     0 0   my $module = shift;
256            
257 0 0         if ( $DRY_RUN ) {
258 0           warn "install: $module\n";
259 0           return;
260             }
261            
262 0 0         if ( INSTALLER eq 'cpanm' ) {
263 0           return maybe_die system 'cpanm', '-n', $module;
264             }
265            
266 0 0         if ( INSTALLER eq 'cpm' ) {
267 0           return maybe_die system 'cpm', 'install', '-g', $module;
268             }
269            
270 0 0         if ( INSTALLER eq 'cpan' ) {
271 0           my @notest = grep /^notest$/, @CPAN::EXPORT;
272 0           'CPAN::Shell'->rematein( @notest, 'install', $module );
273 0 0         if ( 'CPAN::Shell'->can('mandatory_dist_failed') ) {
274 0           return scalar 'CPAN::Shell'->mandatory_dist_failed();
275             }
276 0           return;
277             }
278            
279 0           return maybe_die system( split(/ /, INSTALLER), $module );
280             }
281              
282             sub ensure_configured_cpan {
283 1     1   8 use Cwd ();
  1         1  
  1         228  
284 0     0 0   require CPAN;
285 0           eval { require CPAN::Shell };
  0            
286              
287 0           my $home = $ENV{HOME};
288 0           my $cwd = Cwd::cwd;
289 0           $CPAN::Config = {
290             'applypatch' => q[],
291             'auto_commit' => q[0],
292             'build_cache' => q[100],
293             'build_dir' => qq[$home/.cpan/build],
294             'build_dir_reuse' => q[0],
295             'build_requires_install_policy' => q[yes],
296             'bzip2' => q[/bin/bzip2],
297             'cache_metadata' => q[1],
298             'check_sigs' => q[0],
299             'colorize_output' => q[0],
300             'commandnumber_in_prompt' => q[1],
301             'connect_to_internet_ok' => q[1],
302             'cpan_home' => qq[$home/.cpan],
303             'ftp_passive' => q[1],
304             'ftp_proxy' => q[],
305             'getcwd' => q[cwd],
306             'gpg' => q[/usr/bin/gpg],
307             'gzip' => q[/bin/gzip],
308             'halt_on_failure' => q[0],
309             'histfile' => qq[$home/.cpan/histfile],
310             'histsize' => q[100],
311             'http_proxy' => q[],
312             'inactivity_timeout' => q[0],
313             'index_expire' => q[1],
314             'inhibit_startup_message' => q[0],
315             'keep_source_where' => qq[$home/.cpan/sources],
316             'load_module_verbosity' => q[none],
317             'make' => q[/usr/bin/make],
318             'make_arg' => q[],
319             'make_install_arg' => q[],
320             'make_install_make_command' => q[/usr/bin/make],
321             'makepl_arg' => q[INSTALLDIRS=site],
322             'mbuild_arg' => q[],
323             'mbuild_install_arg' => q[],
324             'mbuild_install_build_command' => q[./Build],
325             'mbuildpl_arg' => q[--installdirs site],
326             'no_proxy' => q[],
327             'pager' => q[/usr/bin/less],
328             'patch' => q[/usr/bin/patch],
329             'perl5lib_verbosity' => q[none],
330             'prefer_external_tar' => q[1],
331             'prefer_installer' => q[MB],
332             'prefs_dir' => qq[$home/.cpan/prefs],
333             'prerequisites_policy' => q[follow],
334             'scan_cache' => q[atstart],
335             'shell' => q[/bin/sh],
336             'show_unparsable_versions' => q[0],
337             'show_upload_date' => q[0],
338             'show_zero_versions' => q[0],
339             'tar' => q[/bin/tar],
340             'tar_verbosity' => q[none],
341             'term_is_latin' => q[1],
342             'term_ornaments' => q[1],
343             'test_report' => q[0],
344             'trust_test_report_history' => q[0],
345             'unzip' => q[/usr/bin/unzip],
346             'urllist' => [q[http://cpan.mirrors.uk2.net/], q[http://cpan.singletasker.co.uk/], q[http://cpan.cpantesters.org/]],
347             'use_sqlite' => q[0],
348             'version_timeout' => q[15],
349             'wget' => q[/usr/bin/wget],
350             'yaml_load_code' => q[0],
351             'yaml_module' => q[YAML],
352             };
353             }
354              
355             1;
356              
357             __END__