Index: src/__phutil_library_map__.php =================================================================== --- src/__phutil_library_map__.php +++ src/__phutil_library_map__.php @@ -2366,6 +2366,7 @@ '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 @@ 'SlowvoteRemarkupRule' => 'PhabricatorRemarkupRuleObject', 'UploadArtifactBuildStepImplementation' => 'VariableBuildStepImplementation', 'VariableBuildStepImplementation' => 'BuildStepImplementation', + 'WaitForPreviousBuildStepImplementation' => 'BuildStepImplementation', ), )); Index: src/applications/harbormaster/controller/HarbormasterBuildViewController.php =================================================================== --- src/applications/harbormaster/controller/HarbormasterBuildViewController.php +++ src/applications/harbormaster/controller/HarbormasterBuildViewController.php @@ -49,7 +49,10 @@ $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 @@ 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: Index: src/applications/harbormaster/controller/HarbormasterBuildableViewController.php =================================================================== --- src/applications/harbormaster/controller/HarbormasterBuildableViewController.php +++ src/applications/harbormaster/controller/HarbormasterBuildableViewController.php @@ -52,8 +52,8 @@ $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'); Index: src/applications/harbormaster/step/BuildStepImplementation.php =================================================================== --- src/applications/harbormaster/step/BuildStepImplementation.php +++ src/applications/harbormaster/step/BuildStepImplementation.php @@ -53,7 +53,7 @@ /** * Validate the current settings of this build step. */ - public function validate() { + public function validateSettings() { return true; } Index: src/applications/harbormaster/step/WaitForPreviousBuildStepImplementation.php =================================================================== --- /dev/null +++ src/applications/harbormaster/step/WaitForPreviousBuildStepImplementation.php @@ -0,0 +1,127 @@ +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; + } +} Index: src/applications/harbormaster/storage/build/HarbormasterBuild.php =================================================================== --- src/applications/harbormaster/storage/build/HarbormasterBuild.php +++ src/applications/harbormaster/storage/build/HarbormasterBuild.php @@ -94,6 +94,13 @@ 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,