import rt 3.8.7
[freeside.git] / rt / sbin / rt-test-dependencies.in
1 #!@PERL@
2 # BEGIN BPS TAGGED BLOCK {{{
3
4 # COPYRIGHT:
5
6 # This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
7 #                                          <jesse@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
54 use strict;
55 no warnings qw(numeric redefine);
56 use Getopt::Long;
57 my %args;
58 my %deps;
59 GetOptions(
60     \%args,                               'v|verbose',
61     'install',                            'with-MYSQL',
62     'with-POSTGRESQL|with-pg|with-pgsql', 'with-SQLITE',
63     'with-ORACLE',                        'with-FASTCGI',
64     'with-SPEEDYCGI',                     'with-MODPERL1',
65     'with-MODPERL2',                      'with-DEV',
66     'with-STANDALONE',
67
68     'with-GPG',
69     'with-ICAL',
70     'with-SMTP',
71     'with-GRAPHVIZ',
72     'with-GD',
73     'with-DASHBOARDS',
74
75     'download=s',
76     'repository=s',
77     'list-deps'
78 );
79
80 unless (keys %args) {
81     help();
82     exit(1);
83 }
84
85 # Set up defaults
86 my %default = (
87     'with-MASON' => 1,
88     'with-CORE' => 1,
89     'with-CLI' => 1,
90     'with-MAILGATE' => 1, 
91     'with-DEV' => @RT_DEVEL_MODE@, 
92     'with-STANDALONE' => 1,
93     'with-GPG' => @RT_GPG@,
94     'with-ICAL' => 1,
95     'with-SMTP' => 1,
96     'with-GRAPHVIZ' => @RT_GRAPHVIZ@,
97     'with-GD' => @RT_GD@,
98     'with-DASHBOARDS' => 1
99 );
100 $args{$_} = $default{$_} foreach grep !exists $args{$_}, keys %default;
101
102 {
103   my $section;
104   my %always_show_sections = (
105     perl => 1,
106     users => 1,
107   );
108
109   sub section {
110     my $s = shift;
111     $section = $s;
112     print "$s:\n" unless $args{'list-deps'};
113   }
114
115   sub print_found {
116     my $msg = shift;
117     my $test = shift;
118     my $extra = shift;
119
120     unless ( $args{'list-deps'} ) {
121         if ( $args{'v'} or not $test or $always_show_sections{$section} ) {
122             print "\t$msg ...";
123             print $test ? "found" : "MISSING";
124             print "\n";
125         }
126
127         print "\t\t$extra\n" if defined $extra;
128     }
129   }
130 }
131
132 sub conclude {
133     my %missing_by_type = @_;
134
135     unless ( $args{'list-deps'} ) {
136         unless ( keys %missing_by_type ) {
137             print "\nAll dependencies have been found.\n";
138             return;
139         }
140
141         print "\nSOME DEPENDENCIES WERE MISSING.\n";
142
143         for my $type ( keys %missing_by_type ) {
144             my $missing = $missing_by_type{$type};
145
146             print "$type missing dependencies:\n";
147             for my $name ( keys %$missing ) {
148                 my $module  = $missing->{$name};
149                 my $version = $module->{version};
150                 print_found( $name . ( $version ? " >= $version" : "" ),
151                     0, $module->{error} );
152             }
153         }
154         exit 1;
155     }
156 }
157
158
159 sub help {
160
161     print <<'.';
162
163 By default, testdeps determine whether you have 
164 installed all the perl modules RT needs to run.
165
166     --install           Install missing modules
167
168 The following switches will tell the tool to check for specific dependencies
169
170     --with-mysql        Database interface for MySQL
171     --with-postgresql   Database interface for PostgreSQL 
172     --with-oracle       Database interface for Oracle
173     --with-sqlite       Database interface and driver for SQLite (unsupported)
174
175     --with-standalone   Libraries needed to support the standalone simple pure perl server
176     --with-fastcgi      Libraries needed to support the fastcgi handler
177     --with-speedycgi    Libraries needed to support the speedycgi handler
178     --with-modperl1     Libraries needed to support the modperl 1 handler
179     --with-modperl2     Libraries needed to support the modperl 2 handler
180
181     --with-dev          Tools needed for RT development
182
183 You can also specify -v or --verbose to list the status of all dependencies,
184 rather than just the missing ones.
185
186 The "RT_FIX_DEPS_CMD" environment variable, if set, will be used
187 instead of the standard CPAN shell by --install to install any
188 required modules.  It will be called with the module name, or, if
189 "RT_FIX_DEPS_CMD" contains a "%s", will replace the "%s" with the
190 module name before calling the program.
191 .
192 }
193
194
195 sub text_to_hash {
196     my %hash;
197     for my $line ( split /\n/, $_[0] ) {
198         my($key, $value) = $line =~ /(\S+)\s*(\S*)/;
199         $value ||= '';
200         $hash{$key} = $value;
201     }
202
203     return %hash;
204 }
205
206 $deps{'CORE'} = [ text_to_hash( << '.') ];
207 Digest::base
208 Digest::MD5 2.27
209 DBI 1.37
210 Class::ReturnValue 0.40
211 DBIx::SearchBuilder 1.54
212 Text::Template 1.44
213 File::ShareDir
214 File::Spec 0.8
215 HTML::Entities 
216 HTML::Scrubber 0.08
217 Log::Dispatch 2.0
218 Sys::Syslog 0.16
219 Locale::Maketext 1.06
220 Locale::Maketext::Lexicon 0.32
221 Locale::Maketext::Fuzzy
222 MIME::Entity 5.425
223 Mail::Mailer 1.57
224 Email::Address
225 Text::Wrapper 
226 Time::ParseDate
227 Time::HiRes 
228 File::Temp 0.18
229 Text::Quoted 2.02
230 Tree::Simple 1.04
231 UNIVERSAL::require
232 Regexp::Common
233 Scalar::Util
234 Module::Versions::Report 1.05
235 Cache::Simple::TimedExpiry
236 Calendar::Simple
237 Encode 2.21
238 CSS::Squish 0.06
239 File::Glob
240 Devel::StackTrace 1.19
241 .
242
243 $deps{'MASON'} = [ text_to_hash( << '.') ];
244 HTML::Mason 1.36
245 Errno
246 Digest::MD5 2.27
247 CGI::Cookie 1.20
248 Storable 2.08
249 Apache::Session 1.53
250 XML::RSS 1.05
251 Text::WikiFormat 0.76
252 CSS::Squish 0.06
253 Devel::StackTrace 1.19
254 .
255
256 $deps{'STANDALONE'} = [ text_to_hash( << '.') ];
257 HTTP::Server::Simple 0.34
258 HTTP::Server::Simple::Mason 0.09
259 Net::Server
260 .
261
262 $deps{'MAILGATE'} = [ text_to_hash( << '.') ];
263 HTML::TreeBuilder
264 HTML::FormatText
265 Getopt::Long
266 LWP::UserAgent
267 Pod::Usage
268 .
269
270 $deps{'CLI'} = [ text_to_hash( << '.') ];
271 Getopt::Long 2.24
272 LWP
273 HTTP::Request::Common
274 Text::ParseWords
275 Term::ReadLine
276 Term::ReadKey
277 .
278
279 $deps{'DEV'} = [ text_to_hash( << '.') ];
280 HTML::Form
281 HTML::TokeParser
282 WWW::Mechanize
283 Test::WWW::Mechanize 1.04
284 Module::Refresh 0.03
285 Test::Expect 0.31
286 XML::Simple
287 File::Find
288 Test::Deep 0 # needed for shredder tests
289 String::ShellQuote 0 # needed for gnupg-incoming.t
290 Test::HTTP::Server::Simple 0.09
291 Test::HTTP::Server::Simple::StashWarnings 0.02
292 Log::Dispatch::Perl
293 Test::Warn
294 Test::Builder 0.77 # needed to fix TODO test
295 IPC::Run3
296 Test::MockTime
297 HTTP::Server::Simple::Mason 0.13
298 .
299
300 $deps{'FASTCGI'} = [ text_to_hash( << '.') ];
301 CGI 3.38
302 FCGI
303 CGI::Fast 
304 .
305
306 $deps{'SPEEDYCGI'} = [ text_to_hash( << '.') ];
307 CGI 3.38
308 CGI::SpeedyCGI
309 .
310
311
312 $deps{'MODPERL1'} = [ text_to_hash( << '.') ];
313 CGI 3.38
314 Apache::Request
315 Apache::DBI 0.92
316 .
317
318 $deps{'MODPERL2'} = [ text_to_hash( << '.') ];
319 CGI 3.38
320 Apache::DBI
321 HTML::Mason 1.36
322 .
323
324 $deps{'MYSQL'} = [ text_to_hash( << '.') ];
325 DBD::mysql 2.1018
326 .
327
328 $deps{'ORACLE'} = [ text_to_hash( << '.') ];
329 DBD::Oracle
330 .
331
332 $deps{'POSTGRESQL'} = [ text_to_hash( << '.') ];
333 DBD::Pg 1.43
334 .
335
336 $deps{'SQLITE'} = [ text_to_hash( << '.') ];
337 DBD::SQLite 1.00
338 .
339
340 $deps{'GPG'} = [ text_to_hash( << '.') ];
341 GnuPG::Interface
342 PerlIO::eol
343 .
344
345 $deps{'ICAL'} = [ text_to_hash( << '.') ];
346 Data::ICal
347 .
348
349 $deps{'SMTP'} = [ text_to_hash( << '.') ];
350 Net::SMTP
351 .
352
353 $deps{'DASHBOARDS'} = [ text_to_hash( << '.') ];
354 HTML::RewriteAttributes 0.02
355 MIME::Types
356 .
357
358 $deps{'GRAPHVIZ'} = [ text_to_hash( << '.') ];
359 GraphViz
360 IPC::Run
361 IPC::Run::SafeHandles
362 .
363
364 $deps{'GD'} = [ text_to_hash( << '.') ];
365 GD
366 GD::Graph
367 GD::Text
368 .
369
370 if ($args{'download'}) {
371
372     download_mods();
373 }
374
375
376 check_perl_version();
377
378 check_users();
379
380 my %Missing_By_Type = ();
381 foreach my $type (sort grep $args{$_}, keys %args) {
382     next unless ($type =~ /^with-(.*?)$/);
383
384     $type = $1;
385     section("$type dependencies");
386
387     my @missing;
388     my @deps = @{ $deps{$type} };
389
390     my %missing = test_deps(@deps);
391
392     if ( $args{'install'} ) {
393         for my $module (keys %missing) {
394             resolve_dep($module, $missing{$module}{version});
395             delete $missing{$module} if test_dep($module, $missing{$module}{version});
396         }
397     }
398
399     $Missing_By_Type{$type} = \%missing if keys %missing;
400 }
401
402 conclude(%Missing_By_Type);
403
404 sub test_deps {
405     my @deps = @_;
406
407     my %missing;
408     while(@deps) {
409         my $module = shift @deps;
410         my $version = shift @deps;
411         my($test, $error) = test_dep($module, $version);
412         my $msg = $module . ($version ? " >= $version" : '');
413         print_found($msg, $test, $error);
414
415         $missing{$module} = { version => $version, error => $error } unless $test;
416     }
417
418     return %missing;
419 }
420
421 sub test_dep {
422     my $module = shift;
423     my $version = shift;
424
425     if ( $args{'list-deps'} ) {
426         print $module, ': ', $version || 0, "\n"; 
427     }
428     else {
429         eval "use $module $version ()";
430         if ($@) {
431             my $error = $@;
432             $error =~ s/\n(.*)$//s;
433             $error =~ s/at \(eval \d+\) line \d+\.$//;
434             undef $error unless $error =~ /this is only/;
435
436             return ( 0, $error );
437         }
438         else {
439             return 1;
440         }
441     }
442 }
443
444 sub resolve_dep {
445     my $module = shift;
446     my $version = shift;
447
448     print "\nInstall module $module\n";
449
450     my $ext = $ENV{'RT_FIX_DEPS_CMD'};
451     unless( $ext ) {
452         my $configured = 1;
453         {
454             local @INC = @INC;
455             if ( $ENV{'HOME'} ) {
456                 unshift @INC, "$ENV{'HOME'}/.cpan";
457             }
458             $configured = eval { require CPAN::MyConfig } || eval { require CPAN::Config };
459         }
460         unless ( $configured ) {
461             print <<END;
462 You haven't configured the CPAN shell yet.
463 Please run `@PERL@ -MCPAN -e shell` to configure it.
464 END
465             exit(1);
466         }
467         my $rv = eval { require CPAN; CPAN::Shell->install($module) };
468         return $rv unless $@;
469
470         print <<END;
471 Failed to load module CPAN.
472
473 -------- Error ---------
474 $@
475 ------------------------
476
477 When we tried to start installing RT's perl dependencies, 
478 we were unable to load the CPAN client. This module is usually distributed
479 with Perl. This usually indicates that your vendor has shipped an unconfigured
480 or incorrectly configured CPAN client.
481 The error above may (or may not) give you a hint about what went wrong
482
483 You have several choices about how to install dependencies in 
484 this situatation:
485
486 1) use a different tool to install dependencies by running setting the following
487    shell environment variable and rerunning this tool:
488     RT_FIX_DEPS_CMD='@PERL@ -MCPAN -e"install %s"'
489 2) Attempt to configure CPAN by running:
490    `@PERL@ -MCPAN -e shell` program from shell.
491    If this fails, you may have to manually upgrade CPAN (see below)
492 3) Try to update the CPAN client. Download it from:
493    http://search.cpan.org/dist/CPAN and try again
494 4) Install each dependency manually by downloading them one by one from
495    http://search.cpan.org
496
497 END
498         exit(1);
499     }
500
501     if( $ext =~ /\%s/) {
502         $ext =~ s/\%s/$module/g; # sprintf( $ext, $module );
503     } else {
504         $ext .= " $module";
505     }
506     print "\t\tcommand: '$ext'\n";
507     return scalar `$ext 1>&2`;
508 }
509
510 sub download_mods {
511     my %modules;
512     use CPAN;
513     
514     foreach my $key (keys %deps) {
515         my @deps = (@{$deps{$key}});
516         while (@deps) {
517             my $mod = shift @deps;
518             my $ver = shift @deps;
519             next if ($mod =~ /^(DBD-|Apache-Request)/);
520             $modules{$mod} = $ver;
521         }
522     }
523     my @mods = keys %modules;
524     CPAN::get();
525     my $moddir = $args{'download'};
526     foreach my $mod (@mods) {
527         $CPAN::Config->{'build_dir'} = $moddir;
528         CPAN::get($mod);
529     }
530
531     opendir(DIR, $moddir);
532     while ( my $dir = readdir(DIR)) {
533         print "Dir is $dir\n";
534         next if ( $dir =~ /^\.\.?$/);
535
536         # Skip things we've previously tagged
537         my $out = `svn ls $args{'repository'}/tags/$dir`;
538         next if ($out);
539
540         if ($dir =~ /^(.*)-(.*?)$/) {
541             `svn_load_dirs -no_user_input -t tags/$dir -v $args{'repository'} dists/$1 $moddir/$dir`;
542             `rm -rf $moddir/$dir`;
543
544         }
545
546     }
547     closedir(DIR);
548     exit;
549 }
550
551 sub check_perl_version {
552   section("perl");
553   eval {require 5.008003};
554   if ($@) {
555     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.");
556     exit(1);
557   } else {
558     print_found( sprintf(">=5.8.3(%vd)", $^V), 1 );
559   }
560 }
561
562 sub check_users {
563   section("users");
564   print_found("rt group (@RTGROUP@)",      defined getgrnam("@RTGROUP@"));
565   print_found("bin owner (@BIN_OWNER@)",   defined getpwnam("@BIN_OWNER@"));
566   print_found("libs owner (@LIBS_OWNER@)", defined getpwnam("@LIBS_OWNER@"));
567   print_found("libs group (@LIBS_GROUP@)", defined getgrnam("@LIBS_GROUP@"));
568   print_found("web owner (@WEB_USER@)",    defined getpwnam("@WEB_USER@"));
569   print_found("web group (@WEB_GROUP@)",   defined getgrnam("@WEB_GROUP@"));
570 }
571
572
573
574 1;