More rigorous session-cleanup for BecomeUser
[usit-rt.git] / sbin / rt-test-dependencies
CommitLineData
84fb5b46
MKG
1#!/usr/bin/perl
2# BEGIN BPS TAGGED BLOCK {{{
3#
4# COPYRIGHT:
5#
6# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
7# <sales@bestpractical.com>
8#
9# (Except where explicitly superseded by other copyright notices)
10#
11#
12# LICENSE:
13#
14# This work is made available to you under the terms of Version 2 of
15# the GNU General Public License. A copy of that license should have
16# been provided with this software, but in any event can be snarfed
17# from www.gnu.org.
18#
19# This work is distributed in the hope that it will be useful, but
20# WITHOUT ANY WARRANTY; without even the implied warranty of
21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22# General Public License for more details.
23#
24# You should have received a copy of the GNU General Public License
25# along with this program; if not, write to the Free Software
26# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27# 02110-1301 or visit their web page on the internet at
28# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
29#
30#
31# CONTRIBUTION SUBMISSION POLICY:
32#
33# (The following paragraph is not intended to limit the rights granted
34# to you to modify and distribute this software under the terms of
35# the GNU General Public License and is only of importance to you if
36# you choose to contribute your changes and enhancements to the
37# community by submitting them to Best Practical Solutions, LLC.)
38#
39# By intentionally submitting any modifications, corrections or
40# derivatives to this work, or any other work intended for use with
41# Request Tracker, to Best Practical Solutions, LLC, you confirm that
42# you are the copyright holder for those contributions and you grant
43# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
44# royalty-free, perpetual, license to use, copy, create derivative
45# works based on those contributions, and sublicense and distribute
46# those contributions and any derivatives thereof.
47#
48# END BPS TAGGED BLOCK }}}
49#
50# This is just a basic script that checks to make sure that all
51# the modules needed by RT before you can install it.
52#
53
54use strict;
55no warnings qw(numeric redefine);
56use Getopt::Long;
57my %args;
58my %deps;
59GetOptions(
60 \%args, 'v|verbose',
61 'install', 'with-MYSQL',
62 'with-POSTGRESQL|with-pg|with-pgsql', 'with-SQLITE',
63 'with-ORACLE', 'with-FASTCGI',
64 'with-MODPERL1', 'with-MODPERL2',
65 'with-STANDALONE',
66
67 'with-DEV',
68
69 'with-GPG',
70 'with-ICAL',
71 'with-SMTP',
72 'with-GRAPHVIZ',
73 'with-GD',
74 'with-DASHBOARDS',
75 'with-USERLOGO',
76 'with-SSL-MAILGATE',
77
78 'download=s',
79 'repository=s',
80 'list-deps',
81 'help|h',
82);
83
84if ( $args{help} ) {
85 require Pod::Usage;
86 Pod::Usage::pod2usage( { verbose => 2 } );
87 exit;
88}
89
90# Set up defaults
91my %default = (
92 'with-MASON' => 1,
93 'with-PSGI' => 1,
94 'with-CORE' => 1,
95 'with-CLI' => 1,
96 'with-MAILGATE' => 1,
97 'with-DEV' => 0,
98 'with-GPG' => 0,
99 'with-ICAL' => 1,
100 'with-SMTP' => 1,
101 'with-GRAPHVIZ' => 0,
102 'with-GD' => 1,
103 'with-DASHBOARDS' => 1,
104 'with-USERLOGO' => 1,
105 'with-SSL-MAILGATE' => 0,
106);
107$args{$_} = $default{$_} foreach grep !exists $args{$_}, keys %default;
108
109{
110 my $section;
111 my %always_show_sections = (
112 perl => 1,
113 users => 1,
114 );
115
116 sub section {
117 my $s = shift;
118 $section = $s;
119 print "$s:\n" unless $args{'list-deps'};
120 }
121
122 sub print_found {
123 my $msg = shift;
124 my $test = shift;
125 my $extra = shift;
126
127 unless ( $args{'list-deps'} ) {
128 if ( $args{'v'} or not $test or $always_show_sections{$section} ) {
129 print "\t$msg ...";
130 print $test ? "found" : "MISSING";
131 print "\n";
132 }
133
134 print "\t\t$extra\n" if defined $extra;
135 }
136 }
137}
138
139sub conclude {
140 my %missing_by_type = @_;
141
142 unless ( $args{'list-deps'} ) {
143 unless ( keys %missing_by_type ) {
144 print "\nAll dependencies have been found.\n";
145 return;
146 }
147
148 print "\nSOME DEPENDENCIES WERE MISSING.\n";
149
150 for my $type ( keys %missing_by_type ) {
151 my $missing = $missing_by_type{$type};
152
153 print "$type missing dependencies:\n";
154 for my $name ( keys %$missing ) {
155 my $module = $missing->{$name};
156 my $version = $module->{version};
157 my $error = $module->{error};
158 print_found( $name . ( $version && !$error ? " >= $version" : "" ),
159 0, $module->{error} );
160 }
161 }
162 exit 1;
163 }
164}
165
166sub text_to_hash {
167 my %hash;
168 for my $line ( split /\n/, $_[0] ) {
169 my($key, $value) = $line =~ /(\S+)\s*(\S*)/;
170 $value ||= '';
171 $hash{$key} = $value;
172 }
173
174 return %hash;
175}
176
177$deps{'CORE'} = [ text_to_hash( << '.') ];
178Class::Accessor 0.34
179DateTime 0.44
180DateTime::Locale 0.40
181Digest::base
182Digest::MD5 2.27
183Digest::SHA
184DBI 1.37
185Class::ReturnValue 0.40
186DBIx::SearchBuilder 1.59
187Text::Template 1.44
188File::ShareDir
189File::Spec 0.8
190HTML::Quoted
191HTML::Scrubber 0.08
b5747ff2
MKG
192HTML::TreeBuilder
193HTML::FormatText
84fb5b46
MKG
194Log::Dispatch 2.23
195Sys::Syslog 0.16
196Locale::Maketext 1.06
197Locale::Maketext::Lexicon 0.32
198Locale::Maketext::Fuzzy
199MIME::Entity 5.425
200Mail::Mailer 1.57
201Email::Address
202Text::Wrapper
203Time::ParseDate
204Time::HiRes
205File::Temp 0.19
206Text::Quoted 2.02
207Tree::Simple 1.04
208UNIVERSAL::require
209Regexp::Common
210Scalar::Util
211Module::Versions::Report 1.05
212Cache::Simple::TimedExpiry
213Encode 2.39
214CSS::Squish 0.06
215File::Glob
216Devel::StackTrace 1.19
217Text::Password::Pronounceable
218Devel::GlobalDestruction
219List::MoreUtils
220Net::CIDR
221Regexp::Common::net::CIDR
222Regexp::IPv6
223.
224
225$deps{'MASON'} = [ text_to_hash( << '.') ];
226HTML::Mason 1.43
227Errno
228Digest::MD5 2.27
229CGI::Cookie 1.20
230Storable 2.08
231Apache::Session 1.53
232XML::RSS 1.05
233Text::WikiFormat 0.76
234CSS::Squish 0.06
235Devel::StackTrace 1.19
236JSON
237IPC::Run3
238.
239
240$deps{'PSGI'} = [ text_to_hash( << '.') ];
241CGI 3.38
242CGI::PSGI 0.12
243HTML::Mason::PSGIHandler 0.52
244Plack 0.9971
245Plack::Handler::Starlet
246CGI::Emulate::PSGI
247.
248
249$deps{'MAILGATE'} = [ text_to_hash( << '.') ];
84fb5b46
MKG
250Getopt::Long
251LWP::UserAgent
252Pod::Usage
253.
254
255$deps{'SSL-MAILGATE'} = [ text_to_hash( << '.') ];
256Crypt::SSLeay
257Net::SSL
258LWP::UserAgent 6.0
259LWP::Protocol::https
260Mozilla::CA
261.
262
263$deps{'CLI'} = [ text_to_hash( << '.') ];
264Getopt::Long 2.24
265LWP
266HTTP::Request::Common
267Text::ParseWords
268Term::ReadLine
269Term::ReadKey
270.
271
272$deps{'DEV'} = [ text_to_hash( << '.') ];
273Email::Abstract
274Test::Email
275HTML::Form
276HTML::TokeParser
277WWW::Mechanize 1.52
278Test::WWW::Mechanize 1.30
279Module::Refresh 0.03
280Test::Expect 0.31
281XML::Simple
282File::Find
283Test::Deep 0 # needed for shredder tests
284String::ShellQuote 0 # needed for gnupg-incoming.t
285Log::Dispatch::Perl
286Test::Warn
287Test::Builder 0.90 # needed for is_passing
288Test::MockTime
289Log::Dispatch::Perl
290Test::WWW::Mechanize::PSGI
b5747ff2 291Plack::Middleware::Test::StashWarnings 0.06
84fb5b46
MKG
292Test::LongString
293.
294
295$deps{'FASTCGI'} = [ text_to_hash( << '.') ];
296FCGI
297FCGI::ProcManager
298.
299
300$deps{'MODPERL1'} = [ text_to_hash( << '.') ];
301Apache::Request
302Apache::DBI 0.92
303.
304
305$deps{'MODPERL2'} = [ text_to_hash( << '.') ];
306Apache::DBI
307HTML::Mason 1.36
308.
309
310$deps{'MYSQL'} = [ text_to_hash( << '.') ];
311DBD::mysql 2.1018
312.
313
314$deps{'ORACLE'} = [ text_to_hash( << '.') ];
315DBD::Oracle
316.
317
318$deps{'POSTGRESQL'} = [ text_to_hash( << '.') ];
319DBD::Pg 1.43
320.
321
322$deps{'SQLITE'} = [ text_to_hash( << '.') ];
323DBD::SQLite 1.00
324.
325
326$deps{'GPG'} = [ text_to_hash( << '.') ];
327GnuPG::Interface
328PerlIO::eol
329.
330
331$deps{'ICAL'} = [ text_to_hash( << '.') ];
332Data::ICal
333.
334
335$deps{'SMTP'} = [ text_to_hash( << '.') ];
336Net::SMTP
337.
338
339$deps{'DASHBOARDS'} = [ text_to_hash( << '.') ];
340HTML::RewriteAttributes 0.04
341MIME::Types
b5747ff2 342URI 1.59
84fb5b46
MKG
343.
344
345$deps{'GRAPHVIZ'} = [ text_to_hash( << '.') ];
346GraphViz
347IPC::Run
348.
349
350$deps{'GD'} = [ text_to_hash( << '.') ];
351GD
352GD::Graph
353GD::Text
354.
355
356$deps{'USERLOGO'} = [ text_to_hash( << '.') ];
357Convert::Color
358.
359
360my %AVOID = (
361 'DBD::Oracle' => [qw(1.23)],
362);
363
364if ($args{'download'}) {
365 download_mods();
366}
367
368
369check_perl_version();
370
371check_users();
372
373my %Missing_By_Type = ();
374foreach my $type (sort grep $args{$_}, keys %args) {
375 next unless ($type =~ /^with-(.*?)$/) and $deps{$1};
376
377 $type = $1;
378 section("$type dependencies");
379
380 my @missing;
381 my @deps = @{ $deps{$type} };
382
383 my %missing = test_deps(@deps);
384
385 if ( $args{'install'} ) {
386 for my $module (keys %missing) {
387 resolve_dep($module, $missing{$module}{version});
388 my $m = $module . '.pm';
389 $m =~ s!::!/!g;
390 if ( delete $INC{$m} ) {
391 my $symtab = $module . '::';
392 no strict 'refs';
393 for my $symbol ( keys %{$symtab} ) {
394 next if substr( $symbol, -2, 2 ) eq '::';
395 delete $symtab->{$symbol};
396 }
397 }
398 delete $missing{$module}
399 if test_dep($module, $missing{$module}{version}, $AVOID{$module});
400 }
401 }
402
403 $Missing_By_Type{$type} = \%missing if keys %missing;
404}
405
406conclude(%Missing_By_Type);
407
408sub test_deps {
409 my @deps = @_;
410
411 my %missing;
412 while(@deps) {
413 my $module = shift @deps;
414 my $version = shift @deps;
415 my($test, $error) = test_dep($module, $version, $AVOID{$module});
416 my $msg = $module . ($version && !$error ? " >= $version" : '');
417 print_found($msg, $test, $error);
418
419 $missing{$module} = { version => $version, error => $error } unless $test;
420 }
421
422 return %missing;
423}
424
425sub test_dep {
426 my $module = shift;
427 my $version = shift;
428 my $avoid = shift;
429
430 if ( $args{'list-deps'} ) {
431 print $module, ': ', $version || 0, "\n";
432 }
433 else {
434 eval "use $module $version ()";
435 if ( my $error = $@ ) {
436 return 0 unless wantarray;
437
438 $error =~ s/\n(.*)$//s;
439 $error =~ s/at \(eval \d+\) line \d+\.$//;
440 undef $error if $error =~ /this is only/;
441
442 return ( 0, $error );
443 }
444
445 if ( $avoid ) {
446 my $version = $module->VERSION;
447 if ( grep $version eq $_, @$avoid ) {
448 return 0 unless wantarray;
449 return (0, "It's known that there are problems with RT and version '$version' of '$module' module. If it's the latest available version of the module then you have to downgrade manually.");
450 }
451 }
452
453 return 1;
454 }
455}
456
457sub resolve_dep {
458 my $module = shift;
459 my $version = shift;
460
461 print "\nInstall module $module\n";
462
463 my $ext = $ENV{'RT_FIX_DEPS_CMD'} || $ENV{'PERL_PREFER_CPAN_CLIENT'};
464 unless( $ext ) {
465 my $configured = 1;
466 {
467 local @INC = @INC;
468 if ( $ENV{'HOME'} ) {
469 unshift @INC, "$ENV{'HOME'}/.cpan";
470 }
471 $configured = eval { require CPAN::MyConfig } || eval { require CPAN::Config };
472 }
473 unless ( $configured ) {
474 print <<END;
475You haven't configured the CPAN shell yet.
476Please run `/usr/bin/perl -MCPAN -e shell` to configure it.
477END
478 exit(1);
479 }
480 my $rv = eval { require CPAN; CPAN::Shell->install($module) };
481 return $rv unless $@;
482
483 print <<END;
484Failed to load module CPAN.
485
486-------- Error ---------
487$@
488------------------------
489
490When we tried to start installing RT's perl dependencies,
491we were unable to load the CPAN client. This module is usually distributed
492with Perl. This usually indicates that your vendor has shipped an unconfigured
493or incorrectly configured CPAN client.
494The error above may (or may not) give you a hint about what went wrong
495
496You have several choices about how to install dependencies in
497this situatation:
498
4991) use a different tool to install dependencies by running setting the following
500 shell environment variable and rerunning this tool:
501 RT_FIX_DEPS_CMD='/usr/bin/perl -MCPAN -e"install %s"'
5022) Attempt to configure CPAN by running:
503 `/usr/bin/perl -MCPAN -e shell` program from shell.
504 If this fails, you may have to manually upgrade CPAN (see below)
5053) Try to update the CPAN client. Download it from:
506 http://search.cpan.org/dist/CPAN and try again
5074) Install each dependency manually by downloading them one by one from
508 http://search.cpan.org
509
510END
511 exit(1);
512 }
513
514 if( $ext =~ /\%s/) {
515 $ext =~ s/\%s/$module/g; # sprintf( $ext, $module );
516 } else {
517 $ext .= " $module";
518 }
519 print "\t\tcommand: '$ext'\n";
520 return scalar `$ext 1>&2`;
521}
522
523sub download_mods {
524 my %modules;
525 use CPAN;
526
527 foreach my $key (keys %deps) {
528 my @deps = (@{$deps{$key}});
529 while (@deps) {
530 my $mod = shift @deps;
531 my $ver = shift @deps;
532 next if ($mod =~ /^(DBD-|Apache-Request)/);
533 $modules{$mod} = $ver;
534 }
535 }
536 my @mods = keys %modules;
537 CPAN::get();
538 my $moddir = $args{'download'};
539 foreach my $mod (@mods) {
540 $CPAN::Config->{'build_dir'} = $moddir;
541 CPAN::get($mod);
542 }
543
544 opendir(DIR, $moddir);
545 while ( my $dir = readdir(DIR)) {
546 print "Dir is $dir\n";
547 next if ( $dir =~ /^\.\.?$/);
548
549 # Skip things we've previously tagged
550 my $out = `svn ls $args{'repository'}/tags/$dir`;
551 next if ($out);
552
553 if ($dir =~ /^(.*)-(.*?)$/) {
554 `svn_load_dirs -no_user_input -t tags/$dir -v $args{'repository'} dists/$1 $moddir/$dir`;
555 `rm -rf $moddir/$dir`;
556
557 }
558
559 }
560 closedir(DIR);
561 exit;
562}
563
564sub check_perl_version {
565 section("perl");
566 eval {require 5.008003};
567 if ($@) {
568 print_found("5.8.3", 0,"RT is known to be non-functional on versions of perl older than 5.8.3. Please upgrade to 5.8.3 or newer.");
569 exit(1);
570 } else {
571 print_found( sprintf(">=5.8.3(%vd)", $^V), 1 );
572 }
573}
574
575sub check_users {
576 section("users");
577 print_found("rt group (uio-rt)", defined getgrnam("uio-rt"));
578 print_found("bin owner (root)", defined getpwnam("root"));
579 print_found("libs owner (root)", defined getpwnam("root"));
580 print_found("libs group (bin)", defined getgrnam("bin"));
581 print_found("web owner (httpd)", defined getpwnam("httpd"));
582 print_found("web group (httpd)", defined getgrnam("httpd"));
583}
584
5851;
586
587__END__
588
589=head1 NAME
590
591rt-test-dependencies - test rt's dependencies
592
593=head1 SYNOPSIS
594
595 rt-test-dependencies
596 rt-test-dependencies --install
597 rt-test-dependencies --with-mysql --with-fastcgi
598
599=head1 DESCRIPTION
600
601by default, C<rt-test-dependencies> determines whether you have installed all
602the perl modules RT needs to run.
603
604the "RT_FIX_DEPS_CMD" environment variable, if set, will be used instead of
605the standard CPAN shell by --install to install any required modules. it will
606be called with the module name, or, if "RT_FIX_DEPS_CMD" contains a "%s", will
607replace the "%s" with the module name before calling the program.
608
609=head1 OPTIONS
610
611=over
612
613=item install
614
615 install missing modules
616
617=item verbose
618
619list the status of all dependencies, rather than just the missing ones.
620
621-v is equal to --verbose
622
623=item specify dependencies
624
625=over
626
627=item --with-mysql
628
629 database interface for mysql
630
631=item --with-postgresql
632
633 database interface for postgresql
634
635=item with-oracle
636
637 database interface for oracle
638
639=item with-sqlite
640
641 database interface and driver for sqlite (unsupported)
642
643=item with-fastcgi
644
645 libraries needed to support the fastcgi handler
646
647=item with-modperl1
648
649 libraries needed to support the modperl 1 handler
650
651=item with-modperl2
652
653 libraries needed to support the modperl 2 handler
654
655=item with-dev
656
657 tools needed for RT development
658
659=back
660
661=back
662