epic-ts
[kivitendo-erp.git] / scripts / installation_check.pl
1 #!/usr/bin/perl -w
2
3 our $master_templates;
4 BEGIN {
5   use FindBin;
6
7   unshift(@INC, $FindBin::Bin . '/../modules/override'); # Use our own versions of various modules (e.g. YAML).
8   push   (@INC, $FindBin::Bin . '/..');                  # '.' will be removed from @INC soon.
9
10   # this is a default dir. may be wrong in your installation, change it then
11   $master_templates = $FindBin::Bin . '/../templates/print/';
12 }
13
14 use strict;
15 use Getopt::Long;
16 use List::MoreUtils qw(uniq);
17 use Pod::Usage;
18 use Term::ANSIColor;
19 use Text::Wrap;
20
21 unless (eval { require Config::Std; 1 }){
22   print STDERR <<EOL ;
23 +------------------------------------------------------------------------------+
24   Perl Modul Config::Std could not be loaded.
25
26   Debian: you may install the needed *.deb package with:
27     apt-get install libconfig-std-perl
28
29   Red Hat/Fedora/CentOS: you may install the needed *.rpm package with:
30     yum install perl-Config-Std
31
32   SUSE: you may install the needed *.rpm package with:
33     zypper install perl-Config-Std
34
35 +------------------------------------------------------------------------------+
36 EOL
37
38   exit 72;
39 }
40
41 use SL::InstallationCheck;
42 use SL::LxOfficeConf;
43
44 my @missing_modules;
45 my %check;
46 Getopt::Long::Configure ("bundling");
47 GetOptions(
48   "v|verbose"   => \ my $v,
49   "a|all"       => \ $check{a},
50   "o|optional!" => \ $check{o},
51   "d|devel!"    => \ $check{d},
52   "l|latex!"    => \ $check{l},
53   "r|required!" => \ $check{r},
54   "h|help"      => sub { pod2usage(-verbose => 2) },
55   "c|color!"    => \ ( my $c = 1 ),
56   "i|install-command!"  => \ my $apt,
57   "s|silent"    => \ $check{s},
58 );
59
60 my %install_methods = (
61   apt    => { key => 'debian', install => 'sudo apt install', system => "Debian, Ubuntu" },
62   yum    => { key => 'fedora', install => 'sudo yum install',     system => "RHEL, Fedora, CentOS" },
63   zypper => { key => 'suse',   install => 'sudo zypper install',  system => "SLES, openSUSE" },
64   cpan   => { key => 'name',   install => "sudo cpan",            system => "CPAN" },
65 );
66
67 # if nothing is requested check "required"
68 my $default_run;
69 if (!defined $check{a}
70  && !defined $check{l}
71  && !defined $check{o}
72  && !defined $check{d}) {
73   $check{r} = 1;
74   $default_run ='1';  # no parameter, therefore print a note after default run
75 }
76
77 if ($check{a}) {
78   $check{$_} //= 1 for qw(o d l r);
79 }
80
81
82 $| = 1;
83
84 if (!SL::LxOfficeConf->read(undef, 'may fail')) {
85   print_header('Could not load the config file. If you have dependencies from any features enabled in the configuration these will still show up as optional because of this. Please rerun this script after installing the dependencies needed to load the configuration.')
86 } else {
87   SL::InstallationCheck::check_for_conditional_dependencies();
88 }
89
90 if ($check{r}) {
91   print_header('Checking Required Modules');
92   check_module($_, required => 1) for @SL::InstallationCheck::required_modules;
93   check_pdfinfo();
94 }
95 if ($check{o}) {
96   print_header('Checking Optional Modules');
97   check_module($_, optional => 1) for @SL::InstallationCheck::optional_modules;
98   check_aqbanking();
99 }
100 if ($check{d}) {
101   print_header('Checking Developer Modules');
102   check_module($_, devel => 1) for @SL::InstallationCheck::developer_modules;
103 }
104 if ($check{l}) {
105   check_latex();
106 }
107
108 my $fail = @missing_modules;
109 print_header('Result');
110 print_line('All', $fail ? 'NOT ok' : 'OK', $fail ? 'red' : 'green');
111
112 if ($default_run && !$check{s}) {
113   if (@missing_modules) {
114     $apt = 1;
115   print <<"EOL";
116
117 HEY! It seems there are modules MISSING! Look for the red lines with "NOT ok"
118 above. You'll want to fix those, I've enabled --install-command for you...
119 EOL
120   } else {
121   print <<"EOL";
122
123 Standard check done, everything is OK and up to date. Have a look at the --help
124 section of this script to see some more advanced checks for developer and
125 optional dependencies, as well as LaTeX packages you might need.
126 EOL
127   }
128 }
129
130 if (@missing_modules && $apt && !$check{s}) {
131   print "\nHere are some sample installation lines, choose one appropriate for your system:\n\n";
132   local $Text::Wrap::separator = " \\\n";
133
134   for (keys %install_methods) {
135     my $method = $install_methods{$_};
136     if (my @install_candidates = grep $_, map { $_->{$method->{key}} } @missing_modules) {
137       print "$method->{system}:\n";
138       print wrap("  ", "    ",  $method->{install}, @install_candidates);
139       print $/;
140     }
141   }
142 }
143
144 exit !!@missing_modules;
145
146 sub check_latex {
147   my ($res) = check_kpsewhich();
148   print_result("Looking for LaTeX kpsewhich", $res);
149
150   # no pdfx -> no zugferd possible
151   my $ret = kpsewhich('template/print/', 'sty', 'pdfx');
152   die "Cannot use pdfx. Please install this package first (debian: apt install texlive-latex-extra)"  if $ret;
153   # check version 2018
154   my $latex = $::lx_office_conf{applications}->{latex} || 'pdflatex';
155   my $pdfx = (system ${latex} . ' --interaction=batchmode "\documentclass{minimal} \RequirePackage{pdfx} \csname @ifpackagelater\endcsname{pdfx}{2018/12/22}{}{\show\relax} \begin{document} \end{document}"');
156
157   print_result ("Looking for pdfx version 2018 or higher", !$pdfx);
158   push @missing_modules, \(name => 'pdfx') if $pdfx;
159
160   if ($res) {
161     check_template_dir($_) for SL::InstallationCheck::template_dirs($master_templates);
162   }
163   print STDERR <<EOL if $pdfx;
164 +------------------------------------------------------------------------------+
165   Your pdfx version is too old. You cannot use ZuGFeRD or modern (2018+)
166   templates. Please consider using a more recent LaTeX environment.
167   Verify with:
168   pdflatex --interaction=batchmode "\RequirePackage{pdfx}[2018/12/22]"
169 +------------------------------------------------------------------------------+
170 EOL
171 }
172
173 sub check_template_dir {
174   my ($dir) = @_;
175   my $path  = $master_templates . $dir;
176
177   print_header("Checking LaTeX Dependencies for Master Templates '$dir'");
178   kpsewhich($path, 'cls', $_) for SL::InstallationCheck::classes_from_latex($path, '\documentclass');
179
180   my @sty = sort { $a cmp $b } uniq (
181     SL::InstallationCheck::classes_from_latex($path, '\usepackage'),
182     qw(textcomp ulem embedfile)
183   );
184   kpsewhich($path, 'sty', $_) for @sty;
185 }
186
187 our $mastertemplate_path = './templates/print/';
188
189 sub check_kpsewhich {
190   return 1 if SL::InstallationCheck::check_kpsewhich();
191
192   print STDERR <<EOL if $v && !$check{s};
193 +------------------------------------------------------------------------------+
194   Can't find kpsewhich, is there a proper installed LaTeX?
195   On Debian you may run "aptitude install texlive-base-bin"
196 +------------------------------------------------------------------------------+
197 EOL
198   return 0;
199 }
200
201 sub kpsewhich {
202   my ($dw, $type, $package) = @_;
203   $package =~ s/[^-_0-9A-Za-z]//g;
204   my $type_desc = $type eq 'cls' ? 'document class' : 'package';
205
206   eval { require String::ShellQuote; 1 } or warn "can't load String::ShellQuote" && return;
207      $dw         = String::ShellQuote::shell_quote $dw;
208   my $e_package  = String::ShellQuote::shell_quote $package;
209   my $e_type     = String::ShellQuote::shell_quote $type;
210
211   my $exit = system(qq|TEXINPUTS=".:$dw:" kpsewhich $e_package.$e_type > /dev/null|);
212   my $res  = $exit > 0 ? 0 : 1;
213
214   print_result("Looking for LaTeX $type_desc $package", $res);
215   if (!$res) {
216     print STDERR <<EOL if $v && !$check{s};
217 +------------------------------------------------------------------------------+
218   LaTeX $type_desc $package could not be loaded.
219
220   On Debian you may find the needed *.deb package with:
221     apt-file search $package.$type
222
223   Maybe you need to install apt-file first by:
224     aptitude install apt-file && apt-file update
225 +------------------------------------------------------------------------------+
226 EOL
227   }
228 }
229
230 sub check_pdfinfo {
231   my $line = "Looking for pdfinfo executable";
232   my $shell_out = `pdfinfo -v 2>&1 | grep version 2> /dev/null`;
233   my ($label,$vers,$ver_string)  = split / /,$shell_out;
234   if ( $label && $label eq 'pdfinfo' ) {
235     chop $ver_string;
236     print_line($line, $ver_string, 'green');
237   } else {
238     print_line($line, 'not installed','red');
239     my %modinfo = ( debian => 'poppler-utils' );
240     push @missing_modules, \%modinfo;
241
242   }
243 }
244
245 sub check_aqbanking {
246   my $aqbin = $::lx_office_conf{applications}->{aqbanking};
247   if ( !$aqbin ) {
248     print_line('Looking for aqbanking executable', 'not configured','red');
249   }
250   else {
251     my $line = "Looking for aqbanking executable '".$aqbin."'";
252     my $shell_out = `$aqbin versions 2>&1 | grep AqBanking-CLI 2> /dev/null`;
253     my ($label,$version)  = split /:/,$shell_out;
254     if ( $label && $label eq ' AqBanking-CLI' ) {
255       chop $version;
256       my ($number_version) = $version =~ /(\d+)/;
257       if ($number_version < 6) {
258         print_line($line, "Requires at least version 6, current version is " . $version, 'red');
259       } else {
260         print_line($line, $version, 'green');
261       }
262     } else {
263       print_line($line, 'not installed','red');
264       my %modinfo = ( name => 'aqbanking' );
265       push @missing_modules, \%modinfo;
266     }
267   }
268 }
269
270 sub check_module {
271   my ($module, %role) = @_;
272
273   my $line = "Looking for $module->{fullname}";
274   $line   .= " (from $module->{dist_name})" if $module->{dist_name};
275   my ($res, $ver) = SL::InstallationCheck::module_available($module->{"name"}, $module->{version});
276   if ($res) {
277     my $ver_string = ref $ver && $ver->can('numify') ? $ver->numify : $ver ? $ver : 'no version';
278     print_line($line, $ver_string, 'green');
279   } else {
280     print_result($line, $res);
281   }
282
283
284   return if $res;
285
286   push @missing_modules, $module;
287
288   my $needed_text =
289       $role{optional} ? 'It is OPTIONAL for kivitendo but RECOMMENDED for improved functionality.'
290     : $role{required} ? 'It is NEEDED by kivitendo and must be installed.'
291     : $role{devel}    ? 'It is OPTIONAL for kivitendo and only useful for developers.'
292     :                   'It is not listed as a dependency yet. Please tell this the developers.';
293
294   my @source_texts = module_source_texts($module);
295   local $" = $/;
296   print STDERR <<EOL if $v && !$check{s};
297 +------------------------------------------------------------------------------+
298   $module->{fullname} could not be loaded.
299
300   This module is either too old or not available on your system.
301   $needed_text
302
303   Here are some ideas how to get it:
304
305 @source_texts
306 +------------------------------------------------------------------------------+
307 EOL
308 }
309
310 sub module_source_texts {
311   my ($module) = @_;
312   my @texts;
313   for my $key (keys %install_methods) {
314     my $method = $install_methods{$key};
315     push @texts, <<"EOL" if $module->{$method->{key}};
316   - Using $method->{system} you can install it with $key:
317       $method->{install} $module->{$method->{key}}
318 EOL
319   }
320   push @texts, <<EOL if $module->{url};
321   - You can download it from this URL and install it manually:
322       $module->{url}
323 EOL
324
325   return @texts;
326 }
327
328 sub mycolor {
329   return $_[0] unless $c;
330   return colored(@_);
331 }
332
333 sub print_result {
334   my ($test, $exit) = @_;
335   if ($exit) {
336     print_line($test, 'ok', 'green');
337   } else {
338     print_line($test, 'NOT ok', 'red');
339   }
340 }
341
342 sub print_line {
343   my ($text, $res, $color) = @_;
344   return if $check{s};
345   print $text, " ", ('.' x (78 - length($text) - length($res))), " ", mycolor($res, $color), $/;
346 }
347
348 sub print_header {
349   return if $check{s};
350   print $/;
351   print "$_[0]:", $/;
352 }
353
354 1;
355
356 __END__
357
358 =encoding UTF-8
359
360 =head1 NAME
361
362 scripts/installation_check.pl - check kivitendo dependencies
363
364 =head1 SYNOPSIS
365
366   scripts/installation_check.pl [OPTION]
367
368 =head1 DESCRIPTION
369
370 Check dependencys. List all perl modules needed by kivitendo, probes for them,
371 and warns if one is not available.  List all LaTeX document classes and
372 packages needed by kivitendo master templates, probes for them, and warns if
373 one is not available.
374
375
376 =head1 OPTIONS
377
378 =over 4
379
380 =item C<-a, --all>
381
382 Probe for all perl modules and all LaTeX master templates.
383
384 =item C<-c, --color>
385
386 Color output. Default on.
387
388 =item C<--no-color>
389
390 No color output. Helpful to avoid terminal escape problems.
391
392 =item C<-d, --devel>
393
394 Probe for perl developer dependencies. (Used for console  and tags file)
395
396 =item C<--no-devel>
397
398 Don't probe for perl developer dependencies. (Useful in combination with --all)
399
400 =item C<-h, --help>
401
402 Display this help.
403
404 =item C<-o, --optional>
405
406 Probe for optional modules.
407
408 =item C<--no-optional>
409
410 Don't probe for optional perl modules. (Useful in combination with --all)
411
412 =item C<-r, --required>
413
414 Probe for required perl modules (default).
415
416 =item C<--no-required>
417
418 Don't probe for required perl modules. (Useful in combination with --all)
419
420 =item C<-l. --latex>
421
422 Probe for LaTeX documentclasses and packages in master templates.
423
424 =item C<--no-latex>
425
426 Don't probe for LaTeX document classes and packages in master templates. (Useful in combination with --all)
427
428 =item C<-v. --verbose>
429
430 Print additional info for missing dependencies
431
432 =item C<-i, --install-command>
433
434 Tries to generate installation commands for the most common package managers.
435 Note that these lists can be slightly off, but it should still save you a lot
436 of typing.
437
438 =back
439
440 =head1 BUGS, CAVEATS and TODO
441
442 =over 4
443
444 =item *
445
446 Fedora packages not listed yet.
447
448 =item *
449
450 Not possible yet to generate a combined cpan/apt-get string to install all needed.
451
452 =item *
453
454 Not able to handle devel cpan modules yet.
455
456 =item *
457
458 Version requirements not fully tested yet.
459
460 =back
461
462 =head1 AUTHOR
463
464   Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
465   Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
466   Wulf Coulmann E<lt>wulf@coulmann.deE<gt>
467
468 =cut