diff --git a/arcanist/workflow/ArcanistLandBotWorkflow.php b/arcanist/workflow/ArcanistLandBotWorkflow.php index 548bbd3ff..e8703ba09 100644 --- a/arcanist/workflow/ArcanistLandBotWorkflow.php +++ b/arcanist/workflow/ArcanistLandBotWorkflow.php @@ -1,204 +1,218 @@ array( 'help' => pht( 'Prepare the change to be pushed to the land queue, but do not '. 'actually push it.'), ), 'preview' => array( 'help' => pht( 'Prints the commits that would be landed. Does not actually modify '. 'or land the commits.'), ), 'revision' => array( 'param' => 'id', 'help' => pht( 'Land a specific revision, rather than inferring the revision '. 'based on branch content.'), ), '*' => 'branch', ); } public function run() { // Make sure gpg is installed try { execx('command -v gpg'); } catch (Exception $e) { throw new ArcanistUsageException(pht( 'gpg not found. Try installing it first.')); } // Determine the branch you are currently on so we can return to it later $repositoryApi = $this->getRepositoryAPI(); $oldBranch = $repositoryApi->getBranchName(); if (!strlen($oldBranch)) { $oldBranch = $repositoryApi->getWorkingCopyRevision(); } // Run the default land workflow to take advantage of various sanity checks // and fetch the revision ID if it's not set via --revision. $landArgs = array(); if ($this->getArgument('preview')) { array_push($landArgs, '--preview'); } else { // Always hold the revision since we are not using 'arc land' to actually // land the change. array_push($landArgs, '--hold'); } // Checkout the specified branch/commit which will determine the revision // to land. Your old branch will be restored at the end. $branch = $this->getArgument('branch'); if (!empty($branch)) { - $repositoryApi->execxLocal('checkout %s', $branch[0]); + $branch = $branch[0]; + $repositoryApi->execxLocal('checkout %s', $branch); } $revision = $this->getArgument('revision'); if (empty($revision)) { // By default, queue the latest revision on the current branch $revisions = $repositoryApi->loadWorkingCopyDifferentialRevisions( $this->getConduit(), array()); + + if (empty($revisions)) { + $location = 'current branch'; + if (!empty($branch)) { + // Restore the branch you were on previously + $repositoryApi->execxLocal('checkout %s', $oldBranch); + + $location = "'$branch'"; + } + throw new ArcanistUsageException(pht( + "Error: Could not find a valid revision at %s", $location)); + } + $revision = 'D' . end($revisions)['id']; } array_push($landArgs, '--revision'); array_push($landArgs, $revision); if ($this->getArgument('preview')) { // Restore the branch you were on previously $repositoryApi->execxLocal('checkout %s', $oldBranch); echo phutil_console_format(pht( 'Found revision %s', $revision) . "\n"); return 0; } $landWorkflow = $this->buildChildWorkflow('land', $landArgs); $landWorkflow->run(); // Restore the branch you were on previously $repositoryApi->execxLocal('checkout %s', $oldBranch); if ($this->getArgument('hold')) { echo phutil_console_format(pht( 'Revision %s has not been queued with the land bot', $revision) . "\n"); return 0; } // Encrypt your Conduit token to securely pass it to the land bot $workingCopy = $repositoryApi->getWorkingCopyIdentity(); $pubkeyFile = $workingCopy->getProjectPath( './contrib/source-control-tools/land-patch.pub'); $token = $this->getConduit()->getConduitToken(); list($encryptedToken) = execx( 'echo %s | gpg --armor -o- --encrypt --recipient-file %s', $token, $pubkeyFile); // Clear and delete the unencrypted $token $tokenLen = strlen($token); for ($i = 0; $i < $tokenLen; $i++) { $token[$i] = "\0"; } unset($token); // Get the configured committer name and email similar to how // ArcanistGitAPI::getAuthor() does. Arcanist provides no API for this. list($gitIdent) = $repositoryApi->execxLocal('var GIT_COMMITTER_IDENT'); $committerName = preg_replace('/\s+<.*/', '', rtrim($gitIdent, "\n")); preg_match('/<(.*)>/', rtrim($gitIdent, "\n"), $gitAuthorEmailMatches); $committerEmail = $gitAuthorEmailMatches[1]; // Prepare cURL request to land bot $ch = curl_init('https://buildbot.bitcoinabc.org/land'); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type:application/json', 'Accept:application/json')); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(array( "committerName" => $committerName, "committerEmail" => $committerEmail, "revision" => $revision, "conduitToken" => $encryptedToken))); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); echo phutil_console_format(pht( 'Pushing revision %s to land bot queue...', $revision) . "\n\n"); $response = curl_exec($ch); if (!$response) { throw new ArcanistUsageException(pht( 'Error pushing to land bot queue: %s', curl_error($ch)) . "\n"); } $jsonResponse = json_decode($response); if (!$jsonResponse) { throw new ArcanistUsageException(pht( 'JSON decoding error: %d', json_last_error()) . "\n"); } echo phutil_console_format("** " . pht('Your revision is in the queue:') . " **\n\n"); echo phutil_console_format( pht('Revision:') . "\thttps://reviews.bitcoinabc.org/%s\n", $revision); echo phutil_console_format(pht('Build log:') . "\t%s\n", $jsonResponse->webUrl . '&tab=buildLog&guest=1'); curl_close($ch); return 0; } }