File Coverage

blib/lib/OPM/Installer.pm
Criterion Covered Total %
statement 102 145 70.3
branch 26 56 46.4
condition 12 33 36.3
subroutine 14 20 70.0
pod 2 2 100.0
total 156 256 60.9


line stmt bran cond sub pod time code
1             package OPM::Installer;
2              
3             # ABSTRACT: Install ticketsystem (Znuny/OTOBO) add ons
4              
5 2     2   84641 use v5.24;
  2         15  
6              
7 2     2   9 use strict;
  2         4  
  2         31  
8 2     2   17 use warnings;
  2         3  
  2         64  
9              
10             our $VERSION = '1.0.0'; # VERSION
11              
12 2     2   439 use Moo;
  2         9306  
  2         11  
13 2     2   2357 use Capture::Tiny qw(:all);
  2         33556  
  2         212  
14 2     2   792 use IO::All;
  2         17493  
  2         11  
15 2     2   130 use Module::Runtime qw(use_module is_module_name);
  2         4  
  2         10  
16 2     2   1055 use Types::Standard qw(ArrayRef Str Bool);
  2         126440  
  2         13  
17              
18 2     2   2451 use OPM::Parser;
  2         348457  
  2         73  
19              
20 2     2   763 use OPM::Installer::Utils::TS;
  2         6  
  2         64  
21 2     2   21 use OPM::Installer::Utils::File;
  2         4  
  2         32  
22 2     2   9 use OPM::Installer::Logger;
  2         5  
  2         2786  
23              
24             has package => ( is => 'ro', isa => Str );
25             has version => ( is => 'ro', isa => Str, lazy => 1, default => \&_build_version );
26             has prove => ( is => 'ro', default => sub { 0 } );
27             has manager => ( is => 'ro', lazy => 1, default => \&_build_manager );
28             has repositories => ( is => 'ro', isa => ArrayRef[Str] );
29             has conf => ( is => 'ro' );
30             has force => ( is => 'ro', isa => Bool );
31             has sudo => ( is => 'ro', isa => Bool );
32             has utils_ts => ( is => 'ro', lazy => 1, default => sub{ OPM::Installer::Utils::TS->new } );
33             has verbose => ( is => 'ro', isa => Bool, default => sub { 0 } );
34             has logger => ( is => 'ro', lazy => 1, default => sub { OPM::Installer::Logger->new } );
35              
36             sub list_available {
37 0     0 1 0 my ( $self, %params ) = @_;
38              
39 0         0 my %file_opts;
40 0 0 0     0 if ( $params{repositories} and ref $params{repositories} eq 'ARRAY' ) {
41 0         0 $file_opts{repositories} = $params{repositories};
42             }
43              
44 0         0 my $package_utils = OPM::Installer::Utils::File->new(
45             %file_opts,
46             package => 'DummyPackage', # ::File needs a package set
47             version => $self->version,
48             );
49              
50 0         0 return $package_utils->list_available;
51             }
52              
53             sub install {
54 5     5 1 2781 my $self = shift;
55              
56 5 50       16 if ( @_ % 2 ) {
57 5         11 unshift @_, 'package';
58             }
59              
60 5         11 my %params = @_;
61              
62 5         6 my %file_opts;
63 5 50       14 if ( $self->repositories ) {
64 5         10 $file_opts{repositories} = $self->repositories;
65             }
66              
67 5 50 33     13 if ( $params{repositories} and ref $params{repositories} eq 'ARRAY' ) {
68 0         0 $file_opts{repositories} = $params{repositories};
69             }
70              
71 5         6 my $version_string = "";
72 5 0 33     9 if ( $params{version} and $params{version_exact} ) {
73 0         0 $file_opts{version} = $params{version};
74 0         0 $version_string = $params{version};
75             }
76              
77 5 50 0     11 say sprintf "Try to install %s %s...", $params{package} || $self->package, $version_string if $self->verbose;
78            
79 5   33     93 my $installed_version = $self->utils_ts->is_installed( package => $params{package} || $self->package );
80 5 100       110 if ( $installed_version ) {
81             my $message = sprintf 'Addon %s is installed (%s)',
82 1   33     6 $params{package} || $self->package, $installed_version;
83              
84 1         22 $self->logger->debug( message => $message );
85 1         197 say $message;
86              
87 1 50       6 if ( $params{version} ) {
88             my $check = $self->utils_ts->_check_version(
89             installed => $installed_version,
90             requested => $params{version},
91 0         0 );
92              
93 0 0       0 return 1 if $check;
94             }
95             }
96              
97             my $package_utils = OPM::Installer::Utils::File->new(
98             %file_opts,
99 5   33     83 package => $params{package} || $self->package,
100             framework_version => $self->version,
101             verbose => $self->verbose,
102             );
103              
104 5         7971 my $package_path = $package_utils->resolve_path;
105              
106 5 100       40 if ( !$package_path ) {
107             my $message = sprintf "Could not find a .opm file for %s%s (framework version %s)",
108             $params{package} || $self->package,
109 1 50 33     23 ( $file_opts{version} ? " $file_opts{version}" : "" ),
110             $self->version;
111              
112 1         23 $self->logger->error( fatal => $message );
113 1         113 say $message;
114 1         9 return;
115             }
116              
117 4         92 my $parsed = OPM::Parser->new(
118             opm_file => $package_path,
119             );
120              
121 4         7106 $parsed->parse;
122              
123 4 100       16267 if ( $parsed->error_string ) {
124 1         19 my $message = sprintf "Cannot parse $package_path: %s", $parsed->error_string;
125 1         20 $self->logger->error( fatal => $message );
126 1         187 say $message;
127 1         11 return;
128             }
129              
130 3 50       59 if ( !$self->_check_matching_versions( $parsed, $self->version ) ) {
131 0         0 my $message = sprintf 'framework versions of %s (%s) doesn\'t match ticketsystem version %s',
132             $parsed->name,
133             join ( ', ', $parsed->framework ),
134             $self->version;
135              
136 0         0 $self->logger->error( fatal => $message );
137 0         0 say $message;
138 0         0 return;
139             }
140              
141 3 100       43 if ( $self->utils_ts->is_installed( package => $parsed->name, version => $parsed->version ) ) {
142 2         108 my $message = sprintf 'Addon %s is up to date (%s)',
143             $parsed->name, $parsed->version;
144              
145 2         66 $self->logger->debug( message => $message );
146 2         310 say $message;
147 2         96 return 1;
148             }
149              
150 1 50       45 say sprintf "Working on %s...", $parsed->name if $self->verbose;
151 1         15 $self->logger->debug( message => sprintf "Working on %s...", $parsed->name );
152              
153 1 50       29 my @dependencies = @{ $parsed->dependencies || [] };
  1         15  
154 1         7 my @cpan_deps = grep{ $_->{type} eq 'CPAN' }@dependencies;
  0         0  
155 1         2 my @addon_deps = grep{ $_->{type} eq 'Addon' }@dependencies;
  0         0  
156              
157 1         2 my $found_dependencies = join ', ', map{ $_->{name} }@dependencies;
  0         0  
158 1 50       4 say sprintf "Found dependencies: %s", $found_dependencies if $self->verbose;
159 1         13 $self->logger->debug( message => sprintf "Found dependencies: %s", $found_dependencies );
160              
161 1         13 for my $cpan_dep ( @cpan_deps ) {
162 0         0 my $module = $cpan_dep->{name};
163 0         0 my $version = $cpan_dep->{version};
164              
165 0 0       0 next CPANDEP if !is_module_name( $module );
166              
167 0 0       0 use_module( $module, $version ) and next;
168              
169 0         0 $self->_cpan_install( %{$cpan_dep} );
  0         0  
170             }
171              
172 1         3 for my $addon_dep ( @addon_deps ) {
173 0         0 my $module = $addon_dep->{name};
174 0         0 my $version = $addon_dep->{version};
175              
176 0 0       0 $self->utils_ts->is_installed( %{$addon_dep} ) and next;
  0         0  
177              
178 0         0 my $success = $self->install( package => $module, version => $version );
179 0 0 0     0 if ( !$success && !$self->force ) {
180 0         0 return;
181             }
182             }
183              
184 1 50       4 if ( $self->prove ) {
185             # TODO: run unittests
186             }
187              
188 1         5 my $content = io( $package_path )->slurp;
189              
190 1         517 my $message = sprintf "Install %s ...", $parsed->name;
191 1 50       43 say $message if $self->verbose;
192 1         15 $self->logger->debug( message => $message );
193              
194 1         25 $self->manager->PackageInstall( String => $content );
195              
196 1         28 return 1;
197             }
198              
199             sub _cpan_install {
200 0     0   0 my ( $self, %params) = @_;
201              
202 0         0 my $dist = $params{name};
203 0 0       0 my @sudo = $self->sudo ? 'sudo' : ();
204             my ($out, $err, $exit) = capture {
205 0     0   0 system @sudo, 'cpanm', $dist;
206 0         0 };
207              
208 0 0       0 if ( $out !~ m{Successfully installed } ) {
209 0         0 die "Installation of dependency failed ($dist)! - ($err)";
210             }
211              
212 0         0 return;
213             }
214              
215             sub _build_manager {
216 0     0   0 my $self = shift;
217              
218 0         0 return $self->utils_ts->manager;
219             }
220              
221             sub _build_utils_ts {
222 0     0   0 OPM::Installer::Utils::TS->new;
223             }
224              
225             sub _build_version {
226 0     0   0 shift->utils_ts->framework_version;
227             }
228              
229             sub _check_matching_versions {
230 11     11   203 my ($self, $parsed, $addon_version) = @_;
231              
232 11         35 my ($major, $minor, $patch) = split /\./, $addon_version;
233              
234 11         16 my $check_ok;
235              
236             FRAMEWORK:
237 11         13 for my $required_framework ( @{ $parsed->framework } ) {
  11         55  
238 29         106 my ($r_major, $r_minor, $r_patch) = split /\./, $required_framework;
239              
240 29 100       59 next FRAMEWORK if $r_major != $major;
241 22 100 100     76 next FRAMEWORK if lc $r_minor ne 'x' && $r_minor != $minor;
242 8 100 100     26 next FRAMEWORK if lc $r_patch ne 'x' && $r_patch != $patch;
243              
244 6         7 $check_ok = 1;
245 6         12 last FRAMEWORK;
246             }
247              
248 11         39 return $check_ok;
249             }
250              
251             1;
252              
253             __END__