File Coverage

blib/lib/App/GhaInstall.pm
Criterion Covered Total %
statement 11 143 7.6
branch 0 94 0.0
condition 0 12 0.0
subroutine 4 17 23.5
pod 0 13 0.0
total 15 279 5.3


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