File Coverage

blib/lib/LedgerSMB/Installer/Configuration.pm
Criterion Covered Total %
statement 89 144 61.8
branch 14 46 30.4
condition 8 22 36.3
subroutine 22 27 81.4
pod 0 12 0.0
total 133 251 52.9


line stmt bran cond sub pod time code
1             package LedgerSMB::Installer::Configuration v0.999.11;
2              
3 2     2   360506 use v5.20;
  2         8  
4 2     2   10 use experimental qw(signatures);
  2         4  
  2         21  
5              
6 2     2   364 use Cwd qw( getcwd );
  2         5  
  2         135  
7 2     2   10 use File::Spec;
  2         3  
  2         44  
8 2     2   9 use Symbol;
  2         4  
  2         154  
9              
10              
11 2     2   11 use HTTP::Tiny;
  2         3  
  2         54  
12 2     2   364 use Log::Any qw($log);
  2         9280  
  2         20  
13              
14             my $http = HTTP::Tiny->new;
15              
16              
17 8     8 0 187205 sub new( $class, %args ) {
  8         19  
  8         20  
  8         14  
18             return bless {
19             # initialization options
20             _assume_yes => $args{assume_yes} // 0,
21             _installpath => $args{installpath} // 'ledgersmb',
22             _locallib => $args{locallib} // 'local',
23             _loglevel => $args{loglevel} // 'info',
24             _prep_env => $args{prepare_env},
25             _sys_pkgs => $args{sys_pkgs},
26             _verify_sig => $args{verify_sig} // 1,
27             _version => $args{version},
28             _uninstall_env => $args{uninstall_env},
29              
30             # internal state
31 8   50     219 _deps => undef,
      100        
      100        
      50        
      50        
32             _cleanup_pkgs => [],
33             }, $class;
34             }
35              
36 4     4 0 12 sub dependency_url($self, $distro, $id) {
  4         8  
  4         7  
  4         34  
  4         9  
37 4         13 return "https://download.ledgersmb.org/f/dependencies/$distro/$id.json";
38             }
39              
40 0     0 0 0 sub have_deps($self) {
  0         0  
  0         0  
41             return (defined $self->{_deps}
42             and defined $self->{_deps}->{packages}
43 0   0     0 and $self->{_deps}->{packages}->@*);
44             }
45              
46 3     3 0 4050 sub retrieve_precomputed_deps($self, $name, $id) {
  3         7  
  3         18  
  3         7  
  3         5  
47 3 50 33     19 return unless $name and $id;
48              
49 3         13 my $url = $self->dependency_url($name, $id);
50              
51 3         34 $log->info( "Retrieving dependency listing from $url" );
52 3         21 my $r = $http->get( $url );
53 3         24 my $pkgs;
54 3 100       15 if ($r->{success}) {
    100          
55 1         5 $self->{_deps} = JSON::PP->new->utf8->decode( $r->{content} );
56 1         1304 $pkgs = $self->{_deps}->{packages};
57             }
58             elsif ($r->{status} == 599) {
59             die $log->fatal(
60             'Error trying to retrieve precomputed dependencies: ' . $r->{content}
61 1         8 );
62             }
63 2         5 $self->{_deps_retrieved} = 1;
64 2         11 return ($self->{_deps}->{packages}, $self->{_deps}->{modules});
65             }
66              
67 2     2 0 35 sub mark_pkgs_for_cleanup($self, $pkgs) {
  2         4  
  2         5  
  2         4  
68 2         8 push $self->{_cleanup_pkgs}->@*, $pkgs->@*;
69             }
70              
71 1     1 0 6 sub pkgs_for_cleanup($self) {
  1         3  
  1         2  
72 1         9 return $self->{_cleanup_pkgs}->@*;
73             }
74              
75 2     2 0 51 sub normalize_paths($self) {
  2         5  
  2         3  
76 2         7 my $installpath = $self->installpath;
77 2 100       31 if (not File::Spec->file_name_is_absolute( $installpath )) {
78 1         12 my @dirs = File::Spec->splitdir( $installpath );
79 1 50       4 if (@dirs) {
80 1 50       20 if ($dirs[0] ne File::Spec->curdir) {
81 1         26 $self->installpath( File::Spec->catdir( getcwd(), $installpath ) );
82             }
83             }
84             }
85 2         7 my $locallib = $self->locallib;
86 2 100       14 if (not File::Spec->file_name_is_absolute( $locallib )) {
87 1         6 my @dirs = File::Spec->splitdir( $locallib );
88 1 50       5 if (@dirs == 1) {
89 1         8 $self->locallib( File::Spec->catdir( $installpath, $locallib ) );
90             }
91             else {
92 0         0 $self->locallib( File::Spec->catdir( getcwd(), $locallib ) );
93             }
94             }
95             }
96              
97 0     0 0 0 sub effective_compute_deps( $self ) {
  0         0  
  0         0  
98 0 0       0 return '' unless $self->sys_pkgs;
99 0 0       0 return '' if $self->{_deps};
100              
101 0 0       0 if (defined $self->compute_deps) {
102 0         0 return $self->compute_deps;
103             }
104              
105             $log->warning( "Result of 'effective_compute_deps()' not reliable: "
106             . "no attempt to retrieve dependencies" )
107 0 0       0 unless $self->{_deps_retrieved};
108              
109 0         0 return 1;
110             }
111              
112 0     0 0 0 sub effective_prepare_env( $self ) {
  0         0  
  0         0  
113 0 0       0 if (defined $self->prepare_env) {
114 0         0 return $self->prepare_env;
115             }
116              
117 0 0       0 return 1 if $self->assume_yes;
118              
119             # ask and set 'prepare_env' (so uninstall_env can use it) ...
120 0 0       0 if (-t STDIN) {
121 0         0 while (1) {
122 0         0 my $key = '';
123 0         0 print "\nPackage installation required. Proceed? (y/N) ";
124 0         0 my $line = ;
125 0         0 $key = substr( $line, 0, 1 );
126 0 0 0     0 if (lc($key) eq 'y') {
    0          
127 0         0 $self->prepare_env( 1 );
128 0         0 return 1;
129             }
130             elsif (lc($key) eq 'n'
131             or $key eq "\n") {
132 0         0 $self->prepare_env( 0 );
133 0         0 return 0;
134             }
135             else {
136 0         0 say "\nInvalid input";
137             }
138             }
139             }
140             else {
141 0         0 $log->info( "Input is not a TTY; assuming answer 'no' to package installation permission" );
142 0         0 $self->prepare_env( 0 );
143 0         0 return 0;
144             }
145             }
146              
147 0     0 0 0 sub effective_uninstall_env( $self ) {
  0         0  
  0         0  
148 0 0       0 if (defined $self->uninstall_env) {
149 0         0 return $self->uninstall_env;
150             }
151              
152 0         0 return $self->effective_prepare_env;
153             }
154              
155 0     0 0 0 sub effective_version( $self ) {
  0         0  
  0         0  
156 0 0       0 return $self->version if defined $self->version;
157 0         0 $log->debug( "Resolving 'latest' version to actual version number" );
158              
159 0         0 my $r = $http->get( 'https://api.github.com/repos/ledgersmb/LedgerSMB/releases/latest' );
160 0 0       0 if ($r->{success}) {
    0          
161 0         0 my $content = JSON::PP->new->utf8->decode( $r->{content} );
162              
163 0 0 0     0 if (defined $content
164             and defined $content->{tag_name}) {
165 0         0 $self->version( $content->{tag_name} );
166 0         0 $log->info( "Resolved 'latest' version to $content->{tag_name} for installation" );
167              
168 0         0 return $content->{tag_name};
169             }
170             else {
171 0         0 die $log->fatal( "Information for 'latest' release does not include tag_name" );
172             }
173             # unreachable
174             }
175             elsif ($r->{status} == 599) {
176             die $log->fatal(
177             'Error trying to retrieve precomputed dependencies: ' . $r->{content}
178 0         0 );
179             }
180             # unreachable
181             }
182              
183 2     2 0 88 sub option_callbacks($self, $options) {
  2         4  
  2         5  
  2         3  
184             my %opts = (
185 2     2   1169 'yes|y!' => sub { $self->assume_yes( $_[1] ) },
186 2     2   428 'system-packages!' => sub { $self->sys_pkgs( $_[1] ) },
187 2     2   370 'prepare-env!' => sub { $self->prepare_env( $_[1] ) },
188 1     1   230 'target=s' => sub { $self->installpath( $_[1] ) },
189 1     1   213 'local-lib=s' => sub { $self->locallib( $_[1] ) },
190 1     1   191 'log-level=s' => sub { $self->loglevel( $_[1] ) },
191 2     2   360 'verify-sig!' => sub { $self->verify_sig( $_[1] ) },
192 2         34 );
193              
194 2         23 return %opts{$options->@*};
195             }
196              
197             for my $acc (qw( assume_yes installpath locallib loglevel
198             compute_deps prepare_env sys_pkgs
199             verify_sig uninstall_env version cpanfile cpanfile_path )) {
200             my $ref = qualify_to_ref $acc;
201 32     32   116 *{$ref} = sub($self, $arg = undef) {
  32         57  
  32         57  
  32         47  
202 32 100       108 $self->{"_$acc"} = $arg
203             if defined $arg;
204 32         210 return $self->{"_$acc"};
205             };
206             }
207              
208             1;