function deploy($target)
{
if (empty($this->targets[$target])) {
$this->log->error("{$target} target does not exist");
}
$targ = $this->targets[$target];
$dir = $this->rev;
$pwd = NULL;
if ($this->prompt) {
echo "Key Password: "******"\n");
shell_exec('stty ' . $savetty);
echo "\n";
}
$this->log->progress = 5;
$this->log->verbose("Getting {$targ['scm']} info for {$targ['repository']}");
$this->log->output("Deploying the following to {$target}");
$cwd = getcwd();
chdir($this->tmpdir);
`{$targ['scm']} info --username {$targ['scm.user']} --password {$targ['scm.passwd']} --no-auth-cache --non-interactive --trust-server-cert {$targ['repository']} > {$dir}.info`;
$this->log->progress = 10;
if (php_sapi_name() == 'cli') {
$this->log->output(trim(file_get_contents("{$dir}.info")));
} else {
$this->log->output(nl2br(trim(file_get_contents("{$dir}.info"))));
}
if (!is_dir($this->rev)) {
$this->log->output("Exporting {$targ['repository']} to {$dir}");
`{$targ['scm']} export -q --username {$targ['scm.user']} --password {$targ['scm.passwd']} --no-auth-cache --non-interactive --trust-server-cert {$targ['repository']} {$dir}`;
$this->log->rollback_set("rm -rf {$dir}.info {$dir}");
}
$this->log->progress += (int) (100 / (count($targ['hosts']) + 1));
// Clean up targets we don't need before pushing the code
if (is_dir("{$dir}/deploy/targets")) {
foreach (glob("{$dir}/deploy/targets/*") as $t) {
if (basename($t) != $target) {
`rm -rf {$t}`;
}
}
}
$this->log->progress += 10;
`tar czf {$dir}.tar.gz {$dir}`;
$this->log->verbose("tar file {$dir}.tar.gz created");
$this->log->rollback_add("rm {$dir}.tar.gz");
$md5 = md5_file("{$dir}.tar.gz");
$this->log->progress += 5;
$this->log->verbose("md5 checksum is {$md5}");
// Now push the tarball to each host
$each_progress = (int) ((100 - $this->log->progress) / (count($targ['hosts']) + 1));
foreach ($targ['hosts'] as $ip) {
$host = new Host($ip, $targ, $this->log, $pwd);
$targ['ssh'][$ip] = $host;
$host->exec("mkdir -p {$targ['deploy_to']}/releases");
// Alternatively you can use $host->sftp() here
$host->scp("{$dir}.tar.gz", "{$targ['deploy_to']}/releases/{$dir}.tar.gz");
$this->log->progress += $each_progress;
$this->log->rollback_add("rm -f {$targ['deploy_to']}/releases/{$dir}.tar.gz", $ip);
// Make sure the file got there uncorrupted
$result = $host->exec("md5sum -b {$targ['deploy_to']}/releases/{$dir}.tar.gz");
list($remote_md5, $junk) = explode(' ', $result, 2);
if ($md5 != $remote_md5) {
$this->log->error("Local checksum {$md5} does not match remote checksum {$remote_md5}");
}
$this->log->verbose("File uploaded and checksum matched");
}
// Multiple loops to do these almost in parallel
$each_progress = (int) ((100 - $this->log->progress) / (count($targ['hosts']) + 1));
foreach ($targ['ssh'] as $ip => $host) {
$this->log->progress += $each_progress;
$host->exec("cd {$targ['deploy_to']}/releases && tar zxf {$dir}.tar.gz && rm {$dir}.tar.gz && cd {$dir} && REVISION={$dir} make {$target}");
if (strlen(trim($dir))) {
$this->log->rollback_add("rm -rf {$targ['deploy_to']}/releases/{$dir}", $ip);
}
}
// Sanity check
$each_progress = (int) ((100 - $this->log->progress) / (count($targ['hosts']) + 1));
foreach ($targ['ssh'] as $ip => $host) {
$this->log->progress += $each_progress;
$current_version[$ip] = $host->exec("curl -s -S -H 'Host: {$targ['application']}' 'localhost/setrev.php'");
if ($current_version[$ip]) {
$this->log->rollback_add("curl -s -S -H 'Host: {$targ['application']}' 'localhost/setrev.php?user={$this->user}&rel=rollback&rev={$current_version[$ip]}'", $ip);
}
}
// Move the symlink into place and hit the local setrev script
$each_progress = (int) ((100 - $this->log->progress) / (count($targ['hosts']) + 1));
foreach ($targ['ssh'] as $ip => $host) {
$this->log->progress += $each_progress;
$this->log->output("Moving symlink from {$current_version[$ip]} to {$dir} on {$host->name}");
$host->exec("ln -s {$targ['deploy_to']}/releases/{$dir} {$targ['deploy_to']}/new_current && mv -Tf {$targ['deploy_to']}/new_current {$targ['deploy_to']}/current");
if ($current_version[$ip]) {
$this->log->rollback_add("ln -s {$targ['deploy_to']}/releases/{$current_version[$ip]} {$targ['deploy_to']}/new_current && mv -Tf {$targ['deploy_to']}/new_current {$targ['deploy_to']}/current");
}
$this->log->output("Symlink moved, version {$dir} is now active");
$host->exec("curl -s -S -H 'Host: {$targ['application']}' 'localhost/setrev.php?user={$this->user}&rel={$targ['repository']}&rev={$dir}'");
$this->log->rollback_add("curl -s -S -H 'Host: {$targ['application']}' 'localhost/setrev.php?user={$this->user}&rel={$targ['repository']}&rev={$current_version[$ip]}'", $ip);
}
// Deploy was good - non-critical cleanup after this point
$this->log->rollback_set('');
// Delete previous targets, but keep $targ[keep] of them around
$each_progress = (int) ((100 - $this->log->progress) / count($targ['hosts']));
foreach ($targ['ssh'] as $ip => $host) {
$this->log->progress += $each_progress;
$keep = $targ['keep'] + 1;
// The number of old revisions to keep around on the server
$host->exec("cd {$targ['deploy_to']}/releases && j=0; for i in `ls -d1at ./20????????????`; do j=`expr \$j + 1`; if [ \"\$j\" -ge {$keep} ]; then rm -rf \$i; fi; done");
}
// And get rid of the local installation files
`rm -rf {$dir}.info {$dir}.tar.gz {$dir}`;
chdir($cwd);
$this->log->output("SUCCESS!");
}