automation should not need slave/ftp on master b=415970 r=nthomas

This commit is contained in:
rhelmer@mozilla.com 2008-04-03 19:28:42 -07:00
Родитель 26044cf326
Коммит 114f0b66af
9 изменённых файлов: 136 добавлений и 206 удалений

Просмотреть файл

@ -10,7 +10,7 @@ use POSIX qw(strftime);
use File::Temp qw(tempfile);
use Bootstrap::Config;
use Bootstrap::Util;
use Bootstrap::Util qw(CvsCatfile);
use MozBuild::Util qw(RunShellCommand Email);
use base 'Exporter';
@ -307,4 +307,41 @@ sub CvsCo {
$this->Shell(%cvsCoArgs);
}
sub CreateCandidatesDir() {
my $this = shift;
my $config = new Bootstrap::Config();
my $stagingUser = $config->Get(var => 'stagingUser');
my $stagingServer = $config->Get(var => 'stagingServer');
my $candidateDir = $config->GetFtpCandidateDir(bitsUnsigned => 1);
$this->Shell(
cmd => 'ssh',
cmdArgs => ['-2', '-l', $stagingUser, $stagingServer,
'mkdir -p ' . $candidateDir],
);
# Make sure permissions are created on the server correctly;
#
# Note the '..' at the end of the chmod string; this is because
# Config::GetFtpCandidateDir() returns the full path, including the
# rcN directories on the end. What we really want to ensure
# have the correct permissions (from the mkdir call above) is the
# firefox/nightly/$version-candidates/ directory.
#
# XXX - This is ugly; another solution is to fix the umask on stage, or
# change what GetFtpCandidateDir() returns.
my $chmodArg = CvsCatfile($config->GetFtpCandidateDir(bitsUnsigned => 0),
'..');
$this->Shell(
cmd => 'ssh',
cmdArgs => ['-2', '-l', $stagingUser, $stagingServer,
'chmod 0755 ' . $chmodArg],
);
}
1;

Просмотреть файл

@ -6,7 +6,7 @@ package Bootstrap::Step::Build;
use File::Temp qw(tempfile);
use Bootstrap::Step;
use Bootstrap::Util qw(CvsCatfile SyncToStaging);
use Bootstrap::Util qw(CvsCatfile);
use MozBuild::Util qw(MkdirWithPath);
@ -136,9 +136,9 @@ sub Push {
$pushDir =~ s!^http://ftp.mozilla.org/pub/mozilla.org!/home/ftp/pub!;
my $candidateDir = $config->GetFtpCandidateDir(bitsUnsigned => 1);
$this->CreateCandidatesDir();
my $osFileMatch = $config->SystemInfo(var => 'osname');
# TODO - use a more generic function for this kind of remapping
if ($osFileMatch eq 'win32') {
$osFileMatch = 'win';
@ -146,34 +146,6 @@ sub Push {
$osFileMatch = 'mac';
}
$this->Shell(
cmd => 'ssh',
cmdArgs => ['-2', '-l', $stagingUser, $stagingServer,
'mkdir -p ' . $candidateDir],
logFile => $pushLog,
);
# Make sure permissions are created on the server correctly;
#
# Note the '..' at the end of the chmod string; this is because
# Config::GetFtpCandidateDir() returns the full path, including the
# rcN directories on the end. What we really want to ensure
# have the correct permissions (from the mkdir call above) is the
# firefox/nightly/$version-candidates/ directory.
#
# XXX - This is ugly; another solution is to fix the umask on stage, or
# change what GetFtpCandidateDir() returns.
my $chmodArg = CvsCatfile($config->GetFtpCandidateDir(bitsUnsigned => 0),
'..');
$this->Shell(
cmd => 'ssh',
cmdArgs => ['-2', '-l', $stagingUser, $stagingServer,
'chmod 0755 ' . $chmodArg],
logFile => $pushLog,
);
$this->Shell(
cmd => 'ssh',
cmdArgs => ['-2', '-l', $stagingUser, $stagingServer,
@ -184,8 +156,6 @@ sub Push {
$candidateDir],
logFile => $pushLog,
);
SyncToStaging();
}
sub Announce {

Просмотреть файл

@ -94,7 +94,6 @@ sub BumpPatcherConfig {
my $patcherConfig = $config->Get(var => 'patcherConfig');
my $stagingUser = $config->Get(var => 'stagingUser');
my $stagingServer = $config->Get(var => 'stagingServer');
my $externalStagingServer = $config->Get(var => 'externalStagingServer');
my $ftpServer = $config->Get(var => 'ftpServer');
my $bouncerServer = $config->Get(var => 'bouncerServer');
my $logDir = $config->Get(sysvar => 'logDir');
@ -182,7 +181,7 @@ sub BumpPatcherConfig {
'.%locale%.%platform%.partial.mar');
$partialUpdate->{'betatest-url'} =
'http://' . $externalStagingServer. '/pub/mozilla.org/' . $product .
'http://' . $stagingServer. '/pub/mozilla.org/' . $product .
'/nightly/' . $version . '-candidates/' . $rcStr . '/' . $product .
'-' . $oldVersion . '-' . $version . '.%locale%.%platform%.partial.mar';
@ -204,7 +203,7 @@ sub BumpPatcherConfig {
'.%locale%.%platform%.complete.mar');
$completeUpdate->{'betatest-url'} =
'http://' . $externalStagingServer . '/pub/mozilla.org/' . $product .
'http://' . $stagingServer . '/pub/mozilla.org/' . $product .
'/nightly/' . $version . '-candidates/' . $rcStr . '/' . $product .
'-' . $version . '.%locale%.%platform%.complete.mar';
@ -251,7 +250,7 @@ sub BumpPatcherConfig {
$releaseObj->{'locales'} = join(' ', sort (keys(%{$localeInfo})));
$releaseObj->{'completemarurl'} =
'http://' . $externalStagingServer . '/pub/mozilla.org/' . $product.
'http://' . $stagingServer . '/pub/mozilla.org/' . $product.
'/nightly/' . $version . '-candidates/' . $rcStr . '/' . $product . '-'.
$version . '.%locale%.%platform%.complete.mar',

Просмотреть файл

@ -5,7 +5,7 @@
package Bootstrap::Step::Repack;
use Bootstrap::Step;
use Bootstrap::Config;
use Bootstrap::Util qw(CvsCatfile SyncToStaging);
use Bootstrap::Util qw(CvsCatfile);
use MozBuild::Util qw(MkdirWithPath);
@ISA = ("Bootstrap::Step");
@ -206,21 +206,16 @@ sub Push {
$pushDir =~ s!^http://ftp.mozilla.org/pub/mozilla.org!/home/ftp/pub!;
my $candidateDir = $config->GetFtpCandidateDir(bitsUnsigned => 1);
$this->CreateCandidatesDir();
my $osFileMatch = $config->SystemInfo(var => 'osname');
my $osFileMatch = $config->SystemInfo(var => 'osname');
# TODO - use a more generic function for this kind of remapping
if ($osFileMatch eq 'win32') {
$osFileMatch = 'win';
} elsif ($osFileMatch eq 'macosx') {
$osFileMatch = 'mac';
}
$this->Shell(
cmd => 'ssh',
cmdArgs => ['-2', '-l', $stagingUser, $stagingServer,
'mkdir -p ' . $candidateDir],
logFile => $pushLog,
);
$this->Shell(
cmd => 'ssh',
cmdArgs => ['-2', '-l', $stagingUser, $stagingServer,
@ -231,8 +226,6 @@ sub Push {
$pushDir, $candidateDir],
logFile => $pushLog,
);
SyncToStaging();
}
sub Announce {

Просмотреть файл

@ -4,7 +4,6 @@
package Bootstrap::Step::Sign;
use Bootstrap::Step;
use Bootstrap::Config;
use Bootstrap::Util qw(SyncToStaging);
@ISA = ("Bootstrap::Step");
sub Execute {
@ -35,8 +34,4 @@ sub Announce {
);
}
sub Push {
SyncToStaging();
}
1;

Просмотреть файл

@ -8,7 +8,6 @@ use Bootstrap::Config;
use File::Copy qw(move);
use File::Find qw(find);
use MozBuild::Util qw(MkdirWithPath);
use Bootstrap::Util qw(SyncToStaging);
@ISA = ("Bootstrap::Step");
sub Execute {
@ -21,23 +20,23 @@ sub Execute {
my $version = $config->GetVersion(longName => 0);
my $rc = $config->Get(var => 'rc');
my $logDir = $config->Get(sysvar => 'logDir');
my $stageHome = $config->Get(var => 'stageHome');
my $sourceDir = $config->Get(var => 'sourceDir');
my $mozillaCvsroot = $config->Get(var => 'mozillaCvsroot');
# create staging area
my $stageDir = catfile($stageHome, $product . '-' . $version,
my $versionedSourceDir = catfile($sourceDir, $product . '-' . $version,
'batch-source', 'rc' . $rc);
if (not -d $stageDir) {
MkdirWithPath(dir => $stageDir)
or die("Cannot create $stageDir: $!");
if (not -d $versionedSourceDir) {
MkdirWithPath(dir => $versionedSourceDir)
or die("Cannot create $versionedSourceDir: $!");
}
$this->CvsCo(cvsroot => $mozillaCvsroot,
tag => $productTag . '_RELEASE',
modules => ['mozilla/client.mk',
catfile('mozilla', $appName, 'config')],
workDir => $stageDir,
workDir => $versionedSourceDir,
logFile => catfile($logDir, 'source.log')
);
@ -45,26 +44,27 @@ sub Execute {
cmd => 'make',
cmdArgs => ['-f', 'client.mk', 'checkout',
'MOZ_CO_PROJECT=' . $appName],
dir => catfile($stageDir, 'mozilla'),
dir => catfile($versionedSourceDir, 'mozilla'),
logFile => catfile($logDir, 'source.log'),
);
# change all CVS/Root files to anonymous CVSROOT
File::Find::find(\&CvsChrootCallback, catfile($stageDir, 'mozilla'));
File::Find::find(\&CvsChrootCallback, catfile($versionedSourceDir,
'mozilla'));
# remove leftover mozconfig files
unlink(glob(catfile($stageDir, 'mozilla', '.mozconfig*')));
unlink(glob(catfile($versionedSourceDir, 'mozilla', '.mozconfig*')));
my $tarFile = $product . '-' . $version . '-' . 'source' . '.tar.bz2';
$this->Shell(
cmd => 'tar',
cmdArgs => ['-cjf', $tarFile, 'mozilla'],
dir => catfile($stageDir),
dir => catfile($versionedSourceDir),
logFile => catfile($logDir, 'source.log'),
);
chmod(0644, glob("$stageDir/$tarFile"));
chmod(0644, glob("$versionedSourceDir/$tarFile"));
}
sub Verify {
@ -94,28 +94,24 @@ sub Push {
my $version = $config->GetVersion(longName => 0);
my $rc = $config->Get(var => 'rc');
my $logDir = $config->Get(sysvar => 'logDir');
my $stageHome = $config->Get(var => 'stageHome');
my $sourceDir = $config->Get(var => 'sourceDir');
my $stagingUser = $config->Get(var => 'stagingUser');
my $stagingServer = $config->Get(var => 'stagingServer');
my $stageDir = catfile($stageHome, $product . '-' . $version);
my $candidateDir = catfile('/home', 'ftp', 'pub', $product, 'nightly',
$version . '-candidates', 'rc' . $rc ) . '/';
my $candidateDir = $config->GetFtpCandidateDir(bitsUnsigned => 0);
if (not -d $candidateDir) {
MkdirWithPath(dir => $candidateDir)
or die("Could not mkdir $candidateDir: $!");
$this->Log(msg => "Created directory $candidateDir");
}
my $versionedSourceDir = catfile($sourceDir, $product . '-' . $version);
$this->CreateCandidatesDir();
$this->Shell(
cmd => 'rsync',
cmdArgs => ['-av', catfile('batch-source', 'rc' . $rc,
cmdArgs => ['-av', '-e', 'ssh', catfile('batch-source', 'rc' . $rc,
$product . '-' . $version . '-source.tar.bz2'),
$candidateDir],
$stagingUser . '@' . $stagingServer . ':' . $candidateDir],
logFile => catfile($logDir, 'source.log'),
dir => catfile($stageDir),
dir => catfile($versionedSourceDir),
);
SyncToStaging();
}
sub Announce {

Просмотреть файл

@ -13,7 +13,7 @@ use Cwd;
use Bootstrap::Step;
use Bootstrap::Config;
use Bootstrap::Util qw(CvsCatfile SyncToStaging);
use Bootstrap::Util qw(CvsCatfile);
use MozBuild::Util qw(MkdirWithPath);
@ -22,7 +22,7 @@ use MozBuild::Util qw(MkdirWithPath);
use strict;
#
# List of directories that are allowed to be in the prestage-trimmed directory,
# List of directories that are allowed to be in the stage-unsigned directory,
# theoretically because TrimCallback() will know what to do with them.
#
my @ALLOWED_DELIVERABLE_DIRECTORIES = qw(windows-xpi mac-xpi linux-xpi);
@ -190,6 +190,7 @@ sub Execute {
my $config = new Bootstrap::Config();
my $product = $config->Get(var => 'product');
my $productTag = $config->Get(var => 'productTag');
my $version = $config->GetVersion(longName => 0);
my $rc = $config->Get(var => 'rc');
my $logDir = $config->Get(sysvar => 'logDir');
@ -198,7 +199,9 @@ sub Execute {
my $mozillaCvsroot = $config->Get(var => 'mozillaCvsroot');
my $mofoCvsroot = $config->Get(var => 'mofoCvsroot');
my $releaseTag = $config->Get(var => 'productTag') . '_RELEASE';
my $stagingUser = $config->Get(var => 'stagingUser');
my $stagingServer = $config->Get(var => 'stagingServer');
## Prepare the staging directory for the release.
# Create the staging directory.
@ -282,39 +285,25 @@ sub Execute {
logFile => catfile($logDir, 'stage_merge_skel.log'),
dir => $stageDir,
);
# Collect the release files from the candidates directory into a prestage
# directory.
my $prestageDir = catfile($batch1Dir, 'prestage');
if (not -d $prestageDir) {
MkdirWithPath(dir => $prestageDir, mask => 0755)
or die "Cannot create $prestageDir: $!";
$this->Log(msg => "Created directory $prestageDir");
}
$this->Shell(
cmd => 'rsync',
cmdArgs => ['-Lav', catfile('/home', 'ftp', 'pub', $product, 'nightly',
$version . '-candidates', 'rc' . $rc ) .
'/',
'./'],
logFile => catfile($logDir, 'stage_collect.log'),
dir => $prestageDir
);
# Create a pruning/"trimmed" area; this area will be used to remove
# Collect the release files from the candidates directory into
# a pruning/"trimmed" area; this area will be used to remove
# locales and deliverables we don't ship.
my $ftpNightlyDir = $config->GetFtpCandidateDir(bitsUnsigned => 0);
$this->Shell(
cmd => 'rsync',
cmdArgs => ['-av', 'prestage/', 'prestage-trimmed/'],
logFile => catfile($logDir, 'stage_collect_trimmed.log'),
cmdArgs => ['-Lav', '-e', 'ssh',
$stagingUser . '@' . $stagingServer . ':' . $ftpNightlyDir,
'./stage-unsigned'],
logFile => catfile($logDir, 'download_stage.log'),
dir => $batch1Dir
);
my $prestageTrimmedDir = catfile($batch1Dir, 'prestage-trimmed');
my $prestageTrimmedDir = catfile($batch1Dir, 'stage-unsigned');
# Remove unknown/unrecognized directories from the -candidates dir; after
# this, the only directories that should be in the prestage-trimmed
# this, the only directories that should be in the stage-unsigned
# directory are directories that we expliciately handle below, to prep
# for groom-files.
$this->{'scrubTrimmedDirDeleteList'} = [];
@ -341,14 +330,34 @@ sub Execute {
# some point (says the hacker who wrote most of that ickyness ;-)
find(sub { return $this->TrimCallback(); }, $prestageTrimmedDir);
# Create a stage-unsigned directory to run groom-files in.
# Process the update mars; we copy everything from the trimmed directory
# that we created above; this will have only the locales/deliverables
# we actually ship; then, remove everything but the mars, including
# the [empty] directories; then, run groom-files in the directory that
# has only updates now.
$this->Shell(
cmd => 'rsync',
cmdArgs => ['-av', 'prestage-trimmed/', 'stage-unsigned/'],
logFile => catfile($logDir, 'stage_collect_stage.log'),
cmdArgs => ['-av', 'stage-unsigned/', 'mar/'],
logFile => catfile($logDir, 'stage_trimmed_to_mars.log'),
dir => $batch1Dir
);
$this->{'leaveOnlyMarsDirDeleteList'} = [];
find(sub { return $this->LeaveOnlyUpdateMarsCallback(); },
catfile($stageDir, 'batch1', 'mar'));
foreach my $delDir (@{$this->{'leaveOnlyMarsDirDeleteList'}}) {
if (-e $delDir && -d $delDir) {
$this->Log(msg => "rmtree() ing $delDir");
if (rmtree($delDir, 1, 1) <= 0) {
die("ASSERT: rmtree() called on $delDir, but nothing deleted.");
}
}
}
$this->GroomFiles(catfile($batch1Dir, 'mar'));
# Remove MAR files from stage-unsigned now that they have been processed.
find(sub { return $this->RemoveMarsCallback(); },
catfile($batch1Dir, 'stage-unsigned'));
@ -392,37 +401,8 @@ sub Execute {
dir => $batch1Dir
);
# Process the update mars; we copy everything from the trimmed directory
# that we created above; this will have only the locales/deliverables
# we actually ship; then, remove everything but the mars, including
# the [empty] directories; then, run groom-files in the directory that
# has only updates now.
$this->Shell(
cmd => 'rsync',
cmdArgs => ['-av', 'prestage-trimmed/', 'mar/'],
logFile => catfile($logDir, 'stage_trimmed_to_mars.log'),
dir => $batch1Dir
);
$this->{'leaveOnlyMarsDirDeleteList'} = [];
find(sub { return $this->LeaveOnlyUpdateMarsCallback(); },
catfile($stageDir, 'batch1', 'mar'));
foreach my $delDir (@{$this->{'leaveOnlyMarsDirDeleteList'}}) {
if (-e $delDir && -d $delDir) {
$this->Log(msg => "rmtree() ing $delDir");
if (rmtree($delDir, 1, 1) <= 0) {
die("ASSERT: rmtree() called on $delDir, but nothing deleted.");
}
}
}
$this->GroomFiles(catfile($batch1Dir, 'mar'));
SyncToStaging();
}
sub Verify {
my $this = shift;
@ -436,9 +416,6 @@ sub Verify {
my $mozillaCvsroot = $config->Get(var => 'mozillaCvsroot');
my $linuxExtension = $config->GetLinuxExtension();
## Prepare the staging directory for the release.
# Create the staging directory.
my $stageDir = $this->GetStageDir();
# check out locales manifest (shipped-locales)
@ -474,6 +451,27 @@ sub Verify {
);
}
sub Push {
my $this = shift;
my $config = new Bootstrap::Config();
my $logDir = $config->Get(sysvar => 'logDir');
my $stageHome = $config->Get(var => 'stageHome');
my $stagingUser = $config->Get(var => 'stagingUser');
my $stagingServer = $config->Get(var => 'stagingServer');
# upload public and private staging areas
my $stageDir = $this->GetStageDir();
$this->Shell(
cmd => 'rsync',
cmdArgs => ['-av', '-e', 'ssh', $stageDir,
$stagingUser . '@' . $stagingServer . ':' .
$stageDir],
logFile => catfile($logDir, 'upload_stage_private.log'),
);
}
sub LeaveOnlyUpdateMarsCallback {
my $this = shift;
my $dirent = $File::Find::name;
@ -532,9 +530,9 @@ sub ScrubTrimmedDirCallback {
my $dirent = $File::Find::name;
my $trimmedDir = catfile($this->GetStageDir(), 'batch1',
'prestage-trimmed');
'stage-unsigned');
# if $dirent is a directory and is a direct child of the prestage-trimmed
# if $dirent is a directory and is a direct child of the stage-unsigned
# directory (a hacky attempt at the equivalent of find's maxdepth 1 option);
if (-d $dirent && dirname($dirent) eq $trimmedDir) {
foreach my $allowedDir (@ALLOWED_DELIVERABLE_DIRECTORIES) {
@ -566,7 +564,7 @@ sub TrimCallback {
# source tarballs don't have a locale, so don't check them for one;
# all other deliverables need to be checked to make sure they should
# be in prestage-trimmed, i.e. if their locale shipped.
# be in stage-unsigned, i.e. if their locale shipped.
if ($dirent !~ /\-source\.tar\.bz2$/) {
my $validDeliverable = 0;

Просмотреть файл

@ -6,7 +6,7 @@ package Bootstrap::Step::Updates;
use Bootstrap::Step;
use Bootstrap::Config;
use Bootstrap::Util qw(CvsCatfile SyncToStaging GetLocaleManifest);
use Bootstrap::Util qw(CvsCatfile GetLocaleManifest);
use File::Find qw(find);
use POSIX qw(strftime);
@ -233,7 +233,7 @@ sub BumpVerifyConfig {
my $mozillaCvsroot = $config->Get(var => 'mozillaCvsroot');
my $verifyDir = $config->Get(var => 'verifyDir');
my $ausServerUrl = $config->Get(var => 'ausServerUrl');
my $externalStagingServer = $config->Get(var => 'externalStagingServer');
my $stagingServer = $config->Get(var => 'stagingServer');
my $verifyConfig = $config->Get(sysvar => 'verifyConfig');
my $logDir = $config->Get(sysvar => 'logDir');
my $linuxExtension = $config->GetLinuxExtension();
@ -320,7 +320,7 @@ sub BumpVerifyConfig {
$channel . '" from="/' . $product . '/releases/' .
$oldVersion . '/' . $ftpOsname . '/%locale%/' . $releaseFile .
'" aus_server="' . $ausServerUrl . '" ftp_server="' .
$externalStagingServer . '/pub/mozilla.org" to="/' .
$stagingServer . '/pub/mozilla.org" to="/' .
$product . '/nightly/' . $version . '-candidates/rc' .
$rc . '/' . $nightlyFile . '"' . "\n");
@ -406,8 +406,6 @@ sub Push {
);
}
SyncToStaging();
# Backup test channels
$this->Shell(
cmd => 'ssh',

Просмотреть файл

@ -14,8 +14,7 @@ our @EXPORT_OK = qw(CvsCatfile CvsTag
GetFtpNightlyDir
GetLocaleManifest
GetBouncerPlatforms GetPatcherPlatforms
GetBouncerToPatcherPlatformMap
SyncToStaging);
GetBouncerToPatcherPlatformMap);
our($DEFAULT_SHELL_TIMEOUT);
@ -293,59 +292,4 @@ sub GetDiffFileList {
return \@differentFiles;
}
sub SyncToStaging {
my $config = new Bootstrap::Config();
my $version = $config->GetVersion(longName => 0);
my $product = $config->Get(var => 'product');
my $productTag = $config->Get(var => 'productTag');
my $rc = $config->Get(var => 'rc');
my $logDir = $config->Get(sysvar => 'logDir');
my $stageHome = $config->Get(var => 'stageHome');
my $stagingUser = $config->Get(var => 'stagingUser');
my $stagingServer = $config->Get(var => 'stagingServer');
my $externalStagingUser = $config->Get(var => 'externalStagingUser');
my $externalStagingServer = $config->Get(var => 'externalStagingServer');
my $rcTag = $productTag . '_RC' . $rc;
my $pushLog = catfile($logDir, 'build_' . $rcTag . '-push.log');
my $dirName = $config->GetFtpNightlyDir();
my $command = 'ssh';
my @cmdArgs = ($stagingUser . '@' . $stagingServer,
'rsync', '-av', $dirName,
$externalStagingUser.'@'.$externalStagingServer.':'.
$dirName);
print 'Bootstrap::Util::SyncToStaging() Running shell command: '.$command.' '.join(' ', @cmdArgs)."\n";
my $rv = RunShellCommand(command => $command,
args => \@cmdArgs,
redirectStderr => 1,
logfile => $pushLog);
print 'Bootstrap::Util::SyncToStaging() Output: ' .
$rv->{'output'} . "\n";
if ($rv->{'exitValue'} != 0) {
die "ASSERT: SyncToStaging(): rsync failed\n";
}
$dirName = CvsCatfile($stageHome, $product.'-'.$version.'/');
@cmdArgs = ($stagingUser . '@' . $stagingServer,
'rsync', '-av', $dirName,
$externalStagingUser.'@'.$externalStagingServer.':'.
$dirName);
print 'Bootstrap::Util::SyncToStaging() Running shell command: '.$command.' '.join(' ', @cmdArgs)."\n";
$rv = RunShellCommand(command => $command,
args => \@cmdArgs,
redirectStderr => 1,
logfile => $pushLog);
print 'Bootstrap::Util::SyncToStaging() Output: ' .
$rv->{'output'} . "\n";
if ($rv->{'exitValue'} != 0) {
die "ASSERT: SyncToStaging(): rsync failed\n";
}
}
1;