diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 7dda0a0229..67f61c1341 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -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', ), )); diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php index 2a946dad72..8029b6fc3a 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php @@ -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: diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php index c4f5291f37..758d8b28da 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php @@ -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'); diff --git a/src/applications/harbormaster/step/BuildStepImplementation.php b/src/applications/harbormaster/step/BuildStepImplementation.php index 86c3a1870f..087aba2f7e 100644 --- a/src/applications/harbormaster/step/BuildStepImplementation.php +++ b/src/applications/harbormaster/step/BuildStepImplementation.php @@ -53,7 +53,7 @@ abstract class BuildStepImplementation { /** * Validate the current settings of this build step. */ - public function validate() { + public function validateSettings() { return true; } diff --git a/src/applications/harbormaster/step/WaitForPreviousBuildStepImplementation.php b/src/applications/harbormaster/step/WaitForPreviousBuildStepImplementation.php new file mode 100644 index 0000000000..40b4bd0be8 --- /dev/null +++ b/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; + } +} diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuild.php b/src/applications/harbormaster/storage/build/HarbormasterBuild.php index 0ffc72c545..96e25ab239 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php @@ -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,