Add a "Make an HTTP Request" build step

Summary:
Ref T1049. This is very minimal, but does what it says.

I merged the variable replacement code so Remote + HTTP can share more stuff.

Test Plan:
Ran "HTTP" and "Remote" build plans.

{F79886}

{F79887}

Reviewers: hach-que, btrahan

Reviewed By: hach-que

CC: zeeg, aran

Maniphest Tasks: T1049

Differential Revision: https://secure.phabricator.com/D7541
This commit is contained in:
epriestley 2013-11-09 07:16:12 -08:00
parent 43847d6bd7
commit 56c65e33ef
7 changed files with 114 additions and 29 deletions

View file

@ -682,6 +682,7 @@ phutil_register_library_map(array(
'HarbormasterCapabilityManagePlans' => 'applications/harbormaster/capability/HarbormasterCapabilityManagePlans.php', 'HarbormasterCapabilityManagePlans' => 'applications/harbormaster/capability/HarbormasterCapabilityManagePlans.php',
'HarbormasterController' => 'applications/harbormaster/controller/HarbormasterController.php', 'HarbormasterController' => 'applications/harbormaster/controller/HarbormasterController.php',
'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php', 'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php',
'HarbormasterHTTPRequestBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php',
'HarbormasterObject' => 'applications/harbormaster/storage/HarbormasterObject.php', 'HarbormasterObject' => 'applications/harbormaster/storage/HarbormasterObject.php',
'HarbormasterPHIDTypeBuild' => 'applications/harbormaster/phid/HarbormasterPHIDTypeBuild.php', 'HarbormasterPHIDTypeBuild' => 'applications/harbormaster/phid/HarbormasterPHIDTypeBuild.php',
'HarbormasterPHIDTypeBuildItem' => 'applications/harbormaster/phid/HarbormasterPHIDTypeBuildItem.php', 'HarbormasterPHIDTypeBuildItem' => 'applications/harbormaster/phid/HarbormasterPHIDTypeBuildItem.php',
@ -2979,6 +2980,7 @@ phutil_register_library_map(array(
'HarbormasterCapabilityManagePlans' => 'PhabricatorPolicyCapability', 'HarbormasterCapabilityManagePlans' => 'PhabricatorPolicyCapability',
'HarbormasterController' => 'PhabricatorController', 'HarbormasterController' => 'PhabricatorController',
'HarbormasterDAO' => 'PhabricatorLiskDAO', 'HarbormasterDAO' => 'PhabricatorLiskDAO',
'HarbormasterHTTPRequestBuildStepImplementation' => 'VariableBuildStepImplementation',
'HarbormasterObject' => 'HarbormasterDAO', 'HarbormasterObject' => 'HarbormasterDAO',
'HarbormasterPHIDTypeBuild' => 'PhabricatorPHIDType', 'HarbormasterPHIDTypeBuild' => 'PhabricatorPHIDType',
'HarbormasterPHIDTypeBuildItem' => 'PhabricatorPHIDType', 'HarbormasterPHIDTypeBuildItem' => 'PhabricatorPHIDType',

View file

@ -26,7 +26,7 @@ final class HarbormasterBuildCancelController
$build_uri = $this->getApplicationURI('/build/'.$build->getID()); $build_uri = $this->getApplicationURI('/build/'.$build->getID());
if ($request->isDialogFormPost()) { if ($request->isDialogFormPost()) {
$build->setCancelRequested(true); $build->setCancelRequested(1);
$build->save(); $build->save();
return id(new AphrontRedirectResponse())->setURI($build_uri); return id(new AphrontRedirectResponse())->setURI($build_uri);

View file

@ -0,0 +1,70 @@
<?php
final class HarbormasterHTTPRequestBuildStepImplementation
extends VariableBuildStepImplementation {
public function getName() {
return pht('Make HTTP Request');
}
public function getGenericDescription() {
return pht('Make an HTTP request.');
}
public function getDescription() {
$settings = $this->getSettings();
$uri = new PhutilURI($settings['uri']);
$domain = $uri->getDomain();
return pht('Make an HTTP request to %s', $domain);
}
public function execute(
HarbormasterBuild $build,
HarbormasterBuildStep $build_step) {
$settings = $this->getSettings();
$variables = $this->retrieveVariablesFromBuild($build);
$uri = $this->mergeVariables(
'vurisprintf',
$settings['uri'],
$variables);
$log_body = $build->createLog($build_step, $uri, 'http-body');
$start = $log_body->start();
list($status, $body, $headers) = id(new HTTPSFuture($uri))
->setMethod('POST')
->setTimeout(60)
->resolve();
$log_body->append($body);
$log_body->finalize($start);
if ($status->getStatusCode() != 200) {
$build->setBuildStatus(HarbormasterBuild::STATUS_FAILED);
}
}
public function validateSettings() {
$settings = $this->getSettings();
if ($settings['uri'] === null || !is_string($settings['uri'])) {
return false;
}
return true;
}
public function getSettingDefinitions() {
return array(
'uri' => array(
'name' => 'URI',
'description' => pht('The URI to request.'),
'type' => BuildStepImplementation::SETTING_TYPE_STRING,
),
);
}
}

View file

@ -25,24 +25,12 @@ final class RemoteCommandBuildStepImplementation
HarbormasterBuildStep $build_step) { HarbormasterBuildStep $build_step) {
$settings = $this->getSettings(); $settings = $this->getSettings();
$parameters = array();
$matches = array();
$variables = $this->retrieveVariablesFromBuild($build); $variables = $this->retrieveVariablesFromBuild($build);
$command = $settings['command'];
preg_match_all(
"/\\\$\\{(?P<name>[a-z\.]+)\\}/",
$command,
$matches);
foreach ($matches["name"] as $match) {
$parameters[] = idx($variables, $match, "");
}
$command = str_replace("%", "%%", $command);
$command = preg_replace("/\\\$\\{(?P<name>[a-z\.]+)\\}/", "%s", $command);
$command = vcsprintf( $command = $this->mergeVariables(
$command, 'vcsprintf',
$parameters); $settings['command'],
$variables);
$future = null; $future = null;
if (empty($settings['sshkey'])) { if (empty($settings['sshkey'])) {

View file

@ -33,17 +33,42 @@ abstract class VariableBuildStepImplementation extends BuildStepImplementation {
return $results; return $results;
} }
public function mergeVariables(HarbormasterBuild $build, $string) {
$variables = $this->retrieveVariablesFromBuild($build); /**
foreach ($variables as $name => $value) { * Convert a user-provided string with variables in it, like:
if ($value === null) { *
$value = ''; * ls ${dirname}
*
* ...into a string with variables merged into it safely:
*
* ls 'dir with spaces'
*
* @param string Name of a `vxsprintf` function, like @{function:vcsprintf}.
* @param string User-provided pattern string containing `${variables}`.
* @param dict List of available replacement variables.
* @return string String with variables replaced safely into it.
*/
protected function mergeVariables($function, $pattern, array $variables) {
$regexp = '/\\$\\{(?P<name>[a-z\\.]+)\\}/';
$matches = null;
preg_match_all($regexp, $pattern, $matches);
$argv = array();
foreach ($matches['name'] as $name) {
if (!array_key_exists($name, $variables)) {
throw new Exception(pht("No such variable '%s'!", $name));
} }
$string = str_replace('${'.$name.'}', $value, $string); $argv[] = $variables[$name];
} }
return $string;
$pattern = str_replace('%', '%%', $pattern);
$pattern = preg_replace($regexp, '%s', $pattern);
return call_user_func($function, $pattern, $argv);
} }
public function getAvailableVariables() { public function getAvailableVariables() {
return array( return array(
'buildable.revision' => 'buildable.revision' =>

View file

@ -54,7 +54,7 @@ final class HarbormasterBuild extends HarbormasterDAO
public static function initializeNewBuild(PhabricatorUser $actor) { public static function initializeNewBuild(PhabricatorUser $actor) {
return id(new HarbormasterBuild()) return id(new HarbormasterBuild())
->setBuildStatus(self::STATUS_INACTIVE) ->setBuildStatus(self::STATUS_INACTIVE)
->setCancelRequested(false); ->setCancelRequested(0);
} }
public function getConfiguration() { public function getConfiguration() {
@ -118,7 +118,7 @@ final class HarbormasterBuild extends HarbormasterDAO
$copy = id(new HarbormasterBuild())->load($this->getID()); $copy = id(new HarbormasterBuild())->load($this->getID());
if ($copy->getCancelRequested()) { if ($copy->getCancelRequested()) {
$this->setBuildStatus(HarbormasterBuild::STATUS_CANCELLED); $this->setBuildStatus(HarbormasterBuild::STATUS_CANCELLED);
$this->setCancelRequested(false); $this->setCancelRequested(0);
$this->save(); $this->save();
return true; return true;
} }

View file

@ -28,7 +28,7 @@ final class HarbormasterBuildLog extends HarbormasterDAO
->setBuildPHID($build->getPHID()) ->setBuildPHID($build->getPHID())
->setBuildStepPHID($build_step->getPHID()) ->setBuildStepPHID($build_step->getPHID())
->setDuration(null) ->setDuration(null)
->setLive(false); ->setLive(0);
} }
public function getConfiguration() { public function getConfiguration() {
@ -70,7 +70,7 @@ final class HarbormasterBuildLog extends HarbormasterDAO
throw new Exception("Live logging has already started for this log."); throw new Exception("Live logging has already started for this log.");
} }
$this->setLive(true); $this->setLive(1);
$this->save(); $this->save();
return time(); return time();
@ -150,7 +150,7 @@ final class HarbormasterBuildLog extends HarbormasterDAO
if ($start > 0) { if ($start > 0) {
$this->setDuration(time() - $start); $this->setDuration(time() - $start);
} }
$this->setLive(false); $this->setLive(0);
$this->save(); $this->save();
} }