Index: src/__phutil_library_map__.php =================================================================== --- src/__phutil_library_map__.php +++ src/__phutil_library_map__.php @@ -682,6 +682,7 @@ 'HarbormasterCapabilityManagePlans' => 'applications/harbormaster/capability/HarbormasterCapabilityManagePlans.php', 'HarbormasterController' => 'applications/harbormaster/controller/HarbormasterController.php', 'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php', + 'HarbormasterHTTPRequestBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php', 'HarbormasterObject' => 'applications/harbormaster/storage/HarbormasterObject.php', 'HarbormasterPHIDTypeBuild' => 'applications/harbormaster/phid/HarbormasterPHIDTypeBuild.php', 'HarbormasterPHIDTypeBuildItem' => 'applications/harbormaster/phid/HarbormasterPHIDTypeBuildItem.php', @@ -2979,6 +2980,7 @@ 'HarbormasterCapabilityManagePlans' => 'PhabricatorPolicyCapability', 'HarbormasterController' => 'PhabricatorController', 'HarbormasterDAO' => 'PhabricatorLiskDAO', + 'HarbormasterHTTPRequestBuildStepImplementation' => 'VariableBuildStepImplementation', 'HarbormasterObject' => 'HarbormasterDAO', 'HarbormasterPHIDTypeBuild' => 'PhabricatorPHIDType', 'HarbormasterPHIDTypeBuildItem' => 'PhabricatorPHIDType', Index: src/applications/harbormaster/controller/HarbormasterBuildCancelController.php =================================================================== --- src/applications/harbormaster/controller/HarbormasterBuildCancelController.php +++ src/applications/harbormaster/controller/HarbormasterBuildCancelController.php @@ -26,7 +26,7 @@ $build_uri = $this->getApplicationURI('/build/'.$build->getID()); if ($request->isDialogFormPost()) { - $build->setCancelRequested(true); + $build->setCancelRequested(1); $build->save(); return id(new AphrontRedirectResponse())->setURI($build_uri); Index: src/applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php =================================================================== --- /dev/null +++ src/applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php @@ -0,0 +1,70 @@ +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, + ), + ); + } + +} Index: src/applications/harbormaster/step/RemoteCommandBuildStepImplementation.php =================================================================== --- src/applications/harbormaster/step/RemoteCommandBuildStepImplementation.php +++ src/applications/harbormaster/step/RemoteCommandBuildStepImplementation.php @@ -25,24 +25,12 @@ HarbormasterBuildStep $build_step) { $settings = $this->getSettings(); - - $parameters = array(); - $matches = array(); $variables = $this->retrieveVariablesFromBuild($build); - $command = $settings['command']; - preg_match_all( - "/\\\$\\{(?P[a-z\.]+)\\}/", - $command, - $matches); - foreach ($matches["name"] as $match) { - $parameters[] = idx($variables, $match, ""); - } - $command = str_replace("%", "%%", $command); - $command = preg_replace("/\\\$\\{(?P[a-z\.]+)\\}/", "%s", $command); - $command = vcsprintf( - $command, - $parameters); + $command = $this->mergeVariables( + 'vcsprintf', + $settings['command'], + $variables); $future = null; if (empty($settings['sshkey'])) { Index: src/applications/harbormaster/step/VariableBuildStepImplementation.php =================================================================== --- src/applications/harbormaster/step/VariableBuildStepImplementation.php +++ src/applications/harbormaster/step/VariableBuildStepImplementation.php @@ -33,17 +33,42 @@ return $results; } - public function mergeVariables(HarbormasterBuild $build, $string) { - $variables = $this->retrieveVariablesFromBuild($build); - foreach ($variables as $name => $value) { - if ($value === null) { - $value = ''; + + /** + * Convert a user-provided string with variables in it, like: + * + * 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[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() { return array( 'buildable.revision' => Index: src/applications/harbormaster/storage/build/HarbormasterBuild.php =================================================================== --- src/applications/harbormaster/storage/build/HarbormasterBuild.php +++ src/applications/harbormaster/storage/build/HarbormasterBuild.php @@ -54,7 +54,7 @@ public static function initializeNewBuild(PhabricatorUser $actor) { return id(new HarbormasterBuild()) ->setBuildStatus(self::STATUS_INACTIVE) - ->setCancelRequested(false); + ->setCancelRequested(0); } public function getConfiguration() { @@ -118,7 +118,7 @@ $copy = id(new HarbormasterBuild())->load($this->getID()); if ($copy->getCancelRequested()) { $this->setBuildStatus(HarbormasterBuild::STATUS_CANCELLED); - $this->setCancelRequested(false); + $this->setCancelRequested(0); $this->save(); return true; } Index: src/applications/harbormaster/storage/build/HarbormasterBuildLog.php =================================================================== --- src/applications/harbormaster/storage/build/HarbormasterBuildLog.php +++ src/applications/harbormaster/storage/build/HarbormasterBuildLog.php @@ -28,7 +28,7 @@ ->setBuildPHID($build->getPHID()) ->setBuildStepPHID($build_step->getPHID()) ->setDuration(null) - ->setLive(false); + ->setLive(0); } public function getConfiguration() { @@ -70,7 +70,7 @@ throw new Exception("Live logging has already started for this log."); } - $this->setLive(true); + $this->setLive(1); $this->save(); return time(); @@ -150,7 +150,7 @@ if ($start > 0) { $this->setDuration(time() - $start); } - $this->setLive(false); + $this->setLive(0); $this->save(); }