#!/bin/sh # -*-perl-*- # ====================================================================== # # Run the right perl version: if [ -x /usr/local/bin/perl ]; then perl=/usr/local/bin/perl elif [ -x /usr/bin/perl ]; then perl=/usr/bin/perl else perl=`which perl| sed 's/.*aliased to *//'` fi exec $perl -x -S $0 "$@" # -x: start from the following line # ====================================================================== # #! /Good_Path/perl -w # line 17 # # Name: pencil-test # Author: wd (Wolfgang.Dobler@kis.uni-freiburg.de) # $Date: 2008-07-11 05:02:53 $ # $Revision: 1.40 $ # Description: # Run the pencil code's auto test on some remote host(s) and wrap # results in HTML if required. Can be used from crontab. # Usage: # pencil-test [options] host1 [host2 [host3 ..]] # pencil-test -l [options] # Options: # -h, --help -- Help # -l, --local -- Run on the local host # -H, --html -- Write output as elementary HTML (mostly
) # -X, --xml -- Write output as XML # -u, --update -- Update everything before compiling # -r, --reload -- Do 'touch RELOAD' after every time step # -s, --short -- Use short directory names # -c, --clean -- 'make cleann' before compiling # -D\n, # --pencil-home= -- set PENCIL_HOME directory to DIR # -N , --nice= -- Run tests with nice value of # -i , # --infofile= -- After last test, print content of # -m , # --mail= -- Specify who to send mail to if tests fail # -M , # --Mailer= -- Specify e-mail program to use # -p , # --postproc= -- Use postprocessor for output from make # --script-tests=TYPES -- Run script tests matching TYPES, a # comma-separated list of types # 'type1,type2,...', or a map # (comma-separated list of colon-separated # associations # 'type1:interp1,type2:interp2,...'. Use # 'DEFAULT' to get all configured types # with their default interpreters # -t , # --time-limit= -- Limit total time for each {start,run}.x phase # -T , # --timings-file= -- Append timings to # -b # --use-pc_auto-test -- Run pc_auto-test as backend, not auto-test # (mnemonic: compile with 'build', not 'make'). # Eventually, this will become the default. # -Wa, # --auto-test-options= # -- Pass to pc_auto-test. If # contains commas, it is split into multiple # options at the commas # # Email list syntax: # -m '{-:fluxrings},defaulty@any.where,nosy@some.where{+:fluxrings,+:rad1},ignorant@else.where{-:hydro.*}' # will not send mail if 'interlocked-fluxrings' fails -- except for Nosy # who explicitly requested this. Ignorant will not get emails if only 'hydro1' # or 'hydrogen-bond' fail. Note that the patterns are anchored at the end, # but not at the beginning. # Sample crontab entry: # 30 02 * * * cd $HOME/pencil-auto-test && svn up && bin/pencil-test --clean --html --mail=my@email.net --nice=15 --reload --short --timings-file=$HOME/public_html/autotest_timings.txt > $HOME/public_html/autotest.html use strict; use POSIX qw(strftime); use Getopt::Long; use IPC::Open2; use constant SUCCESS => 1; # return value my (%opts); # Variables written by GetOptions my (%emails, %pos, %neg, %sendmailto); # Info on mailing if tests fail ## Process command line eval { Getopt::Long::config("bundling"); }; GetOptions(\%opts, qw( -h --help --debug -v --version -l --local -H --html -X --xml -u --update -U -r --reload -s --short -c --clean -D=s --pencil-home=s -N=s --nice=s -i=s --infofile=s -m=s --mail=s -M=s --Mailer=s -p=s --postproc=s --script-tests=s -t=s --time-limit=s -T=s --timings-file=s --tag-stable -b --use-pc_auto-test -W=s --auto-test-options=s )) or die "Aborting.\n"; die '$Id$ ' . "\n" if ($opts{'v'} || $opts{'version'}); die usage() if ($opts{'h'} || $opts{'help'}); my $debug = ( $opts{'debug'} || 0 ); my $html = ($opts{'H'} || $opts{'html'} || 0 ); my $local = ($opts{'l'} || $opts{'local'} || 0 ); my $clean = ($opts{'c'} || $opts{'clean'} || 0 ); my $pencil_home = ($opts{'D'} || $opts{'pencil-home'} || '$PENCIL_HOME' || `pwd`); my $nice = ($opts{'N'} || $opts{'nice'} || 0 ); my $infofile = ($opts{'i'} || $opts{'infofile'} || '' ); my $update = ($opts{'u'} || $opts{'update'} || $opts{'U'} || 0 ); # keeping -U for backwards compatibility my $reload = ($opts{'r'} || $opts{'reload'} || 0 ); my $short = ($opts{'s'} || $opts{'short'} || 0 ); my $tag_stable = ( $opts{'tag-stable'} || 0 ); my $emails = ($opts{'m'} || $opts{'mail'} || ''); my $mailer = ($opts{'M'} || $opts{'Mailer'} || ''); my $postproc = ($opts{'p'} || $opts{'postproc'} || ''); my $script_tests = ( $opts{'script-tests'} || ''); my $time_limit = ($opts{'t'} || $opts{'time-limit'} || ''); my $timingsfile = ($opts{'T'} || $opts{'timings-file'}|| ''); my $with_xml = ($opts{'x'} || $opts{'xml'} || ''); my $use_build = ($opts{'b'} || $opts{'use-pc_auto-test'} || ''); my @auto_test_options = (); if (defined $opts{'W'}) { my $atest_opts = $opts{'W'}; if ($atest_opts =~ /a,(.*)/) { push @auto_test_options, split('\s*,\s*', $1); } else { die "Unknown option <-W$atest_opts>\n"; } } if (defined $opts{'auto-test-options'}) { push @auto_test_options, split('\s*,\s*', $opts{'auto-test-options'}); } my $blurb = "[This message was automatically generated by the 'pencil-test' script]\n"; my ($xml); # Too few or too many arguments? if ($local) { die "No host arguments allowed with -l option.\n" if (@ARGV); } else { die usage() unless (@ARGV); } # Make sure we have /usr/sbin in path (which is where sendmail is often located) $ENV{PATH} = "$ENV{PATH}:/usr/sbin" unless ($ENV{PATH} =~ m{(^|:)/usr/sbin($|:)}); # Do not make a mess if another pencil-test is already running in this directory! my $instances_running = `ps aux | grep -v "grep" | grep "perl" | grep "$0" | wc -l`; $instances_running =~ s/\s+$//s; if ($instances_running > 1) { die ("ERROR: another 'pencil-test' is already running here!\n"); } print_header(); ## Run test(s) my $first = 1; if ($local) { run_tests_on(`hostname`); } else { foreach my $host (@ARGV) { run_tests_on($host) } } print_footer(); # ====================================================================== # sub run_tests_on { # Run auto tests remotely (unless $local is true) on the given host my ($host) = @_; chomp $host; print STDERR "run_test_on($host)" if ($debug); my ($shost) = ($host =~ /([^.]*)/); # host name sans domain $shost = "\u$shost"; if ($html) { unless ($first) { print "
\n
\n" }; print "
\n"; } else { unless ($first) { print "\n\n" }; print " $host:\n================\n"; } ## Construct and execute remote command my @cmd; if ($local) { @cmd = ("sh"); } else { @cmd = ("ssh", "-x", "$host", "sh"); } my $remcmd = build_remote_cmd(); if ($debug) { print STDERR "open2(*POUT, *PIN, \"@cmd\")\n" }; open2(*POUT, *PIN, "@cmd"); if ($debug) { print STDERR "print PIN $remcmd\n" }; print PIN "$remcmd\n"; close PIN; if ($timingsfile ne '') { open(TIMINGS, ">> $timingsfile") or warn "Couldn't open $timingsfile for writing\n"; } my ($line,$timings,$result,$update_error); while (defined($line=$date END_FOOT } if ($with_xml) { $xml.=<<"END_XMLFOOT";)) { # Extract timing lines if ($line =~ /^#TIMING\s+(.*)/) { print "<$line>\n" if ($debug); print TIMINGS "$1\n"; next; } # Extract maintainers line if ($line =~ /Maintainers of failed tests:\s*(.*?)\s*$/) { parse_maintainers($1); } # Identify errors if ($line =~ /^UPDATE ERROR:/) { $update_error = 1; } # Print all other lines print $line; $result .= $line; } close POUT; if ($timingsfile ne '') { close TIMINGS; } # Create a stable tag if auto-test was successful tag_stable_on_success($result,$host) if ($tag_stable); # Send emails if necessary notify_on_error($result,$shost,$update_error); $first = 0; } # ---------------------------------------------------------------------- # sub build_remote_cmd { # Construct the command to send to the remote host my $remcmd = "cd $pencil_home; "; if ($update) { $remcmd .= '' . 'if [ -e .git ]; then' # .git can be a directory or a file . ' (git stash -q -u || echo "UPDATE ERROR: git stash");' . ' (git fetch -q || echo "UPDATE ERROR: git fetch");' # . ' (git reset -q --hard @{u} || echo "UPDATE ERROR: git reset");' . ' (git pull --rebase -q || echo "UPDATE ERROR: git pull");' . ' printf "Updated to git revision %s\n" $(git rev-parse HEAD);' . 'elif [ -d .svn ]; then' . ' (svn -q update || echo "UPDATE ERROR: svn update failed");' . 'else' . ' echo "UPDATE ERROR: Neither git nor svn checkout";' . ' false;' . 'fi;' } if ($use_build) { $remcmd .= "env PENCIL_HOME=$pencil_home ./bin/pc_auto-test "; } else { $remcmd .= "env PENCIL_HOME=$pencil_home ./bin/auto-test "; } $remcmd .= "--time "; if ($emails) { $remcmd .= "--list-maintainers "; } if ($with_xml) { $remcmd .= "--xml " }; if ($clean) { $remcmd .= "--clean " }; if ($reload) { $remcmd .= "--reload " }; if ($short) { $remcmd .= "--short " }; if ($nice) { $remcmd .= "--nice=$nice " }; if ($infofile ne '') { $remcmd .= "--infofile=$infofile " }; if ($postproc) { $remcmd .= "--postproc=\"$postproc\" " }; if ($script_tests ne '') { $remcmd .= "--script-tests=\"$script_tests\" " }; if ($time_limit ne '') { $remcmd .= "--time-limit=\"$time_limit\" " }; if ($timingsfile ne '') { $remcmd .= "--summarize-times " }; if (@auto_test_options) { $remcmd .= "@auto_test_options; "}; return $remcmd; } # ---------------------------------------------------------------------- # sub print_header { my $date = scalar localtime(); my $xml = ""; if ($with_xml) { $xml=<<"END_XMLHEAD"; $date END_XMLHEAD } if ($html) { print <<"END_HEAD"; $dateEND_HEAD } else { print "$date\n\n"; } } # ---------------------------------------------------------------------- # sub print_footer { my $date = scalar localtime(); if ($html) { print <<"END_FOOT";