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

Implement explicit build step ordering in Harbormaster

Summary: This implements support for explicitly marking the sequence of build steps. Users can now drag and re-order build steps in plans, and artifact dependencies are re-calculated so that if you move "Run Command" before "Lease Host", the "Run Command" step has it's artifact setting cleared and thus the step becomes invalid.

Test Plan: Re-ordered build steps and observed dependencies being correctly recalculated.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley

CC: Korvin, epriestley, aran

Maniphest Tasks: T1049

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

+221 -5
+2
resources/sql/patches/20131205.buildsteporder.sql
··· 1 + ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildstep 2 + ADD COLUMN sequence INT UNSIGNED NOT NULL;
+41
resources/sql/patches/20131205.buildstepordermig.php
··· 1 + <?php 2 + 3 + $table = new HarbormasterBuildPlan(); 4 + $conn_w = $table->establishConnection('w'); 5 + $viewer = PhabricatorUser::getOmnipotentUser(); 6 + 7 + // Since HarbormasterBuildStepQuery has been updated to handle the 8 + // correct order, we can't use the built in database access. 9 + 10 + foreach (new LiskMigrationIterator($table) as $plan) { 11 + $planname = $plan->getName(); 12 + echo "Migrating steps in {$planname}...\n"; 13 + 14 + $rows = queryfx_all( 15 + $conn_w, 16 + "SELECT id, sequence FROM harbormaster_buildstep ". 17 + "WHERE buildPlanPHID = %s ". 18 + "ORDER BY id ASC", 19 + $plan->getPHID()); 20 + 21 + $sequence = 1; 22 + foreach ($rows as $row) { 23 + $id = $row['id']; 24 + $existing = $row['sequence']; 25 + if ($existing != 0) { 26 + echo " - {$id} (already migrated)...\n"; 27 + continue; 28 + } 29 + echo " - {$id} to position {$sequence}...\n"; 30 + queryfx( 31 + $conn_w, 32 + "UPDATE harbormaster_buildstep ". 33 + "SET sequence = %d ". 34 + "WHERE id = %d", 35 + $sequence, 36 + $id); 37 + $sequence++; 38 + } 39 + } 40 + 41 + echo "Done.\n";
+2
src/__phutil_library_map__.php
··· 723 723 'HarbormasterPlanController' => 'applications/harbormaster/controller/HarbormasterPlanController.php', 724 724 'HarbormasterPlanEditController' => 'applications/harbormaster/controller/HarbormasterPlanEditController.php', 725 725 'HarbormasterPlanListController' => 'applications/harbormaster/controller/HarbormasterPlanListController.php', 726 + 'HarbormasterPlanOrderController' => 'applications/harbormaster/controller/HarbormasterPlanOrderController.php', 726 727 'HarbormasterPlanViewController' => 'applications/harbormaster/controller/HarbormasterPlanViewController.php', 727 728 'HarbormasterRemarkupRule' => 'applications/harbormaster/remarkup/HarbormasterRemarkupRule.php', 728 729 'HarbormasterScratchTable' => 'applications/harbormaster/storage/HarbormasterScratchTable.php', ··· 3111 3112 0 => 'HarbormasterPlanController', 3112 3113 1 => 'PhabricatorApplicationSearchResultsControllerInterface', 3113 3114 ), 3115 + 'HarbormasterPlanOrderController' => 'HarbormasterController', 3114 3116 'HarbormasterPlanViewController' => 'HarbormasterPlanController', 3115 3117 'HarbormasterRemarkupRule' => 'PhabricatorRemarkupRuleObject', 3116 3118 'HarbormasterScratchTable' => 'HarbormasterDAO',
+1
src/applications/harbormaster/application/PhabricatorApplicationHarbormaster.php
··· 65 65 '(?:query/(?P<queryKey>[^/]+)/)?' 66 66 => 'HarbormasterPlanListController', 67 67 'edit/(?:(?P<id>\d+)/)?' => 'HarbormasterPlanEditController', 68 + 'order/(?:(?P<id>\d+)/)?' => 'HarbormasterPlanOrderController', 68 69 '(?P<id>\d+)/' => 'HarbormasterPlanViewController', 69 70 ), 70 71 ),
+84
src/applications/harbormaster/controller/HarbormasterPlanOrderController.php
··· 1 + <?php 2 + 3 + /** 4 + * @group search 5 + */ 6 + final class HarbormasterPlanOrderController extends HarbormasterController { 7 + 8 + private $id; 9 + 10 + public function willProcessRequest(array $data) { 11 + $this->id = idx($data, 'id'); 12 + } 13 + 14 + public function processRequest() { 15 + $request = $this->getRequest(); 16 + $user = $request->getUser(); 17 + 18 + $request->validateCSRF(); 19 + 20 + $this->requireApplicationCapability( 21 + HarbormasterCapabilityManagePlans::CAPABILITY); 22 + 23 + $plan = id(new HarbormasterBuildPlanQuery()) 24 + ->setViewer($user) 25 + ->withIDs(array($this->id)) 26 + ->executeOne(); 27 + if (!$plan) { 28 + return new Aphront404Response(); 29 + } 30 + 31 + // Load all steps. 32 + $order = $request->getStrList('order'); 33 + $steps = id(new HarbormasterBuildStepQuery()) 34 + ->setViewer($user) 35 + ->withIDs($order) 36 + ->execute(); 37 + $steps = array_select_keys($steps, $order); 38 + $reordered_steps = array(); 39 + 40 + // Apply sequences. 41 + $sequence = 1; 42 + foreach ($steps as $step) { 43 + $step->setSequence($sequence++); 44 + $step->save(); 45 + 46 + $reordered_steps[] = $step; 47 + } 48 + 49 + // We must ensure that steps with artifacts become invalid if they are 50 + // placed before the steps that produce them. 51 + foreach ($reordered_steps as $step) { 52 + $implementation = $step->getStepImplementation(); 53 + $settings = $implementation->getSettings(); 54 + foreach ($implementation->getSettingDefinitions() as $name => $opt) { 55 + switch ($opt['type']) { 56 + case BuildStepImplementation::SETTING_TYPE_ARTIFACT: 57 + $value = $settings[$name]; 58 + $filter = $opt['artifact_type']; 59 + $available_artifacts = 60 + BuildStepImplementation::getAvailableArtifacts( 61 + $plan, 62 + $reordered_steps, 63 + $step, 64 + $filter); 65 + $artifact_found = false; 66 + foreach ($available_artifacts as $key => $type) { 67 + if ($key === $value) { 68 + $artifact_found = true; 69 + } 70 + } 71 + if (!$artifact_found) { 72 + $step->setDetail($name, null); 73 + } 74 + break; 75 + } 76 + $step->save(); 77 + } 78 + } 79 + 80 + // Force the page to re-render. 81 + return id(new AphrontRedirectResponse()); 82 + } 83 + 84 + }
+16 -1
src/applications/harbormaster/controller/HarbormasterPlanViewController.php
··· 74 74 $request = $this->getRequest(); 75 75 $viewer = $request->getUser(); 76 76 77 + $list_id = celerity_generate_unique_node_id(); 78 + 77 79 $steps = id(new HarbormasterBuildStepQuery()) 78 80 ->setViewer($viewer) 79 81 ->withBuildPlanPHIDs(array($plan->getPHID())) ··· 84 86 85 87 $i = 1; 86 88 $step_list = id(new PHUIObjectItemListView()) 87 - ->setUser($viewer); 89 + ->setUser($viewer) 90 + ->setID($list_id); 91 + Javelin::initBehavior( 92 + 'harbormaster-reorder-steps', 93 + array( 94 + 'listID' => $list_id, 95 + 'orderURI' => '/harbormaster/plan/order/'.$plan->getID().'/', 96 + )); 88 97 foreach ($steps as $step) { 89 98 $implementation = null; 90 99 try { ··· 136 145 ->setName(pht("Delete")) 137 146 ->setHref( 138 147 $this->getApplicationURI("step/delete/".$step->getID()."/"))); 148 + $item->setGrippable(true); 149 + $item->addSigil('build-step'); 150 + $item->setMetadata( 151 + array( 152 + 'stepID' => $step->getID(), 153 + )); 139 154 } 140 155 141 156 $step_list->addItem($item);
+3
src/applications/harbormaster/controller/HarbormasterStepAddController.php
··· 36 36 return $this->createDialog($implementations); 37 37 } 38 38 39 + $steps = $plan->loadOrderedBuildSteps(); 40 + 39 41 $step = new HarbormasterBuildStep(); 40 42 $step->setBuildPlanPHID($plan->getPHID()); 41 43 $step->setClassName($class); 42 44 $step->setDetails(array()); 45 + $step->setSequence(count($steps) + 1); 43 46 $step->save(); 44 47 45 48 $edit_uri = $this->getApplicationURI("step/edit/".$step->getID()."/");
+1 -1
src/applications/harbormaster/controller/HarbormasterStepEditController.php
··· 94 94 case BuildStepImplementation::SETTING_TYPE_ARTIFACT: 95 95 $filter = $opt['artifact_type']; 96 96 $available_artifacts = 97 - BuildStepImplementation::getAvailableArtifacts( 97 + BuildStepImplementation::loadAvailableArtifacts( 98 98 $plan, 99 99 $step, 100 100 $filter);
+1 -1
src/applications/harbormaster/query/HarbormasterBuildStepQuery.php
··· 23 23 } 24 24 25 25 public function getPagingColumn() { 26 - return 'id'; 26 + return 'sequence'; 27 27 } 28 28 29 29 public function getReversePaging() {
+18 -2
src/applications/harbormaster/step/BuildStepImplementation.php
··· 117 117 /** 118 118 * Returns a list of all artifacts made available by previous build steps. 119 119 */ 120 - public static function getAvailableArtifacts( 120 + public static function loadAvailableArtifacts( 121 121 HarbormasterBuildPlan $build_plan, 122 122 HarbormasterBuildStep $current_build_step, 123 123 $artifact_type) { 124 124 125 125 $build_steps = $build_plan->loadOrderedBuildSteps(); 126 126 127 + return self::getAvailableArtifacts( 128 + $build_plan, 129 + $build_steps, 130 + $current_build_step, 131 + $artifact_type); 132 + } 133 + 134 + /** 135 + * Returns a list of all artifacts made available by previous build steps. 136 + */ 137 + public static function getAvailableArtifacts( 138 + HarbormasterBuildPlan $build_plan, 139 + array $build_steps, 140 + HarbormasterBuildStep $current_build_step, 141 + $artifact_type) { 142 + 127 143 $previous_implementations = array(); 128 144 foreach ($build_steps as $build_step) { 129 145 if ($build_step->getPHID() === $current_build_step->getPHID()) { ··· 136 152 $artifacts = array(); 137 153 foreach ($artifact_arrays as $array) { 138 154 foreach ($array as $name => $type) { 139 - if ($type !== $artifact_type) { 155 + if ($type !== $artifact_type && $artifact_type !== null) { 140 156 continue; 141 157 } 142 158 $artifacts[$name] = $type;
+2
src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php
··· 6 6 protected $buildPlanPHID; 7 7 protected $className; 8 8 protected $details = array(); 9 + protected $sequence; 9 10 10 11 private $buildPlan = self::ATTACHABLE; 11 12 ··· 60 61 $implementation->loadSettings($this); 61 62 return $implementation; 62 63 } 64 + 63 65 64 66 /* -( PhabricatorPolicyInterface )----------------------------------------- */ 65 67
+8
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 1804 1804 'type' => 'sql', 1805 1805 'name' => $this->getPatchPath('20131204.pushlog.sql'), 1806 1806 ), 1807 + '20131205.buildsteporder.sql' => array( 1808 + 'type' => 'sql', 1809 + 'name' => $this->getPatchPath('20131205.buildsteporder.sql'), 1810 + ), 1811 + '20131205.buildstepordermig.php' => array( 1812 + 'type' => 'php', 1813 + 'name' => $this->getPatchPath('20131205.buildstepordermig.php'), 1814 + ), 1807 1815 ); 1808 1816 } 1809 1817 }
+42
webroot/rsrc/js/application/harbormaster/behavior-reorder-steps.js
··· 1 + /** 2 + * @provides javelin-behavior-harbormaster-reorder-steps 3 + * @requires javelin-behavior 4 + * javelin-stratcom 5 + * javelin-workflow 6 + * javelin-dom 7 + * phabricator-draggable-list 8 + */ 9 + 10 + JX.behavior('harbormaster-reorder-steps', function(config) { 11 + 12 + var root = JX.$(config.listID); 13 + 14 + var list = new JX.DraggableList('build-step', root) 15 + .setFindItemsHandler(function() { 16 + return JX.DOM.scry(root, 'li', 'build-step'); 17 + }); 18 + 19 + list.listen('didDrop', function(node, after) { 20 + var nodes = list.findItems(); 21 + var order = []; 22 + var key; 23 + for (var ii = 0; ii < nodes.length; ii++) { 24 + key = JX.Stratcom.getData(nodes[ii]).stepID; 25 + if (key) { 26 + order.push(key); 27 + } 28 + } 29 + 30 + list.lock(); 31 + JX.DOM.alterClass(node, 'drag-sending', true); 32 + 33 + new JX.Workflow(config.orderURI, {order: order.join()}) 34 + .setHandler(function(e) { 35 + JX.DOM.alterClass(node, 'drag-sending', false); 36 + list.unlock(); 37 + }) 38 + .start(); 39 + }); 40 + 41 + }); 42 +