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

Formalize "manual" buildables in Harbormaster

Summary:
Ref T1049. Generally, it's useful to separate test/trial/manual runs from production/automatic runs.

For example, you don't want to email a bunch of people that the build is broken just because you messed something up when writing a new build plan. You'd rather try it first, then promote it into production once you have some good runs.

Similarly, test runs generally should not affect the outside world, etc. Finally, some build steps (like "wait for other buildables") may want to behave differently when run in production/automation than when run in a testing environment (where they should probably continue immediately).

So, formalize the distinction between automatic buildables (those created passively by the system in response to events) and manual buildables (those created explicitly by users). Add filtering, and stop the automated parts of the system from interacting with the manual parts (for example, we won't show manual results on revisions).

This also moves the "Apply Build Plan" to a third, new home: instead of the sidebar or Buildables, it's now on plans. I think this generally makes more sense given how things have developed. Broadly, this improves isolation of test environments.

Test Plan: Created some builds, browsed around, used filters, etc.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T1049

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

+175 -235
+6
resources/sql/patches/20131224.harbormanual.sql
··· 1 + ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildable 2 + ADD isManualBuildable BOOL NOT NULL; 3 + 4 + ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildable 5 + ADD KEY `key_manual` (isManualBuildable); 6 +
+2 -4
src/__phutil_library_map__.php
··· 707 707 'HarbormasterBuildViewController' => 'applications/harbormaster/controller/HarbormasterBuildViewController.php', 708 708 'HarbormasterBuildWorker' => 'applications/harbormaster/worker/HarbormasterBuildWorker.php', 709 709 'HarbormasterBuildable' => 'applications/harbormaster/storage/HarbormasterBuildable.php', 710 - 'HarbormasterBuildableApplyController' => 'applications/harbormaster/controller/HarbormasterBuildableApplyController.php', 711 - 'HarbormasterBuildableEditController' => 'applications/harbormaster/controller/HarbormasterBuildableEditController.php', 712 710 'HarbormasterBuildableListController' => 'applications/harbormaster/controller/HarbormasterBuildableListController.php', 713 711 'HarbormasterBuildableQuery' => 'applications/harbormaster/query/HarbormasterBuildableQuery.php', 714 712 'HarbormasterBuildableSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildableSearchEngine.php', ··· 730 728 'HarbormasterPlanEditController' => 'applications/harbormaster/controller/HarbormasterPlanEditController.php', 731 729 'HarbormasterPlanListController' => 'applications/harbormaster/controller/HarbormasterPlanListController.php', 732 730 'HarbormasterPlanOrderController' => 'applications/harbormaster/controller/HarbormasterPlanOrderController.php', 731 + 'HarbormasterPlanRunController' => 'applications/harbormaster/controller/HarbormasterPlanRunController.php', 733 732 'HarbormasterPlanViewController' => 'applications/harbormaster/controller/HarbormasterPlanViewController.php', 734 733 'HarbormasterRemarkupRule' => 'applications/harbormaster/remarkup/HarbormasterRemarkupRule.php', 735 734 'HarbormasterScratchTable' => 'applications/harbormaster/storage/HarbormasterScratchTable.php', ··· 3133 3132 0 => 'HarbormasterDAO', 3134 3133 1 => 'PhabricatorPolicyInterface', 3135 3134 ), 3136 - 'HarbormasterBuildableApplyController' => 'HarbormasterController', 3137 - 'HarbormasterBuildableEditController' => 'HarbormasterController', 3138 3135 'HarbormasterBuildableListController' => 3139 3136 array( 3140 3137 0 => 'HarbormasterController', ··· 3164 3161 1 => 'PhabricatorApplicationSearchResultsControllerInterface', 3165 3162 ), 3166 3163 'HarbormasterPlanOrderController' => 'HarbormasterController', 3164 + 'HarbormasterPlanRunController' => 'HarbormasterController', 3167 3165 'HarbormasterPlanViewController' => 'HarbormasterPlanController', 3168 3166 'HarbormasterRemarkupRule' => 'PhabricatorRemarkupRuleObject', 3169 3167 'HarbormasterScratchTable' => 'HarbormasterDAO',
+1 -4
src/applications/harbormaster/application/PhabricatorApplicationHarbormaster.php
··· 48 48 '/harbormaster/' => array( 49 49 '(?:query/(?P<queryKey>[^/]+)/)?' 50 50 => 'HarbormasterBuildableListController', 51 - 'buildable/' => array( 52 - 'edit/(?:(?P<id>\d+)/)?' => 'HarbormasterBuildableEditController', 53 - 'apply/(?:(?P<id>\d+)/)?' => 'HarbormasterBuildableApplyController', 54 - ), 55 51 'step/' => array( 56 52 'add/(?:(?P<id>\d+)/)?' => 'HarbormasterStepAddController', 57 53 'edit/(?:(?P<id>\d+)/)?' => 'HarbormasterStepEditController', ··· 67 63 'edit/(?:(?P<id>\d+)/)?' => 'HarbormasterPlanEditController', 68 64 'order/(?:(?P<id>\d+)/)?' => 'HarbormasterPlanOrderController', 69 65 'disable/(?P<id>\d+)/' => 'HarbormasterPlanDisableController', 66 + 'run/(?P<id>\d+)/' => 'HarbormasterPlanRunController', 70 67 '(?P<id>\d+)/' => 'HarbormasterPlanViewController', 71 68 ), 72 69 ),
-74
src/applications/harbormaster/controller/HarbormasterBuildableApplyController.php
··· 1 - <?php 2 - 3 - final class HarbormasterBuildableApplyController 4 - extends HarbormasterController { 5 - 6 - private $id; 7 - 8 - public function willProcessRequest(array $data) { 9 - $this->id = $data['id']; 10 - } 11 - 12 - public function processRequest() { 13 - $request = $this->getRequest(); 14 - $viewer = $request->getUser(); 15 - 16 - $id = $this->id; 17 - 18 - $buildable = id(new HarbormasterBuildableQuery()) 19 - ->setViewer($viewer) 20 - ->withIDs(array($id)) 21 - ->executeOne(); 22 - if ($buildable === null) { 23 - throw new Exception("Buildable not found!"); 24 - } 25 - 26 - $buildable_uri = '/B'.$buildable->getID(); 27 - 28 - if ($request->isDialogFormPost()) { 29 - $plan = id(new HarbormasterBuildPlanQuery()) 30 - ->setViewer($viewer) 31 - ->withIDs(array($request->getInt('build-plan'))) 32 - ->executeOne(); 33 - 34 - HarbormasterBuildable::applyBuildPlans( 35 - $buildable->getBuildablePHID(), 36 - $buildable->getContainerPHID(), 37 - array($plan->getPHID())); 38 - 39 - return id(new AphrontRedirectResponse())->setURI($buildable_uri); 40 - } 41 - 42 - $plans = id(new HarbormasterBuildPlanQuery()) 43 - ->setViewer($viewer) 44 - ->execute(); 45 - 46 - $options = array(); 47 - foreach ($plans as $plan) { 48 - $options[$plan->getID()] = $plan->getName(); 49 - } 50 - 51 - // FIXME: I'd really like to use the dialog that "Edit Differential 52 - // Revisions" uses, but that code is quite hard-coded for the particular 53 - // uses, so for now we just give a single dropdown. 54 - 55 - $dialog = new AphrontDialogView(); 56 - $dialog->setTitle(pht('Apply which plan?')) 57 - ->setUser($viewer) 58 - ->addSubmitButton(pht('Apply')) 59 - ->addCancelButton($buildable_uri); 60 - $dialog->appendChild( 61 - phutil_tag( 62 - 'p', 63 - array(), 64 - pht( 65 - 'Select what build plan you want to apply to this buildable:'))); 66 - $dialog->appendChild( 67 - id(new AphrontFormSelectControl()) 68 - ->setUser($viewer) 69 - ->setName('build-plan') 70 - ->setOptions($options)); 71 - return id(new AphrontDialogResponse())->setDialog($dialog); 72 - } 73 - 74 - }
-140
src/applications/harbormaster/controller/HarbormasterBuildableEditController.php
··· 1 - <?php 2 - 3 - final class HarbormasterBuildableEditController 4 - extends HarbormasterController { 5 - 6 - private $id; 7 - 8 - public function willProcessRequest(array $data) { 9 - $this->id = idx($data, 'id'); 10 - } 11 - 12 - public function processRequest() { 13 - $request = $this->getRequest(); 14 - $viewer = $request->getUser(); 15 - 16 - $this->requireApplicationCapability( 17 - HarbormasterCapabilityManagePlans::CAPABILITY); 18 - 19 - if ($this->id) { 20 - $buildable = id(new HarbormasterBuildableQuery()) 21 - ->setViewer($viewer) 22 - ->withIDs(array($this->id)) 23 - ->executeOne(); 24 - if (!$buildable) { 25 - return new Aphront404Response(); 26 - } 27 - } else { 28 - $buildable = HarbormasterBuildable::initializeNewBuildable($viewer); 29 - } 30 - 31 - $e_name = true; 32 - $v_name = null; 33 - 34 - $errors = array(); 35 - if ($request->isFormPost()) { 36 - $v_name = $request->getStr('buildablePHID'); 37 - 38 - if ($v_name) { 39 - $object = id(new PhabricatorObjectQuery()) 40 - ->setViewer($viewer) 41 - ->withNames(array($v_name)) 42 - ->executeOne(); 43 - 44 - if ($object instanceof DifferentialRevision) { 45 - $revision = $object; 46 - $object = $object->loadActiveDiff(); 47 - $buildable 48 - ->setBuildablePHID($object->getPHID()) 49 - ->setContainerPHID($revision->getPHID()); 50 - } else if ($object instanceof PhabricatorRepositoryCommit) { 51 - $buildable 52 - ->setBuildablePHID($object->getPHID()) 53 - ->setContainerPHID($object->getRepository()->getPHID()); 54 - } else { 55 - $e_name = pht('Invalid'); 56 - $errors[] = pht('Enter the name of a revision or commit.'); 57 - } 58 - } else { 59 - $e_name = pht('Required'); 60 - $errors[] = pht('You must choose a revision or commit to build.'); 61 - } 62 - 63 - if (!$errors) { 64 - $buildable->save(); 65 - 66 - $buildable_uri = '/B'.$buildable->getID(); 67 - return id(new AphrontRedirectResponse())->setURI($buildable_uri); 68 - } 69 - } 70 - 71 - if ($errors) { 72 - $errors = id(new AphrontErrorView())->setErrors($errors); 73 - } 74 - 75 - $is_new = (!$buildable->getID()); 76 - if ($is_new) { 77 - $title = pht('New Buildable'); 78 - $cancel_uri = $this->getApplicationURI(); 79 - $save_button = pht('Create Buildable'); 80 - } else { 81 - $id = $buildable->getID(); 82 - 83 - $title = pht('Edit Buildable'); 84 - $cancel_uri = "/B{$id}"; 85 - $save_button = pht('Save Buildable'); 86 - } 87 - 88 - $form = id(new AphrontFormView()) 89 - ->setUser($viewer); 90 - 91 - if ($is_new) { 92 - $form 93 - ->appendRemarkupInstructions( 94 - pht( 95 - 'Enter the name of a commit or revision, like `rX123456789` '. 96 - 'or `D123`.')) 97 - ->appendChild( 98 - id(new AphrontFormTextControl()) 99 - ->setLabel('Buildable Name') 100 - ->setName('buildablePHID') 101 - ->setError($e_name) 102 - ->setValue($v_name)); 103 - } else { 104 - $form->appendChild( 105 - id(new AphrontFormMarkupControl()) 106 - ->setLabel(pht('Buildable')) 107 - ->setValue($buildable->getBuildableHandle()->renderLink())); 108 - } 109 - 110 - $form->appendChild( 111 - id(new AphrontFormSubmitControl()) 112 - ->setValue($save_button) 113 - ->addCancelButton($cancel_uri)); 114 - 115 - $box = id(new PHUIObjectBoxView()) 116 - ->setHeaderText($title) 117 - ->setFormError($errors) 118 - ->setForm($form); 119 - 120 - $crumbs = $this->buildApplicationCrumbs(); 121 - if ($is_new) { 122 - $crumbs->addTextCrumb(pht('New Buildable')); 123 - } else { 124 - $id = $buildable->getID(); 125 - $crumbs->addTextCrumb("B{$id}", "/B{$id}"); 126 - $crumbs->addTextCrumb(pht('Edit')); 127 - } 128 - 129 - return $this->buildApplicationPage( 130 - array( 131 - $crumbs, 132 - $box, 133 - ), 134 - array( 135 - 'title' => $title, 136 - 'device' => true, 137 - )); 138 - } 139 - 140 - }
+7 -2
src/applications/harbormaster/controller/HarbormasterBuildableListController.php
··· 44 44 $item->setHref("/B{$id}"); 45 45 } 46 46 47 + if ($buildable->getIsManualBuildable()) { 48 + $item->addIcon('wrench-grey', pht('Manual')); 49 + } 50 + 47 51 $list->addItem($item); 52 + 53 + 48 54 49 55 // TODO: This is proof-of-concept for getting meaningful status 50 56 // information into this list, and should get an improvement pass ··· 86 92 $nav->addFilter('new/', pht('New Build Plan')); 87 93 } 88 94 89 - $nav->addLabel('Utilities'); 90 - $nav->addFilter('buildable/edit/', pht('New Manual Build')); 95 + $nav->addLabel(pht('Build Plans')); 91 96 $nav->addFilter('plan/', pht('Manage Build Plans')); 92 97 93 98 $nav->selectFilter(null);
+6 -9
src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
··· 118 118 ->setObject($buildable) 119 119 ->setObjectURI("/B{$id}"); 120 120 121 - $apply_uri = $this->getApplicationURI('/buildable/apply/'.$id.'/'); 122 - 123 - $list->addAction( 124 - id(new PhabricatorActionView()) 125 - ->setName(pht('Apply Build Plan')) 126 - ->setIcon('edit') 127 - ->setHref($apply_uri) 128 - ->setWorkflow(true)); 129 - 130 121 return $list; 131 122 } 132 123 ··· 152 143 pht('Container'), 153 144 $buildable->getContainerHandle()->renderLink()); 154 145 } 146 + 147 + $properties->addProperty( 148 + pht('Origin'), 149 + $buildable->getIsManualBuildable() 150 + ? pht('Manual Buildable') 151 + : pht('Automatic Buildable')); 155 152 156 153 } 157 154
+103
src/applications/harbormaster/controller/HarbormasterPlanRunController.php
··· 1 + <?php 2 + 3 + final class HarbormasterPlanRunController 4 + extends HarbormasterController { 5 + 6 + private $id; 7 + 8 + public function willProcessRequest(array $data) { 9 + $this->id = $data['id']; 10 + } 11 + 12 + public function processRequest() { 13 + $request = $this->getRequest(); 14 + $viewer = $request->getUser(); 15 + 16 + $this->requireApplicationCapability( 17 + HarbormasterCapabilityManagePlans::CAPABILITY); 18 + 19 + $plan_id = $this->id; 20 + $plan = id(new HarbormasterBuildPlanQuery()) 21 + ->setViewer($viewer) 22 + ->withIDs(array($plan_id)) 23 + ->executeOne(); 24 + if (!$plan) { 25 + return new Aphront404Response(); 26 + } 27 + 28 + $e_name = true; 29 + $v_name = null; 30 + 31 + $errors = array(); 32 + if ($request->isFormPost()) { 33 + $buildable = HarbormasterBuildable::initializeNewBuildable($viewer) 34 + ->setIsManualBuildable(true); 35 + 36 + $v_name = $request->getStr('buildablePHID'); 37 + 38 + if ($v_name) { 39 + $object = id(new PhabricatorObjectQuery()) 40 + ->setViewer($viewer) 41 + ->withNames(array($v_name)) 42 + ->executeOne(); 43 + 44 + if ($object instanceof DifferentialRevision) { 45 + $revision = $object; 46 + $object = $object->loadActiveDiff(); 47 + $buildable 48 + ->setBuildablePHID($object->getPHID()) 49 + ->setContainerPHID($revision->getPHID()); 50 + } else if ($object instanceof PhabricatorRepositoryCommit) { 51 + $buildable 52 + ->setBuildablePHID($object->getPHID()) 53 + ->setContainerPHID($object->getRepository()->getPHID()); 54 + } else { 55 + $e_name = pht('Invalid'); 56 + $errors[] = pht('Enter the name of a revision or commit.'); 57 + } 58 + } else { 59 + $e_name = pht('Required'); 60 + $errors[] = pht('You must choose a revision or commit to build.'); 61 + } 62 + 63 + if (!$errors) { 64 + $buildable->save(); 65 + 66 + $buildable_uri = '/B'.$buildable->getID(); 67 + return id(new AphrontRedirectResponse())->setURI($buildable_uri); 68 + } 69 + } 70 + 71 + if ($errors) { 72 + $errors = id(new AphrontErrorView())->setErrors($errors); 73 + } 74 + 75 + $title = pht('Run Build Plan Manually'); 76 + $cancel_uri = $this->getApplicationURI("plan/{$plan_id}/"); 77 + $save_button = pht('Run Plan Manually'); 78 + 79 + $form = id(new PHUIFormLayoutView()) 80 + ->setUser($viewer) 81 + ->appendRemarkupInstructions( 82 + pht( 83 + "Enter the name of a commit or revision to run this plan on.\n\n". 84 + "For example: `rX123456` or `D123`")) 85 + ->appendChild( 86 + id(new AphrontFormTextControl()) 87 + ->setLabel('Buildable Name') 88 + ->setName('buildablePHID') 89 + ->setError($e_name) 90 + ->setValue($v_name)); 91 + 92 + $dialog = id(new AphrontDialogView()) 93 + ->setWidth(AphrontDialogView::WIDTH_FORM) 94 + ->setUser($viewer) 95 + ->setTitle($title) 96 + ->appendChild($form) 97 + ->addCancelButton($cancel_uri) 98 + ->addSubmitButton($save_button); 99 + 100 + return id(new AphrontDialogResponse())->setDialog($dialog); 101 + } 102 + 103 + }
+9 -1
src/applications/harbormaster/controller/HarbormasterPlanViewController.php
··· 200 200 id(new PhabricatorActionView()) 201 201 ->setName(pht('Add Build Step')) 202 202 ->setHref($this->getApplicationURI("step/add/{$id}/")) 203 - ->setWorkflow($can_edit) 203 + ->setWorkflow(true) 204 204 ->setDisabled(!$can_edit) 205 205 ->setIcon('new')); 206 + 207 + $list->addAction( 208 + id(new PhabricatorActionView()) 209 + ->setName(pht('Run Plan Manually')) 210 + ->setHref($this->getApplicationURI("plan/run/{$id}/")) 211 + ->setWorkflow(true) 212 + ->setDisabled(!$can_edit) 213 + ->setIcon('start-sandcastle')); 206 214 207 215 return $list; 208 216 }
+13
src/applications/harbormaster/query/HarbormasterBuildableQuery.php
··· 7 7 private $phids; 8 8 private $buildablePHIDs; 9 9 private $containerPHIDs; 10 + private $manualBuildables; 10 11 11 12 private $needContainerObjects; 12 13 private $needContainerHandles; ··· 30 31 31 32 public function withContainerPHIDs(array $container_phids) { 32 33 $this->containerPHIDs = $container_phids; 34 + return $this; 35 + } 36 + 37 + public function withManualBuildables($manual) { 38 + $this->manualBuildables = $manual; 33 39 return $this; 34 40 } 35 41 ··· 195 201 $conn_r, 196 202 'containerPHID in (%Ls)', 197 203 $this->containerPHIDs); 204 + } 205 + 206 + if ($this->manualBuildables !== null) { 207 + $where[] = qsprintf( 208 + $conn_r, 209 + 'isManualBuildable = %d', 210 + (int)$this->manualBuildables); 198 211 } 199 212 200 213 $where[] = $this->buildPagingClause($conn_r);
+20 -1
src/applications/harbormaster/query/HarbormasterBuildableSearchEngine.php
··· 42 42 $buildable_phids = array_merge($commits, $diffs); 43 43 $saved->setParameter('buildablePHIDs', $buildable_phids); 44 44 45 + $saved->setParameter( 46 + 'manual', 47 + $this->readBoolFromRequest($request, 'manual')); 45 48 46 49 return $saved; 47 50 } ··· 61 64 62 65 if ($buildable_phids) { 63 66 $query->withBuildablePHIDs($buildable_phids); 67 + } 68 + 69 + $manual = $saved->getParameter('manual'); 70 + if ($manual !== null) { 71 + $query->withManualBuildables($manual); 64 72 } 65 73 66 74 return $query; ··· 126 134 id(new AphrontFormTextControl()) 127 135 ->setLabel(pht('Commits')) 128 136 ->setName('commits') 129 - ->setValue(implode(', ', $commit_names))); 137 + ->setValue(implode(', ', $commit_names))) 138 + ->appendChild( 139 + id(new AphrontFormSelectControl()) 140 + ->setLabel(pht('Origin')) 141 + ->setName('manual') 142 + ->setValue($this->getBoolFromQuery($saved_query, 'manual')) 143 + ->setOptions( 144 + array( 145 + '' => pht('(All Origins)'), 146 + 'true' => pht('Manual Buildables'), 147 + 'false' => pht('Automatic Buildables'), 148 + ))); 130 149 } 131 150 132 151 protected function getURI($path) {
+1
src/applications/harbormaster/step/WaitForPreviousBuildStepImplementation.php
··· 102 102 $buildables = id(new HarbormasterBuildableQuery()) 103 103 ->setViewer(PhabricatorUser::getOmnipotentUser()) 104 104 ->withBuildablePHIDs($build_objects) 105 + ->withManualBuildables(false) 105 106 ->execute(); 106 107 $buildable_phids = mpull($buildables, 'getPHID'); 107 108
+3
src/applications/harbormaster/storage/HarbormasterBuildable.php
··· 7 7 protected $containerPHID; 8 8 protected $buildStatus; 9 9 protected $buildableStatus; 10 + protected $isManualBuildable; 10 11 11 12 private $buildableObject = self::ATTACHABLE; 12 13 private $containerObject = self::ATTACHABLE; ··· 18 19 19 20 public static function initializeNewBuildable(PhabricatorUser $actor) { 20 21 return id(new HarbormasterBuildable()) 22 + ->setIsManualBuildable(0) 21 23 ->setBuildStatus(self::STATUS_WHATEVER) 22 24 ->setBuildableStatus(self::STATUS_WHATEVER); 23 25 } ··· 34 36 $buildable = id(new HarbormasterBuildableQuery()) 35 37 ->setViewer($actor) 36 38 ->withBuildablePHIDs(array($buildable_object_phid)) 39 + ->withManualBuildables(false) 37 40 ->setLimit(1) 38 41 ->executeOne(); 39 42 if ($buildable) {
+4
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 1848 1848 'type' => 'sql', 1849 1849 'name' => $this->getPatchPath('20131219.pxdrop.sql'), 1850 1850 ), 1851 + '20131224.harbormanual.sql' => array( 1852 + 'type' => 'sql', 1853 + 'name' => $this->getPatchPath('20131224.harbormanual.sql'), 1854 + ), 1851 1855 ); 1852 1856 } 1853 1857 }