port = $config['p4']['port']; $p4->user = $config['p4']['user']; $p4->password = $config['p4']['password']; $p4->connect(); // // if this is a branch; figure out the head change that passed tests // // otherwise, it must be a review so we enforce more required fields // $isBranch = strlen($review) && isset($config['branch_labels'][$review]); // if ($isBranch) { // $change = $p4->run('label', '-o', $config['branch_labels'][$review]); // $change = trim($change[0]['Revision'], '@'); // $branch = $review; // } else { $isBranch = false; // lcheung: we are not using branch for workshop strlen($review) || die('Missing required param "review"'); ctype_digit($review) || die('Non-numeric "review" specified'); strlen($change) || die('Missing required param "change"'); ctype_digit($change) || die('Non-numeric "change" specified'); strlen($branch) || die('Missing required param "branch"'); isset($config['template_workspaces'][$branch]) || die('Unrecognized branch specified'); // } ignore_user_abort(true); // define the root folder for this deployment $root = $deployRoot . '/' . $review; @mkdir($root, 0777, true); $root = realpath($root); // someone else might be in the midst of deploying; lock to ensure attempts are serialized $handle = @fopen("$root/deployed", 'c'); $handle || die('Failed to open lock file under ' . $root); flock($handle, LOCK_EX); // if we're already up to date; just redirect start a worker and exit if (@file_get_contents("$root/deployed") == $change) { disconnectAndRedirect($review, $config); // start a worker on the deployment on our way out the door file_get_contents("http://$review.${config['hostname']}/queue/worker"); exit; } /** * looks like we were not already up to date; nuke existing deployment keeping the perforce data * * the anticipated structure is: * $deployRoot/review-id/ * deployed (just a text file containing the last deployed change #) * perforce (this instances perforce depot) * swarm (the full swarm instance as per normal with proposed work unshelved) * public (the web-root as per normal) * deploy (contains log of deployment, symlink to access logs, etc.) */ // create our temp client: // - ensure the client root exists // - create our client from the template to prime it // - set the client root to be the correct value // - lastly, tell our connection to use the new client $clientroot = "$root/workshop"; // FIXME - sync stuff to $clientroot and symlink swarm to $clientroot/swarm @mkdir("$clientroot", 0777, true); chdir("$clientroot"); $p4->run('client', '-t', $config['template_workspaces'][$branch], "workshop-deploy-$review"); $client = $p4->run('client', '-o', "workshop-deploy-$review"); $client = $client[0]; $client['Root'] = "$clientroot"; $client['Stream'] = '//workshop/swarm-deploy'; $p4->input = $client; $p4->run('client', '-i'); $p4->client = "workshop-deploy-$review"; // nuke the current swarm deployment if there is one and this isn't a branch sync !$isBranch && @`rm -rf $clientroot/*`; // - sync the workspace we setup // - attempt to unshelve the change // - resolve first merging then accepting shelved version for any straglers // - revert -k to clear up the have table try { $p4->run('sync', "//...@$change"); $p4->run('unshelve', '-s', $change); $p4->run('resolve', '-am'); $p4->run('resolve', '-ay'); } catch (\Exception $ignored) { // meh, was probably a commit not a shelved version echo "failure during sync/unshelve/resolve stage " . $ignored->getMessage() . "\n"; } try { // if this isn't a branch; try to revert the everything !$isBranch && $p4->run('revert', '-k', '//...'); } catch (\Exception $ignored) { // meh, was probably a commit not a shelved version // do this on its own to ensure it occurs though echo "revert error " . $ignored->getMessage() . "\n"; } // always delete our temp client; then rethrow if issues cropped up // note the branch uses a non-temp client so we leave it be !$isBranch && $p4->run('client', '-d', "workshop-deploy-$review"); if (isset($e)) { throw $e; } // symlink $root/swarm to $clientroot/swarm to match the existing // vhost configuration symlink("$clientroot/swarm", "$root/swarm"); // tweak the .htaccess file to support our name based vhost selection $mod = fileperms("$root/swarm/public/.htaccess"); chmod("$root/swarm/public/.htaccess", 0777); file_put_contents("$root/swarm/public/.htaccess", "\nRewriteBase /\n", FILE_APPEND); chmod("$root/swarm/public/.htaccess", $mod); // if the perforce server doesn't already exist; set one up if (!is_dir("$root/perforce")) { @mkdir("$root/perforce", 0777, true); // copy our 'template' server into the deployed instances home $depot = __DIR__ . '/depot.zip'; chdir("$root/perforce"); `unzip $depot`; // configure and install the swarm triggers `cp $root/swarm/p4-bin/scripts/swarm-trigger.sh $root/perforce/`; file_put_contents( "$root/perforce/swarm-trigger.conf", <<port = "rsh:$p4d -iqr \"$root/perforce\" -J off -vtrack=0 -vserver.locks.dir=disabled"; $rshP4->user = 'super'; $rshP4->connect(); $rshP4->input = "Triggers:\n" . `./swarm-trigger.sh -o`; $rshP4->run('triggers', '-i'); } // setup the trigger token @mkdir("$root/swarm/data/queue/tokens", 0777, true); touch("$root/swarm/data/queue/tokens/00000000-0000-0000-0000-000000000000"); // create a magic 'deploy' folder that: // - holds any emails that were sent by swarm // - allows listing contents via apache // - links to apache logs // - exposes swarm data folder (and thereby log) @mkdir("$root/swarm/public/deploy", 0777, true); @mkdir("$root/swarm/public/deploy/mail", 0777, true); file_put_contents("$root/swarm/public/deploy/.htaccess", "Options +Indexes\nRewriteRule ^.*$ - [NC,L]\n"); @symlink("/var/log/apache2", "$root/swarm/public/deploy/apache-logs"); @symlink("$root/swarm/data", "$root/swarm/public/deploy/swarm-data"); // write out the swarm config !file_exists("$root/swarm/data/config.php") && file_put_contents( "$root/swarm/data/config.php", " array( 'hostname' => "$review.${config['hostname']}", 'mode' => 'development' ), 'p4' => array( 'port' => "rsh:$p4d -iqr \"$root/perforce\" -J off -vtrack=0 -vserver.locks.dir=disabled", 'user' => 'swarm', 'password' => '' ), 'log' => array( 'priority' => 5 ), 'mail' => array( 'transport' => array( 'path' => "$root/deploy/mail", ), ), 'queue' => array( 'worker_lifetime' => 30*60 // 30 minutes ) ), true ) . ";" . "\n\n// last updated (fluxes to cause stale workers to shut-down): " . time() . "\n" ); // redirect the user and disconnect them but keep this script chugging $debug || disconnectAndRedirect($review, $config); // make a placeholder file to indicate docs are being built @mkdir("$root/swarm/public/docs", 0777, true); file_put_contents( "$root/swarm/public/docs/.htaccess", "DirectoryIndex index.html index.htm building.html\nRewriteEngine off\n" ); file_put_contents( "$root/swarm/public/docs/building.html", "Docs are being built; it will take a couple minutes.
" . "You can see the log of progress here." ); // start a worker on the new swarm instance file_get_contents("http://$review.${config['hostname']}/queue/worker"); // build the docs chdir("$root/swarm/"); `ant -Ddoc.bld.dir=collateral/manuals/_build/ doc 2>&1 > $root/swarm/public/deploy/doc-logs`; // also build the swagger documentation if we're new enough to have it if ($change >= 875216) { `ant doc-api 2>&1 > $root/swarm/public/deploy/swager-logs`; $source = escapeshellarg(realpath(__DIR__ . '/../swagger-ui/dist')); $dest = escapeshellarg(realpath("$root/swarm/public") . '/swagger'); `cp -R $source $dest`; } // lastly record the change id we just deployed to this review and unlock file_put_contents("$root/deployed", $change); fclose($handle); /** * Just a helper function to redirect to the deployed instance and disconnect * the user's browser so we can do any wrap-up background work. */ function disconnectAndRedirect($review, $config) { if (function_exists('apache_setenv')) { apache_setenv('no-gzip', 1); } header('Connection: close'); header('Content-length: 0'); header("Location: http://$review.${config['hostname']}", true, 307); session_write_close(); if (ob_get_level()) { ob_end_flush(); } flush(); }