@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add an "Restartable: If Failed" behavior to Harbormaster build plans

Summary:
Ref T13249. Ref T13258. In some cases, builds are not idempotent and should not be restarted casually.

If the scary part is at the very end (deploy / provision / whatever), it could be okay to restart them if they previously failed.

Also, make the "reasons why you can't restart" and "explanations of why you can't restart" logic a little more cohesive.

Test Plan:
- Tried to restart builds in various states (failed/not failed, restartable always/if failed/never, already restarted), got appropriate errors or restarts.
- (I'm not sure the "Autoplan" error is normally reachable, since you can't edit autoplans to configure things to let you try to restart them.)

Reviewers: amckinley

Reviewed By: amckinley

Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam

Maniphest Tasks: T13258, T13249

Differential Revision: https://secure.phabricator.com/D20252

+104 -26
+2
src/__phutil_library_map__.php
··· 1443 1443 'HarbormasterQueryBuildsConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php', 1444 1444 'HarbormasterQueryBuildsSearchEngineAttachment' => 'applications/harbormaster/engineextension/HarbormasterQueryBuildsSearchEngineAttachment.php', 1445 1445 'HarbormasterRemarkupRule' => 'applications/harbormaster/remarkup/HarbormasterRemarkupRule.php', 1446 + 'HarbormasterRestartException' => 'applications/harbormaster/exception/HarbormasterRestartException.php', 1446 1447 'HarbormasterRunBuildPlansHeraldAction' => 'applications/harbormaster/herald/HarbormasterRunBuildPlansHeraldAction.php', 1447 1448 'HarbormasterSchemaSpec' => 'applications/harbormaster/storage/HarbormasterSchemaSpec.php', 1448 1449 'HarbormasterScratchTable' => 'applications/harbormaster/storage/HarbormasterScratchTable.php', ··· 7093 7094 'HarbormasterQueryBuildsConduitAPIMethod' => 'HarbormasterConduitAPIMethod', 7094 7095 'HarbormasterQueryBuildsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 7095 7096 'HarbormasterRemarkupRule' => 'PhabricatorObjectRemarkupRule', 7097 + 'HarbormasterRestartException' => 'Exception', 7096 7098 'HarbormasterRunBuildPlansHeraldAction' => 'HeraldAction', 7097 7099 'HarbormasterSchemaSpec' => 'PhabricatorConfigSchemaSpec', 7098 7100 'HarbormasterScratchTable' => 'HarbormasterDAO',
+4
src/applications/harbormaster/constants/HarbormasterBuildStatus.php
··· 52 52 return ($this->key === self::STATUS_PASSED); 53 53 } 54 54 55 + public function isFailed() { 56 + return ($this->key === self::STATUS_FAILED); 57 + } 58 + 55 59 56 60 /** 57 61 * Get a human readable name for a build status constant.
+6 -12
src/applications/harbormaster/controller/HarbormasterBuildActionController.php
··· 64 64 'restart. Side effects of the build will occur again. Really '. 65 65 'restart build?'); 66 66 $submit = pht('Restart Build'); 67 - } else if (!$build->getBuildPlan()->canRestartBuildPlan()) { 68 - $title = pht('Not Restartable'); 69 - $body = pht( 70 - 'The build plan for this build is not restartable, so you '. 71 - 'can not restart the build.'); 72 67 } else { 73 - $title = pht('Unable to Restart Build'); 74 - if ($build->isRestarting()) { 75 - $body = pht( 76 - 'This build is already restarting. You can not reissue a '. 77 - 'restart command to a restarting build.'); 78 - } else { 79 - $body = pht('You can not restart this build.'); 68 + try { 69 + $build->assertCanRestartBuild(); 70 + throw new Exception(pht('Expected to be unable to restart build.')); 71 + } catch (HarbormasterRestartException $ex) { 72 + $title = $ex->getTitle(); 73 + $body = $ex->getBody(); 80 74 } 81 75 } 82 76 break;
+33
src/applications/harbormaster/exception/HarbormasterRestartException.php
··· 1 + <?php 2 + 3 + final class HarbormasterRestartException extends Exception { 4 + 5 + private $title; 6 + private $body = array(); 7 + 8 + public function __construct($title, $body = null) { 9 + $this->setTitle($title); 10 + $this->appendParagraph($body); 11 + 12 + parent::__construct($title); 13 + } 14 + 15 + public function setTitle($title) { 16 + $this->title = $title; 17 + return $this; 18 + } 19 + 20 + public function getTitle() { 21 + return $this->title; 22 + } 23 + 24 + public function appendParagraph($description) { 25 + $this->body[] = $description; 26 + return $this; 27 + } 28 + 29 + public function getBody() { 30 + return $this->body; 31 + } 32 + 33 + }
+7
src/applications/harbormaster/plan/HarbormasterBuildPlanBehavior.php
··· 15 15 16 16 const BEHAVIOR_RESTARTABLE = 'restartable'; 17 17 const RESTARTABLE_ALWAYS = 'always'; 18 + const RESTARTABLE_IF_FAILED = 'failed'; 18 19 const RESTARTABLE_NEVER = 'never'; 19 20 20 21 const BEHAVIOR_DRAFTS = 'hold-drafts'; ··· 251 252 ->setIsDefault(true) 252 253 ->setDescription( 253 254 pht('The build may be restarted.')), 255 + id(new HarbormasterBuildPlanBehaviorOption()) 256 + ->setKey(self::RESTARTABLE_IF_FAILED) 257 + ->setIcon('fa-times-circle-o yellow') 258 + ->setName(pht('If Failed')) 259 + ->setDescription( 260 + pht('The build may be restarted if it has failed.')), 254 261 id(new HarbormasterBuildPlanBehaviorOption()) 255 262 ->setKey(self::RESTARTABLE_NEVER) 256 263 ->setIcon('fa-times red')
+52 -4
src/applications/harbormaster/storage/build/HarbormasterBuild.php
··· 183 183 return $this->getBuildStatusObject()->isPassed(); 184 184 } 185 185 186 + public function isFailed() { 187 + return $this->getBuildStatusObject()->isFailed(); 188 + } 189 + 186 190 public function getURI() { 187 191 $id = $this->getID(); 188 192 return "/harbormaster/build/{$id}/"; ··· 211 215 } 212 216 213 217 public function canRestartBuild() { 214 - if ($this->isAutobuild()) { 218 + try { 219 + $this->assertCanRestartBuild(); 220 + return true; 221 + } catch (HarbormasterRestartException $ex) { 215 222 return false; 216 223 } 224 + } 217 225 226 + public function assertCanRestartBuild() { 227 + if ($this->isAutobuild()) { 228 + throw new HarbormasterRestartException( 229 + pht('Can Not Restart Autobuild'), 230 + pht( 231 + 'This build can not be restarted because it is an automatic '. 232 + 'build.')); 233 + } 234 + 235 + $restartable = HarbormasterBuildPlanBehavior::BEHAVIOR_RESTARTABLE; 218 236 $plan = $this->getBuildPlan(); 219 - if (!$plan->canRestartBuildPlan()) { 220 - return false; 237 + 238 + $option = HarbormasterBuildPlanBehavior::getBehavior($restartable) 239 + ->getPlanOption($plan); 240 + $option_key = $option->getKey(); 241 + 242 + $never_restartable = HarbormasterBuildPlanBehavior::RESTARTABLE_NEVER; 243 + $is_never = ($option_key === $never_restartable); 244 + if ($is_never) { 245 + throw new HarbormasterRestartException( 246 + pht('Build Plan Prevents Restart'), 247 + pht( 248 + 'This build can not be restarted because the build plan is '. 249 + 'configured to prevent the build from restarting.')); 221 250 } 222 251 223 - return !$this->isRestarting(); 252 + $failed_restartable = HarbormasterBuildPlanBehavior::RESTARTABLE_IF_FAILED; 253 + $is_failed = ($option_key === $failed_restartable); 254 + if ($is_failed) { 255 + if (!$this->isFailed()) { 256 + throw new HarbormasterRestartException( 257 + pht('Only Restartable if Failed'), 258 + pht( 259 + 'This build can not be restarted because the build plan is '. 260 + 'configured to prevent the build from restarting unless it '. 261 + 'has failed, and it has not failed.')); 262 + } 263 + } 264 + 265 + if ($this->isRestarting()) { 266 + throw new HarbormasterRestartException( 267 + pht('Already Restarting'), 268 + pht( 269 + 'This build is already restarting. You can not reissue a restart '. 270 + 'command to a restarting build.')); 271 + } 224 272 } 225 273 226 274 public function canPauseBuild() {
-10
src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php
··· 175 175 $capability); 176 176 } 177 177 178 - public function canRestartBuildPlan() { 179 - $restartable = HarbormasterBuildPlanBehavior::BEHAVIOR_RESTARTABLE; 180 - $is_restartable = HarbormasterBuildPlanBehavior::RESTARTABLE_ALWAYS; 181 - 182 - $option = HarbormasterBuildPlanBehavior::getBehavior($restartable) 183 - ->getPlanOption($this); 184 - 185 - return ($option->getKey() === $is_restartable); 186 - } 187 - 188 178 189 179 /* -( PhabricatorSubscribableInterface )----------------------------------- */ 190 180