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

Use herald to trigger builds of revisions and commits.

Summary:
Depends on D7500.

This seemed like a pretty good idea once I thought of it. Instead of having some custom triggering logic instead Harbormaster, I figured it best to leverage all of Herald's power so that users can create rules to apply builds to commits and differential revisions. This gives the added advantage that they can trigger off builds for particular types of revisions and commits, which seems like it could be really useful (e.g. run extra tests against revisions that touch sensitive areas of the code).

Test Plan: Ran the usual daemons + the Harbormaster daemon. Pushed a commit to the repository and saw both the buildable and build get created when the commit worked picked it up. Submitted a diff and saw both the buildable and build get created when the Herald rules were evaluated for the diff.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley

CC: Korvin, epriestley, aran, hwinkel

Maniphest Tasks: T1049

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

authored by

James Rhodes and committed by
epriestley
7ffea046 3de4e91d

+194 -26
+5
src/applications/differential/editor/DifferentialRevisionEditor.php
··· 273 273 $rem_ccs = $adapter->getCCsRemovedByHerald(); 274 274 $blocking_reviewers = array_keys( 275 275 $adapter->getBlockingReviewersAddedByHerald()); 276 + 277 + HarbormasterBuildable::applyBuildPlans( 278 + $diff->getPHID(), 279 + $revision->getPHID(), 280 + $adapter->getBuildPlans()); 276 281 } else { 277 282 $sub = array( 278 283 'rev' => array(),
+4 -11
src/applications/harbormaster/controller/HarbormasterBuildableApplyController.php
··· 31 31 ->withIDs(array($request->getInt('build-plan'))) 32 32 ->executeOne(); 33 33 34 - $build = HarbormasterBuild::initializeNewBuild($viewer); 35 - $build->setBuildablePHID($buildable->getPHID()); 36 - $build->setBuildPlanPHID($plan->getPHID()); 37 - $build->setBuildStatus(HarbormasterBuild::STATUS_PENDING); 38 - $build->save(); 39 - 40 - PhabricatorWorker::scheduleTask( 41 - 'HarbormasterBuildWorker', 42 - array( 43 - 'buildID' => $build->getID() 44 - )); 34 + HarbormasterBuildable::applyBuildPlans( 35 + $buildable->getBuildablePHID(), 36 + $buildable->getContainerPHID(), 37 + array($plan->getPHID())); 45 38 46 39 return id(new AphrontRedirectResponse())->setURI($buildable_uri); 47 40 }
+7 -1
src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
··· 19 19 ->setViewer($viewer) 20 20 ->withIDs(array($id)) 21 21 ->needBuildableHandles(true) 22 - ->needContainerObjects(true) 22 + ->needContainerHandles(true) 23 23 ->executeOne(); 24 24 if (!$buildable) { 25 25 return new Aphront404Response(); ··· 138 138 $properties->addProperty( 139 139 pht('Buildable'), 140 140 $buildable->getBuildableHandle()->renderLink()); 141 + 142 + if ($buildable->getContainerHandle() !== null) { 143 + $properties->addProperty( 144 + pht('Container'), 145 + $buildable->getContainerHandle()->renderLink()); 146 + } 141 147 142 148 } 143 149
+2
src/applications/harbormaster/phid/HarbormasterPHIDTypeBuildPlan.php
··· 31 31 32 32 foreach ($handles as $phid => $handle) { 33 33 $build_plan = $objects[$phid]; 34 + $handles[$phid]->setName($build_plan->getName()); 35 + $handles[$phid]->setURI('/harbormaster/plan/'.$build_plan->getID()); 34 36 } 35 37 } 36 38
+39 -13
src/applications/harbormaster/query/HarbormasterBuildableQuery.php
··· 9 9 private $containerPHIDs; 10 10 11 11 private $needContainerObjects; 12 + private $needContainerHandles; 12 13 private $needBuildableHandles; 13 14 private $needBuilds; 14 15 ··· 34 35 35 36 public function needContainerObjects($need) { 36 37 $this->needContainerObjects = $need; 38 + return $this; 39 + } 40 + 41 + public function needContainerHandles($need) { 42 + $this->needContainerHandles = $need; 37 43 return $this; 38 44 } 39 45 ··· 88 94 } 89 95 90 96 protected function didFilterPage(array $page) { 91 - if ($this->needContainerObjects) { 92 - $containers = array(); 93 - 97 + if ($this->needContainerObjects || $this->needContainerHandles) { 94 98 $container_phids = array_filter(mpull($page, 'getContainerPHID')); 95 - if ($container_phids) { 96 - $containers = id(new PhabricatorObjectQuery()) 97 - ->setViewer($this->getViewer()) 98 - ->withPHIDs($container_phids) 99 - ->setParentQuery($this) 100 - ->execute(); 101 - $containers = mpull($containers, null, 'getPHID'); 99 + 100 + if ($this->needContainerObjects) { 101 + $containers = array(); 102 + 103 + if ($container_phids) { 104 + $containers = id(new PhabricatorObjectQuery()) 105 + ->setViewer($this->getViewer()) 106 + ->withPHIDs($container_phids) 107 + ->setParentQuery($this) 108 + ->execute(); 109 + $containers = mpull($containers, null, 'getPHID'); 110 + } 111 + 112 + foreach ($page as $key => $buildable) { 113 + $container_phid = $buildable->getContainerPHID(); 114 + $buildable->attachContainerObject(idx($containers, $container_phid)); 115 + } 102 116 } 103 117 104 - foreach ($page as $key => $buildable) { 105 - $container_phid = $buildable->getContainerPHID(); 106 - $buildable->attachContainerObject(idx($containers, $container_phid)); 118 + if ($this->needContainerHandles) { 119 + $handles = array(); 120 + 121 + if ($container_phids) { 122 + $handles = id(new PhabricatorHandleQuery()) 123 + ->setViewer($this->getViewer()) 124 + ->withPHIDs($container_phids) 125 + ->setParentQuery($this) 126 + ->execute(); 127 + } 128 + 129 + foreach ($page as $key => $buildable) { 130 + $container_phid = $buildable->getContainerPHID(); 131 + $buildable->attachContainerHandle(idx($handles, $container_phid)); 132 + } 107 133 } 108 134 } 109 135
+79
src/applications/harbormaster/storage/HarbormasterBuildable.php
··· 11 11 private $buildableObject = self::ATTACHABLE; 12 12 private $containerObject = self::ATTACHABLE; 13 13 private $buildableHandle = self::ATTACHABLE; 14 + private $containerHandle = self::ATTACHABLE; 14 15 private $builds = self::ATTACHABLE; 15 16 16 17 const STATUS_WHATEVER = 'whatever'; ··· 21 22 ->setBuildableStatus(self::STATUS_WHATEVER); 22 23 } 23 24 25 + /** 26 + * Returns an existing buildable for the object's PHID or creates a 27 + * new buildable implicitly if needed. 28 + */ 29 + public static function createOrLoadExisting( 30 + PhabricatorUser $actor, 31 + $buildable_object_phid, 32 + $container_object_phid) { 33 + 34 + $buildable = id(new HarbormasterBuildableQuery()) 35 + ->setViewer($actor) 36 + ->withBuildablePHIDs(array($buildable_object_phid)) 37 + ->executeOne(); 38 + if ($buildable) { 39 + return $buildable; 40 + } 41 + $buildable = HarbormasterBuildable::initializeNewBuildable($actor) 42 + ->setBuildablePHID($buildable_object_phid) 43 + ->setContainerPHID($container_object_phid); 44 + $buildable->save(); 45 + return $buildable; 46 + } 47 + 48 + /** 49 + * Looks up the plan PHIDs and applies the plans to the specified 50 + * object identified by it's PHID. 51 + */ 52 + public static function applyBuildPlans( 53 + $phid, 54 + $container_phid, 55 + array $plan_phids) { 56 + 57 + if (count($plan_phids) === 0) { 58 + return; 59 + } 60 + 61 + // Skip all of this logic if the Harbormaster application 62 + // isn't currently installed. 63 + 64 + $harbormaster_app = 'PhabricatorApplicationHarbormaster'; 65 + if (!PhabricatorApplication::isClassInstalled($harbormaster_app)) { 66 + return; 67 + } 68 + 69 + $buildable = HarbormasterBuildable::createOrLoadExisting( 70 + PhabricatorUser::getOmnipotentUser(), 71 + $phid, 72 + $container_phid); 73 + 74 + $plans = id(new HarbormasterBuildPlanQuery()) 75 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 76 + ->withPHIDs($plan_phids) 77 + ->execute(); 78 + foreach ($plans as $plan) { 79 + $build = HarbormasterBuild::initializeNewBuild( 80 + PhabricatorUser::getOmnipotentUser()); 81 + $build->setBuildablePHID($buildable->getPHID()); 82 + $build->setBuildPlanPHID($plan->getPHID()); 83 + $build->setBuildStatus(HarbormasterBuild::STATUS_PENDING); 84 + $build->save(); 85 + 86 + PhabricatorWorker::scheduleTask( 87 + 'HarbormasterBuildWorker', 88 + array( 89 + 'buildID' => $build->getID() 90 + )); 91 + } 92 + } 93 + 24 94 public function getConfiguration() { 25 95 return array( 26 96 self::CONFIG_AUX_PHID => true, ··· 48 118 49 119 public function getContainerObject() { 50 120 return $this->assertAttached($this->containerObject); 121 + } 122 + 123 + public function attachContainerHandle($container_handle) { 124 + $this->containerHandle = $container_handle; 125 + return $this; 126 + } 127 + 128 + public function getContainerHandle() { 129 + return $this->assertAttached($this->containerHandle); 51 130 } 52 131 53 132 public function attachBuildableHandle($buildable_handle) {
+5
src/applications/herald/adapter/HeraldAdapter.php
··· 54 54 const ACTION_ADD_PROJECTS = 'addprojects'; 55 55 const ACTION_ADD_REVIEWERS = 'addreviewers'; 56 56 const ACTION_ADD_BLOCKING_REVIEWERS = 'addblockingreviewers'; 57 + const ACTION_APPLY_BUILD_PLANS = 'applybuildplans'; 57 58 58 59 const VALUE_TEXT = 'text'; 59 60 const VALUE_NONE = 'none'; ··· 67 68 const VALUE_FLAG_COLOR = 'flagcolor'; 68 69 const VALUE_CONTENT_SOURCE = 'contentsource'; 69 70 const VALUE_USER_OR_PROJECT = 'userorproject'; 71 + const VALUE_BUILD_PLAN = 'buildplan'; 70 72 71 73 private $contentSource; 72 74 ··· 490 492 self::ACTION_ADD_PROJECTS => pht('Add projects'), 491 493 self::ACTION_ADD_REVIEWERS => pht('Add reviewers'), 492 494 self::ACTION_ADD_BLOCKING_REVIEWERS => pht('Add blocking reviewers'), 495 + self::ACTION_APPLY_BUILD_PLANS => pht('Apply build plans'), 493 496 ); 494 497 case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: 495 498 return array( ··· 657 660 case self::ACTION_ADD_REVIEWERS: 658 661 case self::ACTION_ADD_BLOCKING_REVIEWERS: 659 662 return self::VALUE_USER_OR_PROJECT; 663 + case self::ACTION_APPLY_BUILD_PLANS: 664 + return self::VALUE_BUILD_PLAN; 660 665 default: 661 666 throw new Exception("Unknown or invalid action '{$action}'."); 662 667 }
+16 -1
src/applications/herald/adapter/HeraldCommitAdapter.php
··· 22 22 protected $emailPHIDs = array(); 23 23 protected $addCCPHIDs = array(); 24 24 protected $auditMap = array(); 25 + protected $buildPlans = array(); 25 26 26 27 protected $affectedPaths; 27 28 protected $affectedRevision; ··· 120 121 self::ACTION_ADD_CC, 121 122 self::ACTION_EMAIL, 122 123 self::ACTION_AUDIT, 123 - self::ACTION_NOTHING, 124 + self::ACTION_APPLY_BUILD_PLANS, 125 + self::ACTION_NOTHING 124 126 ); 125 127 case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: 126 128 return array( ··· 174 176 175 177 public function getAuditMap() { 176 178 return $this->auditMap; 179 + } 180 + 181 + public function getBuildPlans() { 182 + return $this->buildPlans; 177 183 } 178 184 179 185 public function getHeraldName() { ··· 421 427 $effect, 422 428 true, 423 429 pht('Triggered an audit.')); 430 + break; 431 + case self::ACTION_APPLY_BUILD_PLANS: 432 + foreach ($effect->getTarget() as $phid) { 433 + $this->buildPlans[] = $phid; 434 + } 435 + $result[] = new HeraldApplyTranscript( 436 + $effect, 437 + true, 438 + pht('Applied build plans.')); 424 439 break; 425 440 case self::ACTION_FLAG: 426 441 $result[] = parent::applyFlagEffect(
+15
src/applications/herald/adapter/HeraldDifferentialRevisionAdapter.php
··· 14 14 protected $emailPHIDs = array(); 15 15 protected $addReviewerPHIDs = array(); 16 16 protected $blockingReviewerPHIDs = array(); 17 + protected $buildPlans = array(); 17 18 18 19 protected $repository; 19 20 protected $affectedPackages; ··· 115 116 116 117 public function getBlockingReviewersAddedByHerald() { 117 118 return $this->blockingReviewerPHIDs; 119 + } 120 + 121 + public function getBuildPlans() { 122 + return $this->buildPlans; 118 123 } 119 124 120 125 public function getPHID() { ··· 349 354 self::ACTION_EMAIL, 350 355 self::ACTION_ADD_REVIEWERS, 351 356 self::ACTION_ADD_BLOCKING_REVIEWERS, 357 + self::ACTION_APPLY_BUILD_PLANS, 352 358 self::ACTION_NOTHING, 353 359 ); 354 360 case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: ··· 467 473 $effect, 468 474 true, 469 475 pht('Added blocking reviewers.')); 476 + break; 477 + case self::ACTION_APPLY_BUILD_PLANS: 478 + foreach ($effect->getTarget() as $phid) { 479 + $this->buildPlans[] = $phid; 480 + } 481 + $result[] = new HeraldApplyTranscript( 482 + $effect, 483 + true, 484 + pht('Applied build plans.')); 470 485 break; 471 486 default: 472 487 throw new Exception("No rules to handle action '{$action}'.");
+1
src/applications/herald/controller/HeraldRuleController.php
··· 511 511 'package' => '/typeahead/common/packages/', 512 512 'project' => '/typeahead/common/projects/', 513 513 'userorproject' => '/typeahead/common/accountsorprojects/', 514 + 'buildplan' => '/typeahead/common/buildplans/', 514 515 ), 515 516 'markup' => $template, 516 517 );
+5
src/applications/repository/worker/PhabricatorRepositoryCommitHeraldWorker.php
··· 66 66 $this->createAudits($commit, $audit_phids, $cc_phids, $rules); 67 67 } 68 68 69 + HarbormasterBuildable::applyBuildPlans( 70 + $commit->getPHID(), 71 + $repository->getPHID(), 72 + $adapter->getBuildPlans()); 73 + 69 74 $explicit_auditors = $this->createAuditsFromCommitMessage($commit, $data); 70 75 71 76 if ($repository->getDetail('herald-disabled')) {
+15
src/applications/typeahead/controller/PhabricatorTypeaheadCommonDatasourceController.php
··· 35 35 $need_noproject = false; 36 36 $need_symbols = false; 37 37 $need_jump_objects = false; 38 + $need_build_plans = false; 38 39 switch ($this->type) { 39 40 case 'mainsearch': 40 41 $need_users = true; ··· 92 93 break; 93 94 case 'arcanistprojects': 94 95 $need_arcanist_projects = true; 96 + break; 97 + case 'buildplans': 98 + $need_build_plans = true; 95 99 break; 96 100 } 97 101 ··· 216 220 ->setName($list->getName()) 217 221 ->setURI($list->getURI()) 218 222 ->setPHID($list->getPHID()); 223 + } 224 + } 225 + 226 + if ($need_build_plans) { 227 + $plans = id(new HarbormasterBuildPlanQuery()) 228 + ->setViewer($viewer) 229 + ->execute(); 230 + foreach ($plans as $plan) { 231 + $results[] = id(new PhabricatorTypeaheadResult()) 232 + ->setName($plan->getName()) 233 + ->setPHID($plan->getPHID()); 219 234 } 220 235 } 221 236
+1
webroot/rsrc/js/application/herald/HeraldRuleEditor.js
··· 221 221 case 'package': 222 222 case 'project': 223 223 case 'userorproject': 224 + case 'buildplan': 224 225 var tokenizer = this._newTokenizer(type); 225 226 input = tokenizer[0]; 226 227 get_fn = tokenizer[1];