#!/usr/bin/perl #################################################################################################################################### # CI Test Wrapper #################################################################################################################################### #################################################################################################################################### # Perl includes #################################################################################################################################### use strict; use warnings FATAL => qw(all); use Carp qw(confess longmess); use English '-no_match_vars'; # Convert die to confess to capture the stack trace $SIG{__DIE__} = sub { Carp::confess @_ }; use File::Basename qw(dirname); use Getopt::Long qw(GetOptions); use Cwd qw(abs_path); use Pod::Usage qw(pod2usage); use lib dirname($0) . '/lib'; use lib dirname(dirname($0)) . '/lib'; use lib dirname(dirname($0)) . '/doc/lib'; use pgBackRestDoc::Common::Exception; use pgBackRestDoc::Common::Log; use pgBackRestDoc::ProjectInfo; use pgBackRestTest::Common::ContainerTest; use pgBackRestTest::Common::ExecuteTest; use pgBackRestTest::Common::VmTest; #################################################################################################################################### # Usage #################################################################################################################################### =head1 NAME ci.pl - CI Test Wrapper =head1 SYNOPSIS test.pl [options] doc|test VM Options: --vm docker container to build/test --param parameters to pass to test.pl --sudo test requires sudo --no-tempfs do not create tempfs mount General Options: --help display usage and exit =cut #################################################################################################################################### # Command line parameters #################################################################################################################################### my $strVm; my @stryParam; my $bNoTempFs; my $bSudo; my $bHelp; GetOptions ('help' => \$bHelp, 'param=s@' => \@stryParam, 'no-tempfs' => \$bNoTempFs, 'sudo' => \$bSudo, 'vm=s' => \$strVm) or pod2usage(2); #################################################################################################################################### # Begin/end functions to track timing #################################################################################################################################### my $lProcessBegin; my $strProcessTitle; sub processBegin { $strProcessTitle = shift; &log(INFO, "Begin ${strProcessTitle}"); $lProcessBegin = time(); } sub processExec { my $strCommand = shift; my $rhParam = shift; &log(INFO, " Exec ${strCommand}"); executeTest($strCommand, $rhParam); } sub processEnd { &log(INFO, " End ${strProcessTitle} (" . (time() - $lProcessBegin) . 's)'); } #################################################################################################################################### # Run in eval block to catch errors #################################################################################################################################### eval { # Display version and exit if requested if ($bHelp) { syswrite(*STDOUT, "CI Test Wrapper\n"); syswrite(*STDOUT, "\n"); pod2usage(); exit 0; } if (@ARGV != 1) { syswrite(*STDOUT, "test|doc required\n\n"); pod2usage(); } # VM must be defined if (!defined($strVm)) { confess &log(ERROR, '--vm is required'); } ################################################################################################################################ # Paths ################################################################################################################################ my $strBackRestBase = dirname(dirname(abs_path($0))); my $strReleaseExe = "${strBackRestBase}/doc/release.pl"; my $strTestExe = "${strBackRestBase}/test/test.pl"; logLevelSet(INFO, INFO, OFF); processBegin('install common packages'); processExec('sudo apt-get -qq update', {bSuppressStdErr => true, bSuppressError => true}); processExec( 'sudo DEBIAN_FRONTEND=noninteractive apt-get install -y libxml-checker-perl libyaml-perl', {bSuppressStdErr => true}); processEnd(); if (!$bNoTempFs) { processBegin('mount tmpfs'); processExec('mkdir -p -m 770 test'); processExec('sudo mount -t tmpfs -o size=2048m tmpfs test'); processExec('df -h test', {bShowOutputAsync => true}); processEnd(); } ################################################################################################################################ # Build documentation ################################################################################################################################ my $strUser = getpwuid($UID); if ($ARGV[0] eq 'doc') { if ($strVm eq VM_RH7 || $strVm eq VM_RH8) { processBegin('LaTeX install'); processExec( 'sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends texlive-latex-base' . ' texlive-latex-extra texlive-fonts-recommended', {bSuppressStdErr => true}); processExec( 'sudo DEBIAN_FRONTEND=noninteractive apt-get install -y texlive-font-utils texlive-latex-recommended', {bSuppressStdErr => true}); } processBegin('remove sudo'); processExec("sudo rm /etc/sudoers.d/${strUser}"); processEnd(); processBegin('create link from home to repo for contributing doc'); processExec("ln -s ${strBackRestBase} \${HOME?}/" . PROJECT_EXE); processEnd(); processBegin('release documentation'); processExec("${strReleaseExe} --build --no-gen --vm=${strVm}", {bShowOutputAsync => true, bOutLogOnError => false}); processEnd(); } ################################################################################################################################ # Run test ################################################################################################################################ elsif ($ARGV[0] eq 'test') { # Build list of packages that need to be installed my $strPackage = "make gcc ccache meson python3-pip git rsync zlib1g-dev libssl-dev libxml2-dev libpq-dev libyaml-dev pkg-config" . " uncrustify libssh2-1-dev"; # Add lcov when testing coverage if (vmCoverageC($strVm)) { $strPackage .= " lcov"; } # Extra packages required when testing without containers if ($strVm eq VM_NONE) { $strPackage .= " valgrind liblz4-dev liblz4-tool zstd libzstd-dev bzip2 libbz2-dev"; } # Else packages needed for integration tests on containers else { $strPackage .= " libdbd-pg-perl"; } processBegin('/tmp/pgbackrest owned by root so tests cannot use it'); processExec('sudo mkdir -p /tmp/pgbackrest && sudo chown root:root /tmp/pgbackrest && sudo chmod 700 /tmp/pgbackrest'); processEnd(); processBegin('install test packages'); processExec( "sudo DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y ${strPackage}", {bSuppressStdErr => true}); processEnd(); if (!$bSudo) { processBegin('remove sudo'); processExec("sudo rm /etc/sudoers.d/${strUser}"); processEnd(); } # Build the container if ($strVm ne VM_NONE) { processBegin("${strVm} build"); processExec("${strTestExe} --vm-build --vm=${strVm}", {bShowOutputAsync => true, bOutLogOnError => false}); processEnd(); } processBegin(($strVm eq VM_NONE ? "no container" : $strVm) . ' test'); processExec( "${strTestExe} --gen-check --log-level-test-file=off --no-coverage-report --vm-max=2 --vm=${strVm}" . (@stryParam != 0 ? " --" . join(" --", @stryParam) : ''), {bShowOutputAsync => true, bOutLogOnError => false}); processEnd(); } ################################################################################################################################ # Catch error ################################################################################################################################ else { confess &log(ERROR, 'invalid command ' . $ARGV[0]); } &log(INFO, "CI Complete"); # Exit with success exit 0; } #################################################################################################################################### # Check for errors #################################################################################################################################### or do { # If a backrest exception then return the code exit $EVAL_ERROR->code() if (isException(\$EVAL_ERROR)); # Else output the unhandled error print $EVAL_ERROR; exit ERROR_UNHANDLED; }; # It shouldn't be possible to get here &log(ASSERT, 'execution reached invalid location in ' . __FILE__ . ', line ' . __LINE__); exit ERROR_ASSERT;