Implement "Wait for Previous Builds" build step
Summary: This adds a build step which will block a build from continuing if there are previous builds of the build plan still running. Test Plan: Configured a build plan with a wait of 60 seconds and a "wait for previous builds", then started a build. While that was still building, reconfigured the plan to have a wait time of 3 seconds, started it, and saw it move into the "Waiting" status. When the 60 second build finished, both builds passed. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley CC: Korvin, epriestley, aran Maniphest Tasks: T1049 Differential Revision: https://secure.phabricator.com/D7745
This commit is contained in:
		| @@ -2366,6 +2366,7 @@ phutil_register_library_map(array( | ||||
|     'SlowvoteRemarkupRule' => 'applications/slowvote/remarkup/SlowvoteRemarkupRule.php', | ||||
|     'UploadArtifactBuildStepImplementation' => 'applications/harbormaster/step/UploadArtifactBuildStepImplementation.php', | ||||
|     'VariableBuildStepImplementation' => 'applications/harbormaster/step/VariableBuildStepImplementation.php', | ||||
|     'WaitForPreviousBuildStepImplementation' => 'applications/harbormaster/step/WaitForPreviousBuildStepImplementation.php', | ||||
|   ), | ||||
|   'function' => | ||||
|   array( | ||||
| @@ -5056,5 +5057,6 @@ phutil_register_library_map(array( | ||||
|     'SlowvoteRemarkupRule' => 'PhabricatorRemarkupRuleObject', | ||||
|     'UploadArtifactBuildStepImplementation' => 'VariableBuildStepImplementation', | ||||
|     'VariableBuildStepImplementation' => 'BuildStepImplementation', | ||||
|     'WaitForPreviousBuildStepImplementation' => 'BuildStepImplementation', | ||||
|   ), | ||||
| )); | ||||
|   | ||||
| @@ -49,7 +49,10 @@ final class HarbormasterBuildViewController | ||||
|     $targets = array(); | ||||
|     foreach ($build_targets as $build_target) { | ||||
|       $header = id(new PHUIHeaderView()) | ||||
|         ->setHeader(pht('Build Target %d', $build_target->getID())) | ||||
|         ->setHeader(pht( | ||||
|           'Build Target %d (%s)', | ||||
|           $build_target->getID(), | ||||
|           $build_target->getImplementation()->getName())) | ||||
|         ->setUser($viewer); | ||||
|       $properties = new PHUIPropertyListView(); | ||||
|  | ||||
| @@ -280,7 +283,7 @@ final class HarbormasterBuildViewController | ||||
|       case HarbormasterBuild::STATUS_PENDING: | ||||
|         return pht('Pending'); | ||||
|       case HarbormasterBuild::STATUS_WAITING: | ||||
|         return pht('Waiting on Resource'); | ||||
|         return pht('Waiting'); | ||||
|       case HarbormasterBuild::STATUS_BUILDING: | ||||
|         return pht('Building'); | ||||
|       case HarbormasterBuild::STATUS_PASSED: | ||||
|   | ||||
| @@ -52,8 +52,8 @@ final class HarbormasterBuildableViewController | ||||
|             $item->addAttribute(pht('Pending')); | ||||
|             break; | ||||
|           case HarbormasterBuild::STATUS_WAITING: | ||||
|             $item->setBarColor('blue'); | ||||
|             $item->addAttribute(pht('Waiting on Resource')); | ||||
|             $item->setBarColor('violet'); | ||||
|             $item->addAttribute(pht('Waiting')); | ||||
|             break; | ||||
|           case HarbormasterBuild::STATUS_BUILDING: | ||||
|             $item->setBarColor('yellow'); | ||||
|   | ||||
| @@ -53,7 +53,7 @@ abstract class BuildStepImplementation { | ||||
|   /** | ||||
|    * Validate the current settings of this build step. | ||||
|    */ | ||||
|   public function validate() { | ||||
|   public function validateSettings() { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,127 @@ | ||||
| <?php | ||||
|  | ||||
| final class WaitForPreviousBuildStepImplementation | ||||
|   extends BuildStepImplementation { | ||||
|  | ||||
|   public function getName() { | ||||
|     return pht('Wait for Previous Commits to Build'); | ||||
|   } | ||||
|  | ||||
|   public function getGenericDescription() { | ||||
|     return pht( | ||||
|       'Wait for previous commits to finish building the current plan '. | ||||
|       'before continuing.'); | ||||
|   } | ||||
|  | ||||
|   public function getDescription() { | ||||
|     return pht( | ||||
|       'Wait for previous commits to finish building the current plan '. | ||||
|       'before continuing.'); | ||||
|   } | ||||
|  | ||||
|   public function execute( | ||||
|     HarbormasterBuild $build, | ||||
|     HarbormasterBuildTarget $build_target) { | ||||
|  | ||||
|     // We can only wait when building against commits. | ||||
|     $buildable = $build->getBuildable(); | ||||
|     $object = $buildable->getBuildableObject(); | ||||
|     if (!($object instanceof PhabricatorRepositoryCommit)) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     // We are blocked until all previous builds finish. | ||||
|     $build->setBuildStatus(HarbormasterBuild::STATUS_WAITING); | ||||
|     $build->save(); | ||||
|  | ||||
|     // Block until all previous builds of the same build plan have | ||||
|     // finished. | ||||
|     $plan = $build->getBuildPlan(); | ||||
|  | ||||
|     $log = null; | ||||
|     $log_start = null; | ||||
|     $blockers = $this->getBlockers($object, $plan, $build); | ||||
|     while (count($blockers) > 0) { | ||||
|       if ($build->checkForCancellation()) { | ||||
|         if ($log !== null) { | ||||
|           $log->finalize($log_start); | ||||
|         } | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       if ($log === null) { | ||||
|         $log = $build->createLog($build_target, "waiting", "blockers"); | ||||
|         $log_start = $log->start(); | ||||
|       } | ||||
|  | ||||
|       $log->append("Blocked by: ".implode(",", $blockers)."\n"); | ||||
|  | ||||
|       sleep(1); | ||||
|       $blockers = $this->getBlockers($object, $plan, $build); | ||||
|     } | ||||
|     if ($log !== null) { | ||||
|       $log->finalize($log_start); | ||||
|     } | ||||
|  | ||||
|     // Move back into building status. | ||||
|     $build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING); | ||||
|     $build->save(); | ||||
|   } | ||||
|  | ||||
|   private function getBlockers( | ||||
|     PhabricatorRepositoryCommit $commit, | ||||
|     HarbormasterBuildPlan $plan, | ||||
|     HarbormasterBuild $source) { | ||||
|  | ||||
|     $call = new ConduitCall( | ||||
|       'diffusion.commitparentsquery', | ||||
|       array( | ||||
|         'commit'   => $commit->getCommitIdentifier(), | ||||
|         'callsign' => $commit->getRepository()->getCallsign() | ||||
|       )); | ||||
|     $call->setUser(PhabricatorUser::getOmnipotentUser()); | ||||
|     $parents = $call->execute(); | ||||
|  | ||||
|     $hashes = array(); | ||||
|     foreach ($parents as $parent => $obj) { | ||||
|       $hashes[] = $parent; | ||||
|     } | ||||
|  | ||||
|     $parents = id(new DiffusionCommitQuery()) | ||||
|       ->setViewer(PhabricatorUser::getOmnipotentUser()) | ||||
|       ->withRepository($commit->getRepository()) | ||||
|       ->withIdentifiers($hashes) | ||||
|       ->execute(); | ||||
|  | ||||
|     $blockers = array(); | ||||
|  | ||||
|     $build_objects = array(); | ||||
|     foreach ($parents as $parent) { | ||||
|       if (!$parent->isImported()) { | ||||
|         $blockers[] = pht('Commit %s', $parent->getCommitIdentifier()); | ||||
|       } else { | ||||
|         $build_objects[] = $parent->getPHID(); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     $buildables = id(new HarbormasterBuildableQuery()) | ||||
|       ->setViewer(PhabricatorUser::getOmnipotentUser()) | ||||
|       ->withBuildablePHIDs($build_objects) | ||||
|       ->execute(); | ||||
|     $buildable_phids = mpull($buildables, 'getPHID'); | ||||
|  | ||||
|     $builds = id(new HarbormasterBuildQuery()) | ||||
|       ->setViewer(PhabricatorUser::getOmnipotentUser()) | ||||
|       ->withBuildablePHIDs($buildable_phids) | ||||
|       ->withBuildPlanPHIDs(array($plan->getPHID())) | ||||
|       ->execute(); | ||||
|  | ||||
|     foreach ($builds as $build) { | ||||
|       if ($build->isBuilding()) { | ||||
|         $blockers[] = pht('Build %d', $build->getID()); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return $blockers; | ||||
|   } | ||||
| } | ||||
| @@ -94,6 +94,13 @@ final class HarbormasterBuild extends HarbormasterDAO | ||||
|     return $this->assertAttached($this->buildPlan); | ||||
|   } | ||||
|  | ||||
|   public function isBuilding() { | ||||
|     return $this->getBuildStatus() === self::STATUS_PENDING || | ||||
|       $this->getBuildStatus() === self::STATUS_WAITING || | ||||
|       $this->getBuildStatus() === self::STATUS_BUILDING || | ||||
|       $this->getCancelRequested(); | ||||
|   } | ||||
|  | ||||
|   public function createLog( | ||||
|     HarbormasterBuildTarget $build_target, | ||||
|     $log_source, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 James Rhodes
					James Rhodes