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

If the stars align, make "Land Revision" kind of work

Summary:
Ref T182. If 35 other things are configured completely correctly, make it remotely possible that this button may do something approximating the thing that the user wanted.

This primarily fleshes out the idea that "operations" (like landing, merging or cherry-picking) can have some beahavior, and when we run an operation we do whatever that behavior is instead of just running `git show`.

Broadly, this isn't too terrible because Drydock seems like it actually works properly for the most part (???!?!).

Test Plan: {F876431}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T182

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

+159 -10
+9 -1
src/applications/differential/controller/DifferentialRevisionOperationController.php
··· 10 10 $revision = id(new DifferentialRevisionQuery()) 11 11 ->withIDs(array($id)) 12 12 ->setViewer($viewer) 13 + ->needActiveDiffs(true) 13 14 ->executeOne(); 14 15 if (!$revision) { 15 16 return new Aphront404Response(); ··· 58 59 } 59 60 60 61 if ($request->isFormPost()) { 62 + // NOTE: The operation is locked to the current active diff, so if the 63 + // revision is updated before the operation applies nothing sneaky 64 + // occurs. 65 + 66 + $diff = $revision->getActiveDiff(); 67 + 61 68 $op = new DrydockLandRepositoryOperation(); 62 69 63 70 $operation = DrydockRepositoryOperation::initializeNewOperation($op) 64 71 ->setAuthorPHID($viewer->getPHID()) 65 72 ->setObjectPHID($revision->getPHID()) 66 73 ->setRepositoryPHID($repository->getPHID()) 67 - ->setRepositoryTarget('branch:master'); 74 + ->setRepositoryTarget('branch:master') 75 + ->setProperty('differential.diffPHID', $diff->getPHID()); 68 76 69 77 $operation->save(); 70 78 $operation->scheduleUpdate();
+8 -5
src/applications/differential/storage/DifferentialDiff.php
··· 447 447 $results['repository.vcs'] = $repo->getVersionControlSystem(); 448 448 $results['repository.uri'] = $repo->getPublicCloneURI(); 449 449 450 - // TODO: We're just hoping to get lucky. Instead, `arc` should store 451 - // where it sent changes and we should only provide staging details 452 - // if we reasonably believe they are accurate. 453 - $staging_ref = 'refs/tags/phabricator/diff/'.$this->getID(); 454 450 $results['repository.staging.uri'] = $repo->getStagingURI(); 455 - $results['repository.staging.ref'] = $staging_ref; 451 + $results['repository.staging.ref'] = $this->getStagingRef(); 456 452 } 457 453 } 458 454 ··· 478 474 'repository.staging.ref' => 479 475 pht('The ref name for this change in the staging repository.'), 480 476 ); 477 + } 478 + 479 + public function getStagingRef() { 480 + // TODO: We're just hoping to get lucky. Instead, `arc` should store 481 + // where it sent changes and we should only provide staging details 482 + // if we reasonably believe they are accurate. 483 + return 'refs/tags/phabricator/diff/'.$this->getID(); 481 484 } 482 485 483 486
+90
src/applications/drydock/operation/DrydockLandRepositoryOperation.php
··· 5 5 6 6 const OPCONST = 'land'; 7 7 8 + public function applyOperation( 9 + DrydockRepositoryOperation $operation, 10 + DrydockInterface $interface) { 11 + $viewer = $this->getViewer(); 12 + $repository = $operation->getRepository(); 13 + 14 + $cmd = array(); 15 + $arg = array(); 16 + 17 + $object = $operation->getObject(); 18 + if ($object instanceof DifferentialRevision) { 19 + $revision = $object; 20 + 21 + $diff_phid = $operation->getProperty('differential.diffPHID'); 22 + 23 + $diff = id(new DifferentialDiffQuery()) 24 + ->setViewer($viewer) 25 + ->withPHIDs(array($diff_phid)) 26 + ->executeOne(); 27 + if (!$diff) { 28 + throw new Exception( 29 + pht( 30 + 'Unable to load diff "%s".', 31 + $diff_phid)); 32 + } 33 + 34 + $diff_revid = $diff->getRevisionID(); 35 + $revision_id = $revision->getID(); 36 + if ($diff_revid != $revision_id) { 37 + throw new Exception( 38 + pht( 39 + 'Diff ("%s") has wrong revision ID ("%s", expected "%s").', 40 + $diff_phid, 41 + $diff_revid, 42 + $revision_id)); 43 + } 44 + 45 + $cmd[] = 'git fetch --no-tags -- %s +%s:%s'; 46 + $arg[] = $repository->getStagingURI(); 47 + $arg[] = $diff->getStagingRef(); 48 + $arg[] = $diff->getStagingRef(); 49 + 50 + $merge_src = $diff->getStagingRef(); 51 + } else { 52 + throw new Exception( 53 + pht( 54 + 'Invalid or unknown object ("%s") for land operation, expected '. 55 + 'Differential Revision.', 56 + $operation->getObjectPHID())); 57 + } 58 + 59 + $target = $operation->getRepositoryTarget(); 60 + list($type, $name) = explode(':', $target, 2); 61 + switch ($type) { 62 + case 'branch': 63 + $push_dst = 'refs/heads/'.$name; 64 + $merge_dst = 'refs/remotes/origin/'.$name; 65 + break; 66 + default: 67 + throw new Exception( 68 + pht( 69 + 'Unknown repository operation target type "%s" (in target "%s").', 70 + $type, 71 + $target)); 72 + } 73 + 74 + $cmd[] = 'git checkout %s'; 75 + $arg[] = $merge_dst; 76 + 77 + $cmd[] = 'git merge --no-stat --squash --ff-only -- %s'; 78 + $arg[] = $merge_src; 79 + 80 + $cmd[] = 'git -c user.name=%s -c user.email=%s commit --author %s -m %s'; 81 + $arg[] = 'autocommitter'; 82 + $arg[] = 'autocommitter@example.com'; 83 + $arg[] = 'autoauthor <autoauthor@example.com>'; 84 + $arg[] = pht('(Automerge!)'); 85 + 86 + $cmd[] = 'git push origin -- %s:%s'; 87 + $arg[] = 'HEAD'; 88 + $arg[] = $push_dst; 89 + 90 + $cmd = implode(' && ', $cmd); 91 + $argv = array_merge(array($cmd), $arg); 92 + 93 + $result = call_user_func_array( 94 + array($interface, 'execx'), 95 + $argv); 96 + } 97 + 8 98 }
+15
src/applications/drydock/operation/DrydockRepositoryOperationType.php
··· 2 2 3 3 abstract class DrydockRepositoryOperationType extends Phobject { 4 4 5 + private $viewer; 6 + 7 + abstract public function applyOperation( 8 + DrydockRepositoryOperation $operation, 9 + DrydockInterface $interface); 10 + 11 + final public function setViewer(PhabricatorUser $viewer) { 12 + $this->viewer = $viewer; 13 + return $this; 14 + } 15 + 16 + final public function getViewer() { 17 + return $this->viewer; 18 + } 19 + 5 20 final public function getOperationConstant() { 6 21 return $this->getPhobjectClassConstant('OPCONST', 32); 7 22 }
+14 -1
src/applications/drydock/query/DrydockRepositoryOperationQuery.php
··· 42 42 } 43 43 44 44 protected function willFilterPage(array $operations) { 45 + $implementations = DrydockRepositoryOperationType::getAllOperationTypes(); 46 + 47 + foreach ($operations as $key => $operation) { 48 + $impl = idx($implementations, $operation->getOperationType()); 49 + if (!$impl) { 50 + $this->didRejectResult($operation); 51 + unset($operations[$key]); 52 + continue; 53 + } 54 + $impl = clone $impl; 55 + $operation->attachImplementation($impl); 56 + } 57 + 45 58 $repository_phids = mpull($operations, 'getRepositoryPHID'); 46 59 if ($repository_phids) { 47 60 $repositories = id(new PhabricatorRepositoryQuery()) ··· 75 88 ->setParentQuery($this) 76 89 ->withPHIDs($object_phids) 77 90 ->execute(); 78 - $objects = mpull($objects, 'getPHID'); 91 + $objects = mpull($objects, null, 'getPHID'); 79 92 } else { 80 93 $objects = array(); 81 94 }
+16
src/applications/drydock/storage/DrydockRepositoryOperation.php
··· 23 23 24 24 private $repository = self::ATTACHABLE; 25 25 private $object = self::ATTACHABLE; 26 + private $implementation = self::ATTACHABLE; 26 27 27 28 public static function initializeNewOperation( 28 29 DrydockRepositoryOperationType $op) { ··· 77 78 return $this->assertAttached($this->object); 78 79 } 79 80 81 + public function attachImplementation(DrydockRepositoryOperationType $impl) { 82 + $this->implementation = $impl; 83 + return $this; 84 + } 85 + 86 + public function getImplementation() { 87 + return $this->implementation; 88 + } 89 + 80 90 public function getProperty($key, $default = null) { 81 91 return idx($this->properties, $key, $default); 82 92 } ··· 118 128 'objectPHID' => $this->getPHID(), 119 129 'priority' => PhabricatorWorker::PRIORITY_ALERTS, 120 130 )); 131 + } 132 + 133 + public function applyOperation(DrydockInterface $interface) { 134 + return $this->getImplementation()->applyOperation( 135 + $this, 136 + $interface); 121 137 } 122 138 123 139
+7 -3
src/applications/drydock/worker/DrydockRepositoryOperationUpdateWorker.php
··· 25 25 26 26 27 27 private function handleUpdate(DrydockRepositoryOperation $operation) { 28 + $viewer = $this->getViewer(); 29 + 28 30 $operation_state = $operation->getOperationState(); 29 31 30 32 switch ($operation_state) { ··· 59 61 // No matter what happens here, destroy the lease away once we're done. 60 62 $lease->releaseOnDestruction(true); 61 63 62 - // TODO: Some day, do useful things instead of running `git show`. 63 - list($stdout) = $interface->execx('git show'); 64 - phlog($stdout); 64 + $operation->getImplementation() 65 + ->setViewer($viewer); 66 + 67 + $operation->applyOperation($interface); 68 + 65 69 } catch (PhabricatorWorkerYieldException $ex) { 66 70 throw $ex; 67 71 } catch (Exception $ex) {