@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
at recaptime-dev/main 188 lines 5.5 kB view raw
1<?php 2 3final class DrydockRepositoryOperationUpdateWorker 4 extends DrydockWorker { 5 6 protected function doWork() { 7 $operation_phid = $this->getTaskDataValue('operationPHID'); 8 9 $hash = PhabricatorHash::digestForIndex($operation_phid); 10 $lock_key = 'drydock.operation:'.$hash; 11 12 $lock = PhabricatorGlobalLock::newLock($lock_key) 13 ->lock(1); 14 15 try { 16 $operation = $this->loadOperation($operation_phid); 17 $this->handleUpdate($operation); 18 } catch (Exception $ex) { 19 $lock->unlock(); 20 throw $ex; 21 } 22 23 $lock->unlock(); 24 } 25 26 27 private function handleUpdate(DrydockRepositoryOperation $operation) { 28 $operation_state = $operation->getOperationState(); 29 30 switch ($operation_state) { 31 case DrydockRepositoryOperation::STATE_WAIT: 32 $operation 33 ->setOperationState(DrydockRepositoryOperation::STATE_WORK) 34 ->save(); 35 break; 36 case DrydockRepositoryOperation::STATE_WORK: 37 break; 38 case DrydockRepositoryOperation::STATE_DONE: 39 case DrydockRepositoryOperation::STATE_FAIL: 40 // No more processing for these requests. 41 return; 42 } 43 44 // TODO: We should probably check for other running operations with lower 45 // IDs and the same repository target and yield to them here? That is, 46 // enforce sequential evaluation of operations against the same target so 47 // that if you land "A" and then land "B", we always finish "A" first. 48 // For now, just let stuff happen in any order. We can't lease until 49 // we know we're good to move forward because we might deadlock if we do: 50 // we're waiting for another operation to complete, and that operation is 51 // waiting for a lease we're holding. 52 53 try { 54 $lease = $this->loadWorkingCopyLease($operation); 55 56 $interface = $lease->getInterface( 57 DrydockCommandInterface::INTERFACE_TYPE); 58 59 // No matter what happens here, destroy the lease away once we're done. 60 $lease->setReleaseOnDestruction(true); 61 62 $operation->attachWorkingCopyLease($lease); 63 64 $operation->logEvent(DrydockOperationWorkLogType::LOGCONST); 65 66 $operation->applyOperation($interface); 67 68 } catch (PhabricatorWorkerYieldException $ex) { 69 throw $ex; 70 } catch (Exception $ex) { 71 $operation 72 ->setOperationState(DrydockRepositoryOperation::STATE_FAIL) 73 ->save(); 74 throw $ex; 75 } 76 77 $operation 78 ->setOperationState(DrydockRepositoryOperation::STATE_DONE) 79 ->save(); 80 81 // TODO: Once we have sequencing, we could awaken the next operation 82 // against this target after finishing or failing. 83 } 84 85 private function loadWorkingCopyLease( 86 DrydockRepositoryOperation $operation) { 87 $viewer = $this->getViewer(); 88 89 // TODO: This is very similar to leasing in Harbormaster, maybe we can 90 // share some of the logic? 91 92 $working_copy = new DrydockWorkingCopyBlueprintImplementation(); 93 $working_copy_type = $working_copy->getType(); 94 95 $lease_phid = $operation->getProperty('exec.leasePHID'); 96 if ($lease_phid) { 97 $lease = id(new DrydockLeaseQuery()) 98 ->setViewer($viewer) 99 ->withPHIDs(array($lease_phid)) 100 ->executeOne(); 101 if (!$lease) { 102 throw new PhabricatorWorkerPermanentFailureException( 103 pht( 104 'Lease "%s" could not be loaded.', 105 $lease_phid)); 106 } 107 } else { 108 $repository = $operation->getRepository(); 109 110 $allowed_phids = $repository->getAutomationBlueprintPHIDs(); 111 $authorizing_phid = $repository->getPHID(); 112 113 $lease = DrydockLease::initializeNewLease() 114 ->setResourceType($working_copy_type) 115 ->setOwnerPHID($operation->getPHID()) 116 ->setAuthorizingPHID($authorizing_phid) 117 ->setAllowedBlueprintPHIDs($allowed_phids); 118 119 $map = $this->buildRepositoryMap($operation); 120 121 $lease->setAttribute('repositories.map', $map); 122 123 $task_id = $this->getCurrentWorkerTaskID(); 124 if ($task_id) { 125 $lease->setAwakenTaskIDs(array($task_id)); 126 } 127 128 $operation 129 ->setWorkingCopyLeasePHID($lease->getPHID()) 130 ->save(); 131 132 $lease->queueForActivation(); 133 } 134 135 if ($lease->isActivating()) { 136 throw new PhabricatorWorkerYieldException(15); 137 } 138 139 if (!$lease->isActive()) { 140 $vcs_error = $working_copy->getCommandError($lease); 141 if ($vcs_error) { 142 $operation 143 ->setCommandError($vcs_error) 144 ->save(); 145 } 146 147 throw new PhabricatorWorkerPermanentFailureException( 148 pht( 149 'Lease "%s" never activated.', 150 $lease->getPHID())); 151 } 152 153 return $lease; 154 } 155 156 private function buildRepositoryMap(DrydockRepositoryOperation $operation) { 157 $repository = $operation->getRepository(); 158 159 $target = $operation->getRepositoryTarget(); 160 list($type, $name) = explode(':', $target, 2); 161 switch ($type) { 162 case 'branch': 163 $spec = array( 164 'branch' => $name, 165 ); 166 break; 167 case 'none': 168 $spec = array(); 169 break; 170 default: 171 throw new Exception( 172 pht( 173 'Unknown repository operation target type "%s" (in target "%s").', 174 $type, 175 $target)); 176 } 177 178 $spec['merges'] = $operation->getWorkingCopyMerges(); 179 180 $map = array(); 181 $map[$repository->getCloneName()] = array( 182 'phid' => $repository->getPHID(), 183 'default' => true, 184 ) + $spec; 185 186 return $map; 187 } 188}