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

Allow Harbormaster build plans to request additional working copies

Summary:
Ref T9123. To run upstream builds in Harbormaster/Drydock, we need to be able to check out `libphutil`, `arcanist` and `phabricator` next to one another.

This adds an "Also Clone: ..." field to Harbormaster working copy build steps so I can type all three repos into it and get a proper clone with everything we need.

This is somewhat upstream-centric and a bit narrow, but I don't think it's totally unreasonable, and most of the underlying stuff is relatively general.

This adds some more typechecking and improves data/type handling for custom fields, too. In particular, it prevents users from entering an invalid/restricted value in a field (for example, you can't "Also Clone" a repository you don't have permission to see).

Test Plan: Restarted build, got a Drydock resource with multiple repositories in it.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9123

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

+129 -16
+8 -6
src/applications/harbormaster/controller/HarbormasterStepEditController.php
··· 72 72 73 73 $e_name = true; 74 74 $v_name = $step->getName(); 75 - $e_description = true; 75 + $e_description = null; 76 76 $v_description = $step->getDescription(); 77 - $e_depends_on = true; 77 + $e_depends_on = null; 78 78 $v_depends_on = $step->getDetail('dependsOn', array()); 79 79 80 80 $errors = array(); ··· 82 82 if ($request->isFormPost()) { 83 83 $e_name = null; 84 84 $v_name = $request->getStr('name'); 85 - $e_description = null; 86 85 $v_description = $request->getStr('description'); 87 - $e_depends_on = null; 88 86 $v_depends_on = $request->getArr('dependsOn'); 89 87 90 88 $xactions = $field_list->buildFieldTransactionsFromRequest( ··· 139 137 ->setError($e_name) 140 138 ->setValue($v_name)); 141 139 140 + $form->appendChild(id(new AphrontFormDividerControl())); 141 + 142 + $field_list->appendFieldsToForm($form); 143 + 144 + $form->appendChild(id(new AphrontFormDividerControl())); 145 + 142 146 $form 143 147 ->appendControl( 144 148 id(new AphrontFormTokenizerControl()) ··· 151 155 ->setLabel(pht('Depends On')) 152 156 ->setError($e_depends_on) 153 157 ->setValue($v_depends_on)); 154 - 155 - $field_list->appendFieldsToForm($form); 156 158 157 159 $form 158 160 ->appendChild(
+4
src/applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php
··· 72 72 return; 73 73 } 74 74 75 + public function getBuildTargetFieldValue() { 76 + return $this->getProxy()->getFieldValue(); 77 + } 78 + 75 79 }
+5 -1
src/applications/harbormaster/customfield/HarbormasterBuildStepCustomField.php
··· 1 1 <?php 2 2 3 3 abstract class HarbormasterBuildStepCustomField 4 - extends PhabricatorCustomField {} 4 + extends PhabricatorCustomField { 5 + 6 + abstract public function getBuildTargetFieldValue(); 7 + 8 + }
+31 -8
src/applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php
··· 100 100 'type' => 'text', 101 101 'required' => true, 102 102 ), 103 + 'repositoryPHIDs' => array( 104 + 'name' => pht('Also Clone'), 105 + 'type' => 'datasource', 106 + 'datasource.class' => 'DiffusionRepositoryDatasource', 107 + ), 103 108 ); 104 109 } 105 110 ··· 108 113 $variables = $build_target->getVariables(); 109 114 110 115 $repository_phid = idx($variables, 'repository.phid'); 116 + $also_phids = $build_target->getFieldValue('repositoryPHIDs'); 111 117 112 - $repository = id(new PhabricatorRepositoryQuery()) 118 + $all_phids = $also_phids; 119 + $all_phids[] = $repository_phid; 120 + 121 + $repositories = id(new PhabricatorRepositoryQuery()) 113 122 ->setViewer($viewer) 114 - ->withPHIDs(array($repository_phid)) 115 - ->executeOne(); 116 - if (!$repository) { 117 - throw new PhabricatorWorkerPermanentFailureException( 118 - pht( 119 - 'Unable to load repository with PHID "%s".', 120 - $repository_phid)); 123 + ->withPHIDs($all_phids) 124 + ->execute(); 125 + $repositories = mpull($repositories, null, 'getPHID'); 126 + 127 + foreach ($all_phids as $phid) { 128 + if (empty($repositories[$phid])) { 129 + throw new PhabricatorWorkerPermanentFailureException( 130 + pht( 131 + 'Unable to load repository with PHID "%s".', 132 + $phid)); 133 + } 121 134 } 122 135 123 136 $commit = idx($variables, 'repository.commit'); 124 137 125 138 $map = array(); 139 + 140 + foreach ($also_phids as $also_phid) { 141 + $also_repo = $repositories[$also_phid]; 142 + $map[$also_repo->getCloneName()] = array( 143 + 'phid' => $also_repo->getPHID(), 144 + 'branch' => 'master', 145 + ); 146 + } 147 + 148 + $repository = $repositories[$repository_phid]; 126 149 127 150 $directory = $repository->getCloneName(); 128 151 $map[$directory] = array(
+22
src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
··· 263 263 return $log; 264 264 } 265 265 266 + public function getFieldValue($key) { 267 + $field_list = PhabricatorCustomField::getObjectFields( 268 + $this->getBuildStep(), 269 + PhabricatorCustomField::ROLE_VIEW); 270 + 271 + $fields = $field_list->getFields(); 272 + $full_key = "std:harbormaster:core:{$key}"; 273 + 274 + $field = idx($fields, $full_key); 275 + if (!$field) { 276 + throw new Exception( 277 + pht( 278 + 'Unknown build step field "%s"!', 279 + $key)); 280 + } 281 + 282 + $field = clone $field; 283 + $field->setValueFromStorage($this->getDetail($key)); 284 + return $field->getBuildTargetFieldValue(); 285 + } 286 + 287 + 266 288 267 289 /* -( Status )------------------------------------------------------------- */ 268 290
+1
src/applications/harbormaster/worker/HarbormasterTargetWorker.php
··· 28 28 $target = id(new HarbormasterBuildTargetQuery()) 29 29 ->withIDs(array($id)) 30 30 ->setViewer($this->getViewer()) 31 + ->needBuildSteps(true) 31 32 ->executeOne(); 32 33 33 34 if (!$target) {
+55
src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php
··· 152 152 } 153 153 } 154 154 155 + public function validateApplicationTransactions( 156 + PhabricatorApplicationTransactionEditor $editor, 157 + $type, 158 + array $xactions) { 159 + 160 + $errors = parent::validateApplicationTransactions( 161 + $editor, 162 + $type, 163 + $xactions); 164 + 165 + // If the user is adding PHIDs, make sure the new PHIDs are valid and 166 + // visible to the actor. It's OK for a user to edit a field which includes 167 + // some invalid or restricted values, but they can't add new ones. 168 + 169 + foreach ($xactions as $xaction) { 170 + $old = phutil_json_decode($xaction->getOldValue()); 171 + $new = phutil_json_decode($xaction->getNewValue()); 172 + 173 + $add = array_diff($new, $old); 174 + 175 + if (!$add) { 176 + continue; 177 + } 178 + 179 + $objects = id(new PhabricatorObjectQuery()) 180 + ->setViewer($editor->getActor()) 181 + ->withPHIDs($add) 182 + ->execute(); 183 + $objects = mpull($objects, null, 'getPHID'); 184 + 185 + $invalid = array(); 186 + foreach ($add as $phid) { 187 + if (empty($objects[$phid])) { 188 + $invalid[] = $phid; 189 + } 190 + } 191 + 192 + if ($invalid) { 193 + $error = new PhabricatorApplicationTransactionValidationError( 194 + $type, 195 + pht('Invalid'), 196 + pht( 197 + 'Some of the selected PHIDs in field "%s" are invalid or '. 198 + 'restricted: %s.', 199 + $this->getFieldName(), 200 + implode(', ', $invalid)), 201 + $xaction); 202 + $errors[] = $error; 203 + $this->setFieldError(pht('Invalid')); 204 + } 205 + } 206 + 207 + return $errors; 208 + } 209 + 155 210 public function shouldAppearInHerald() { 156 211 return true; 157 212 }
+3 -1
src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php
··· 43 43 // fake it when doing manual CLI stuff. This makes sure CLI yields have 44 44 // their expires times set properly. 45 45 foreach ($tasks as $task) { 46 - $task->setServerTime(PhabricatorTime::getNow()); 46 + if ($task instanceof PhabricatorWorkerActiveTask) { 47 + $task->setServerTime(PhabricatorTime::getNow()); 48 + } 47 49 } 48 50 49 51 return $tasks;