@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.

Start buildables in "PREPARING", move them to "BUILDING" after builds queue

Summary:
Depends on D19064. Ref T13054. See that task for additional discussion.

When buildables are created by `arc` and have lint/unit messages, they can currently pass or fail before Herald triggers actual builds. This puts them in a pre-build state where they can't complete until Herald says it's okay.

On its own, this change intentionally strands `arc diff --only` diffs in the "PREPARING" stage forever.

Test Plan:
- Ran a build with `bin/harbormaster`, saw it build normally.
- Ran a build with web UI, saw it build normally.
- Ran a build with `arc diff`, saw it build normally.
- Ran a build with `arc diff --only`, saw it hang in "PREPARING" forever.

Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam

Maniphest Tasks: T13054

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

+174 -27
+10
src/applications/harbormaster/constants/HarbormasterBuildableStatus.php
··· 2 2 3 3 final class HarbormasterBuildableStatus extends Phobject { 4 4 5 + const STATUS_PREPARING = 'preparing'; 5 6 const STATUS_BUILDING = 'building'; 6 7 const STATUS_PASSED = 'passed'; 7 8 const STATUS_FAILED = 'failed'; ··· 42 43 return $this->getProperty('color'); 43 44 } 44 45 46 + public function isPreparing() { 47 + return ($this->key === self::STATUS_PREPARING); 48 + } 49 + 45 50 public static function getOptionMap() { 46 51 return ipull(self::getSpecifications(), 'name'); 47 52 } 48 53 49 54 private static function getSpecifications() { 50 55 return array( 56 + self::STATUS_PREPARING => array( 57 + 'name' => pht('Preparing'), 58 + 'color' => 'blue', 59 + 'icon' => 'fa-hourglass-o', 60 + ), 51 61 self::STATUS_BUILDING => array( 52 62 'name' => pht('Building'), 53 63 'color' => 'blue',
+6
src/applications/harbormaster/controller/HarbormasterPlanRunController.php
··· 59 59 60 60 if (!$errors) { 61 61 $buildable->save(); 62 + 63 + $buildable->sendMessage( 64 + $viewer, 65 + HarbormasterMessageType::BUILDABLE_BUILD, 66 + false); 67 + 62 68 $buildable->applyPlan($plan, array(), $viewer->getPHID()); 63 69 64 70 $buildable_uri = '/B'.$buildable->getID();
+64 -20
src/applications/harbormaster/engine/HarbormasterBuildEngine.php
··· 428 428 * @param HarbormasterBuild The buildable to update. 429 429 * @return void 430 430 */ 431 - private function updateBuildable(HarbormasterBuildable $buildable) { 431 + public function updateBuildable(HarbormasterBuildable $buildable) { 432 432 $viewer = $this->getViewer(); 433 433 434 434 $lock_key = 'harbormaster.buildable:'.$buildable->getID(); ··· 440 440 ->needBuilds(true) 441 441 ->executeOne(); 442 442 443 - $all_pass = true; 444 - $any_fail = false; 445 - foreach ($buildable->getBuilds() as $build) { 446 - if (!$build->isPassed()) { 447 - $all_pass = false; 448 - } 443 + $messages = id(new HarbormasterBuildMessageQuery()) 444 + ->setViewer($viewer) 445 + ->withReceiverPHIDs(array($buildable->getPHID())) 446 + ->withConsumed(false) 447 + ->execute(); 449 448 450 - if ($build->isComplete() && !$build->isPassed()) { 451 - $any_fail = true; 449 + $done_preparing = false; 450 + foreach ($messages as $message) { 451 + switch ($message->getType()) { 452 + case HarbormasterMessageType::BUILDABLE_BUILD: 453 + $done_preparing = true; 454 + break; 455 + default: 456 + break; 452 457 } 458 + 459 + $message 460 + ->setIsConsumed(true) 461 + ->save(); 453 462 } 454 463 455 - if ($any_fail) { 456 - $new_status = HarbormasterBuildableStatus::STATUS_FAILED; 457 - } else if ($all_pass) { 458 - $new_status = HarbormasterBuildableStatus::STATUS_PASSED; 459 - } else { 460 - $new_status = HarbormasterBuildableStatus::STATUS_BUILDING; 464 + // If we received a "build" command, all builds are scheduled and we can 465 + // move out of "preparing" into "building". 466 + 467 + if ($done_preparing) { 468 + if ($buildable->isPreparing()) { 469 + $buildable 470 + ->setBuildableStatus(HarbormasterBuildableStatus::STATUS_BUILDING) 471 + ->save(); 472 + } 461 473 } 462 474 463 - $old_status = $buildable->getBuildableStatus(); 464 - $did_update = ($old_status != $new_status); 465 - if ($did_update) { 466 - $buildable->setBuildableStatus($new_status); 467 - $buildable->save(); 475 + // Don't update the buildable status if we're still preparing builds: more 476 + // builds may still be scheduled shortly, so even if every build we know 477 + // about so far has passed, that doesn't mean the buildable has actually 478 + // passed everything it needs to. 479 + 480 + if (!$buildable->isPreparing()) { 481 + $all_pass = true; 482 + $any_fail = false; 483 + foreach ($buildable->getBuilds() as $build) { 484 + if (!$build->isPassed()) { 485 + $all_pass = false; 486 + } 487 + 488 + if ($build->isComplete() && !$build->isPassed()) { 489 + $any_fail = true; 490 + } 491 + } 492 + 493 + if ($any_fail) { 494 + $new_status = HarbormasterBuildableStatus::STATUS_FAILED; 495 + } else if ($all_pass) { 496 + $new_status = HarbormasterBuildableStatus::STATUS_PASSED; 497 + } else { 498 + $new_status = HarbormasterBuildableStatus::STATUS_BUILDING; 499 + } 500 + 501 + $old_status = $buildable->getBuildableStatus(); 502 + $did_update = ($old_status != $new_status); 503 + if ($did_update) { 504 + $buildable->setBuildableStatus($new_status); 505 + $buildable->save(); 506 + } 468 507 } 469 508 470 509 $lock->unlock(); 510 + 511 + // Don't publish anything if we're still preparing builds. 512 + if ($buildable->isPreparing()) { 513 + return; 514 + } 471 515 472 516 // If we changed the buildable status, try to post a transaction to the 473 517 // object about it. We can safely do this outside of the locked region.
+2
src/applications/harbormaster/engine/HarbormasterMessageType.php
··· 6 6 const MESSAGE_FAIL = 'fail'; 7 7 const MESSAGE_WORK = 'work'; 8 8 9 + const BUILDABLE_BUILD = 'build'; 10 + 9 11 public static function getAllMessages() { 10 12 return array_keys(self::getMessageSpecifications()); 11 13 }
+5
src/applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php
··· 83 83 ->setContainerPHID($buildable->getHarbormasterContainerPHID()) 84 84 ->save(); 85 85 86 + $buildable->sendMessage( 87 + $viewer, 88 + HarbormasterMessageType::BUILDABLE_BUILD, 89 + false); 90 + 86 91 $console->writeOut( 87 92 "%s\n", 88 93 pht(
+33 -1
src/applications/harbormaster/storage/HarbormasterBuildable.php
··· 18 18 public static function initializeNewBuildable(PhabricatorUser $actor) { 19 19 return id(new HarbormasterBuildable()) 20 20 ->setIsManualBuildable(0) 21 - ->setBuildableStatus(HarbormasterBuildableStatus::STATUS_BUILDING); 21 + ->setBuildableStatus(HarbormasterBuildableStatus::STATUS_PREPARING); 22 22 } 23 23 24 24 public function getMonogram() { ··· 225 225 226 226 public function getStatusColor() { 227 227 return $this->getBuildableStatusObject()->getColor(); 228 + } 229 + 230 + public function isPreparing() { 231 + return $this->getBuildableStatusObject()->isPreparing(); 232 + } 233 + 234 + 235 + /* -( Messages )----------------------------------------------------------- */ 236 + 237 + 238 + public function sendMessage( 239 + PhabricatorUser $viewer, 240 + $message_type, 241 + $queue_update) { 242 + 243 + $message = HarbormasterBuildMessage::initializeNewMessage($viewer) 244 + ->setReceiverPHID($this->getPHID()) 245 + ->setType($message_type) 246 + ->save(); 247 + 248 + if ($queue_update) { 249 + PhabricatorWorker::scheduleTask( 250 + 'HarbormasterBuildWorker', 251 + array( 252 + 'buildablePHID' => $this->getPHID(), 253 + ), 254 + array( 255 + 'objectPHID' => $this->getPHID(), 256 + )); 257 + } 258 + 259 + return $message; 228 260 } 229 261 230 262
+31 -5
src/applications/harbormaster/worker/HarbormasterBuildWorker.php
··· 17 17 18 18 protected function doWork() { 19 19 $viewer = $this->getViewer(); 20 - $build = $this->loadBuild(); 20 + 21 + $engine = id(new HarbormasterBuildEngine()) 22 + ->setViewer($viewer); 21 23 22 - id(new HarbormasterBuildEngine()) 23 - ->setViewer($viewer) 24 - ->setBuild($build) 25 - ->continueBuild(); 24 + $data = $this->getTaskData(); 25 + $build_id = idx($data, 'buildID'); 26 + 27 + if ($build_id) { 28 + $build = $this->loadBuild(); 29 + $engine->setBuild($build); 30 + $engine->continueBuild(); 31 + } else { 32 + $buildable = $this->loadBuildable(); 33 + $engine->updateBuildable($buildable); 34 + } 26 35 } 27 36 28 37 private function loadBuild() { ··· 40 49 } 41 50 42 51 return $build; 52 + } 53 + 54 + private function loadBuildable() { 55 + $data = $this->getTaskData(); 56 + $phid = idx($data, 'buildablePHID'); 57 + 58 + $viewer = $this->getViewer(); 59 + $buildable = id(new HarbormasterBuildableQuery()) 60 + ->setViewer($viewer) 61 + ->withPHIDs(array($phid)) 62 + ->executeOne(); 63 + if (!$buildable) { 64 + throw new PhabricatorWorkerPermanentFailureException( 65 + pht('Invalid buildable PHID "%s".', $phid)); 66 + } 67 + 68 + return $buildable; 43 69 } 44 70 45 71 }
+23 -1
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 3262 3262 $this->setHeraldTranscript($xscript); 3263 3263 3264 3264 if ($adapter instanceof HarbormasterBuildableAdapterInterface) { 3265 + $buildable_phid = $adapter->getHarbormasterBuildablePHID(); 3266 + 3265 3267 HarbormasterBuildable::applyBuildPlans( 3266 - $adapter->getHarbormasterBuildablePHID(), 3268 + $buildable_phid, 3267 3269 $adapter->getHarbormasterContainerPHID(), 3268 3270 $adapter->getQueuedHarbormasterBuildRequests()); 3271 + 3272 + // Whether we queued any builds or not, any automatic buildable for this 3273 + // object is now done preparing builds and can transition into a 3274 + // completed status. 3275 + $buildables = id(new HarbormasterBuildableQuery()) 3276 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 3277 + ->withManualBuildables(false) 3278 + ->withBuildablePHIDs(array($buildable_phid)) 3279 + ->execute(); 3280 + foreach ($buildables as $buildable) { 3281 + // If this buildable has already moved beyond preparation, we don't 3282 + // need to nudge it again. 3283 + if (!$buildable->isPreparing()) { 3284 + continue; 3285 + } 3286 + $buildable->sendMessage( 3287 + $this->getActor(), 3288 + HarbormasterMessageType::BUILDABLE_BUILD, 3289 + true); 3290 + } 3269 3291 } 3270 3292 3271 3293 $this->mustEncrypt = $adapter->getMustEncryptReasons();