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   88904 use v5.24;
  2         15  
6              
7 2     2   9 use strict;
  2         3  
  2         43  
8 2     2   10 use warnings;
  2         4  
  2         59  
9              
10             our $VERSION = '1.0.1'; # VERSION
11              
12 2     2   466 use Moo;
  2         9764  
  2         11  
13 2     2   2446 use Capture::Tiny qw(:all);
  2         34648  
  2         228  
14 2     2   819 use IO::All;
  2         18073  
  2         12  
15 2     2   135 use Module::Runtime qw(use_module is_module_name);
  2         2  
  2         15  
16 2     2   1099 use Types::Standard qw(ArrayRef Str Bool);
  2         129712  
  2         19  
17              
18 2     2   2847 use OPM::Parser;
  2         361298  
  2         81  
19              
20 2     2   852 use OPM::Installer::Utils::TS;
  2         9  
  2         70  
21 2     2   26 use OPM::Installer::Utils::File;
  2         4  
  2         38  
22 2     2   9 use OPM::Installer::Logger;
  2         3  
  2         2727  
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 4346 my $self = shift;
55              
56 5 50       29 if ( @_ % 2 ) {
57 5         18 unshift @_, 'package';
58             }
59              
60 5         19 my %params = @_;
61              
62 5         11 my %file_opts;
63 5 50       27 if ( $self->repositories ) {
64 5         19 $file_opts{repositories} = $self->repositories;
65             }
66              
67 5 50 33     22 if ( $params{repositories} and ref $params{repositories} eq 'ARRAY' ) {
68 0         0 $file_opts{repositories} = $params{repositories};
69             }
70              
71 5         11 my $version_string = "";
72 5 0 33     19 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     21 say sprintf "Try to install %s %s...", $params{package} || $self->package, $version_string if $self->verbose;
78            
79 5   33     101 my $installed_version = $self->utils_ts->is_installed( package => $params{package} || $self->package );
80 5 100       127 if ( $installed_version ) {
81             my $message = sprintf 'Addon %s is installed (%s)',
82 1   33     9 $params{package} || $self->package, $installed_version;
83              
84 1         17 $self->logger->debug( message => $message );
85 1         217 say $message;
86              
87 1 50       7 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     102 package => $params{package} || $self->package,
100             framework_version => $self->version,
101             verbose => $self->verbose,
102             );
103              
104 5         9966 my $package_path = $package_utils->resolve_path;
105              
106 5 100       45 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     29 ( $file_opts{version} ? " $file_opts{version}" : "" ),
110             $self->version;
111              
112 1         29 $self->logger->error( fatal => $message );
113 1         334 say $message;
114 1         13 return;
115             }
116              
117 4         124 my $parsed = OPM::Parser->new(
118             opm_file => $package_path,
119             );
120              
121 4         8017 $parsed->parse;
122              
123 4 100       18102 if ( $parsed->error_string ) {
124 1         19 my $message = sprintf "Cannot parse $package_path: %s", $parsed->error_string;
125 1         27 $self->logger->error( fatal => $message );
126 1         375 say $message;
127 1         16 return;
128             }
129              
130 3 50       83 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       52 if ( $self->utils_ts->is_installed( package => $parsed->name, version => $parsed->version ) ) {
142 2         135 my $message = sprintf 'Addon %s is up to date (%s)',
143             $parsed->name, $parsed->version;
144              
145 2         75 $self->logger->debug( message => $message );
146 2         683 say $message;
147 2         148 return 1;
148             }
149              
150 1 50       59 say sprintf "Working on %s...", $parsed->name if $self->verbose;
151 1         20 $self->logger->debug( message => sprintf "Working on %s...", $parsed->name );
152              
153 1 50       38 my @dependencies = @{ $parsed->dependencies || [] };
  1         18  
154 1         12 my @cpan_deps = grep{ $_->{type} eq 'CPAN' }@dependencies;
  0         0  
155 1         3 my @addon_deps = grep{ $_->{type} eq 'Addon' }@dependencies;
  0         0  
156              
157 1         4 my $found_dependencies = join ', ', map{ $_->{name} }@dependencies;
  0         0  
158 1 50       5 say sprintf "Found dependencies: %s", $found_dependencies if $self->verbose;
159 1         15 $self->logger->debug( message => sprintf "Found dependencies: %s", $found_dependencies );
160              
161 1         15 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         2 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       7 if ( $self->prove ) {
185             # TODO: run unittests
186             }
187              
188 1         7 my $content = io( $package_path )->slurp;
189              
190 1         655 my $message = sprintf "Install %s ...", $parsed->name;
191 1 50       47 say $message if $self->verbose;
192 1         17 $self->logger->debug( message => $message );
193              
194 1         30 $self->manager->PackageInstall( String => $content );
195              
196 1         43 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   239 my ($self, $parsed, $addon_version) = @_;
231              
232 11         39 my ($major, $minor, $patch) = split /\./, $addon_version;
233              
234 11         17 my $check_ok;
235              
236             FRAMEWORK:
237 11         18 for my $required_framework ( @{ $parsed->framework } ) {
  11         62  
238 29         124 my ($r_major, $r_minor, $r_patch) = split /\./, $required_framework;
239              
240 29 100       63 next FRAMEWORK if $r_major != $major;
241 22 100 100     85 next FRAMEWORK if lc $r_minor ne 'x' && $r_minor != $minor;
242 8 100 100     31 next FRAMEWORK if lc $r_patch ne 'x' && $r_patch != $patch;
243              
244 6         10 $check_ok = 1;
245 6         13 last FRAMEWORK;
246             }
247              
248 11         42 return $check_ok;
249             }
250              
251             1;
252              
253             __END__