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!"); }