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

Remove old "Landing Strategy" code

Summary:
Fixes T12869. This is a very old, pre-Drydock chunk of code from D7486 and some followups.

It does three things:

- "Land to Hosted Git": Obsoleted by Drydock, has been commented out in HEAD for a very long time with no complaints. Disabled by D8719 in 2014.
- "Land to Hosted Mercurial": Could be obsoleted by Drydock with a fairly small amount of work, but currently has no replacement. Unclear if this sees any real use. Not actually disabled at HEAD.
- "Land to GitHub": Use GitHub OAuth credentials to land to GitHub. This is sort of theoretically useful and has no analog today. Disabled by D13022 in 2015.

This stuff was largely disabled a long time ago and we haven't seen users hitting issues with it. This could all be moved to an extension today if anyone still relies on it.

Test Plan: Grepped for removed classes, browsed Differential.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T12869

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

-764
-12
src/__phutil_library_map__.php
··· 453 453 'DifferentialGetRevisionCommentsConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetRevisionCommentsConduitAPIMethod.php', 454 454 'DifferentialGetRevisionConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetRevisionConduitAPIMethod.php', 455 455 'DifferentialGetWorkingCopy' => 'applications/differential/DifferentialGetWorkingCopy.php', 456 - 'DifferentialGitHubLandingStrategy' => 'applications/differential/landing/DifferentialGitHubLandingStrategy.php', 457 456 'DifferentialGitSVNIDCommitMessageField' => 'applications/differential/field/DifferentialGitSVNIDCommitMessageField.php', 458 457 'DifferentialHarbormasterField' => 'applications/differential/customfield/DifferentialHarbormasterField.php', 459 458 'DifferentialHiddenComment' => 'applications/differential/storage/DifferentialHiddenComment.php', 460 459 'DifferentialHostField' => 'applications/differential/customfield/DifferentialHostField.php', 461 - 'DifferentialHostedGitLandingStrategy' => 'applications/differential/landing/DifferentialHostedGitLandingStrategy.php', 462 - 'DifferentialHostedMercurialLandingStrategy' => 'applications/differential/landing/DifferentialHostedMercurialLandingStrategy.php', 463 460 'DifferentialHovercardEngineExtension' => 'applications/differential/engineextension/DifferentialHovercardEngineExtension.php', 464 461 'DifferentialHunk' => 'applications/differential/storage/DifferentialHunk.php', 465 462 'DifferentialHunkParser' => 'applications/differential/parser/DifferentialHunkParser.php', ··· 472 469 'DifferentialInlineCommentQuery' => 'applications/differential/query/DifferentialInlineCommentQuery.php', 473 470 'DifferentialJIRAIssuesCommitMessageField' => 'applications/differential/field/DifferentialJIRAIssuesCommitMessageField.php', 474 471 'DifferentialJIRAIssuesField' => 'applications/differential/customfield/DifferentialJIRAIssuesField.php', 475 - 'DifferentialLandingActionMenuEventListener' => 'applications/differential/landing/DifferentialLandingActionMenuEventListener.php', 476 - 'DifferentialLandingStrategy' => 'applications/differential/landing/DifferentialLandingStrategy.php', 477 472 'DifferentialLegacyHunk' => 'applications/differential/storage/DifferentialLegacyHunk.php', 478 473 'DifferentialLineAdjustmentMap' => 'applications/differential/parser/DifferentialLineAdjustmentMap.php', 479 474 'DifferentialLintField' => 'applications/differential/customfield/DifferentialLintField.php', ··· 548 543 'DifferentialRevisionHeraldFieldGroup' => 'applications/differential/herald/DifferentialRevisionHeraldFieldGroup.php', 549 544 'DifferentialRevisionIDCommitMessageField' => 'applications/differential/field/DifferentialRevisionIDCommitMessageField.php', 550 545 'DifferentialRevisionInlinesController' => 'applications/differential/controller/DifferentialRevisionInlinesController.php', 551 - 'DifferentialRevisionLandController' => 'applications/differential/controller/DifferentialRevisionLandController.php', 552 546 'DifferentialRevisionListController' => 'applications/differential/controller/DifferentialRevisionListController.php', 553 547 'DifferentialRevisionListView' => 'applications/differential/view/DifferentialRevisionListView.php', 554 548 'DifferentialRevisionMailReceiver' => 'applications/differential/mail/DifferentialRevisionMailReceiver.php', ··· 5411 5405 'DifferentialGetRevisionCommentsConduitAPIMethod' => 'DifferentialConduitAPIMethod', 5412 5406 'DifferentialGetRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod', 5413 5407 'DifferentialGetWorkingCopy' => 'Phobject', 5414 - 'DifferentialGitHubLandingStrategy' => 'DifferentialLandingStrategy', 5415 5408 'DifferentialGitSVNIDCommitMessageField' => 'DifferentialCommitMessageField', 5416 5409 'DifferentialHarbormasterField' => 'DifferentialCustomField', 5417 5410 'DifferentialHiddenComment' => 'DifferentialDAO', 5418 5411 'DifferentialHostField' => 'DifferentialCustomField', 5419 - 'DifferentialHostedGitLandingStrategy' => 'DifferentialLandingStrategy', 5420 - 'DifferentialHostedMercurialLandingStrategy' => 'DifferentialLandingStrategy', 5421 5412 'DifferentialHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', 5422 5413 'DifferentialHunk' => array( 5423 5414 'DifferentialDAO', ··· 5436 5427 'DifferentialInlineCommentQuery' => 'PhabricatorOffsetPagedQuery', 5437 5428 'DifferentialJIRAIssuesCommitMessageField' => 'DifferentialCommitMessageCustomField', 5438 5429 'DifferentialJIRAIssuesField' => 'DifferentialStoredCustomField', 5439 - 'DifferentialLandingActionMenuEventListener' => 'PhabricatorEventListener', 5440 - 'DifferentialLandingStrategy' => 'Phobject', 5441 5430 'DifferentialLegacyHunk' => 'DifferentialHunk', 5442 5431 'DifferentialLineAdjustmentMap' => 'Phobject', 5443 5432 'DifferentialLintField' => 'DifferentialHarbormasterField', ··· 5529 5518 'DifferentialRevisionHeraldFieldGroup' => 'HeraldFieldGroup', 5530 5519 'DifferentialRevisionIDCommitMessageField' => 'DifferentialCommitMessageField', 5531 5520 'DifferentialRevisionInlinesController' => 'DifferentialController', 5532 - 'DifferentialRevisionLandController' => 'DifferentialController', 5533 5521 'DifferentialRevisionListController' => 'DifferentialController', 5534 5522 'DifferentialRevisionListView' => 'AphrontView', 5535 5523 'DifferentialRevisionMailReceiver' => 'PhabricatorObjectMailReceiver',
-8
src/applications/differential/application/PhabricatorDifferentialApplication.php
··· 41 41 return "\xE2\x9A\x99"; 42 42 } 43 43 44 - public function getEventListeners() { 45 - return array( 46 - new DifferentialLandingActionMenuEventListener(), 47 - ); 48 - } 49 - 50 44 public function getOverview() { 51 45 return pht( 52 46 'Differential is a **code review application** which allows '. ··· 69 63 => 'DifferentialRevisionEditController', 70 64 $this->getEditRoutePattern('attach/(?P<diffID>[^/]+)/to/') 71 65 => 'DifferentialRevisionEditController', 72 - 'land/(?:(?P<id>[1-9]\d*))/(?P<strategy>[^/]+)/' 73 - => 'DifferentialRevisionLandController', 74 66 'closedetails/(?P<phid>[^/]+)/' 75 67 => 'DifferentialRevisionCloseDetailsController', 76 68 'update/(?P<revisionID>[1-9]\d*)/'
-157
src/applications/differential/controller/DifferentialRevisionLandController.php
··· 1 - <?php 2 - 3 - final class DifferentialRevisionLandController extends DifferentialController { 4 - 5 - private $pushStrategy; 6 - 7 - public function handleRequest(AphrontRequest $request) { 8 - $viewer = $this->getViewer(); 9 - $revision_id = $request->getURIData('id'); 10 - $strategy_class = $request->getURIData('strategy'); 11 - 12 - $revision = id(new DifferentialRevisionQuery()) 13 - ->withIDs(array($revision_id)) 14 - ->setViewer($viewer) 15 - ->executeOne(); 16 - if (!$revision) { 17 - return new Aphront404Response(); 18 - } 19 - 20 - if (is_subclass_of($strategy_class, 'DifferentialLandingStrategy')) { 21 - $this->pushStrategy = newv($strategy_class, array()); 22 - } else { 23 - throw new Exception( 24 - pht( 25 - "Strategy type must be a valid class name and must subclass ". 26 - "%s. '%s' is not a subclass of %s", 27 - 'DifferentialLandingStrategy', 28 - $strategy_class, 29 - 'DifferentialLandingStrategy')); 30 - } 31 - 32 - if ($request->isDialogFormPost()) { 33 - $response = null; 34 - $text = ''; 35 - try { 36 - $response = $this->attemptLand($revision, $request); 37 - $title = pht('Success!'); 38 - $text = pht('Revision was successfully landed.'); 39 - } catch (Exception $ex) { 40 - $title = pht('Failed to land revision'); 41 - if ($ex instanceof PhutilProxyException) { 42 - $text = hsprintf( 43 - '%s:<br><pre>%s</pre>', 44 - $ex->getMessage(), 45 - $ex->getPreviousException()->getMessage()); 46 - } else { 47 - $text = phutil_tag('pre', array(), $ex->getMessage()); 48 - } 49 - $text = id(new PHUIInfoView()) 50 - ->appendChild($text); 51 - } 52 - 53 - if ($response instanceof AphrontDialogView) { 54 - $dialog = $response; 55 - } else { 56 - $dialog = id(new AphrontDialogView()) 57 - ->setUser($viewer) 58 - ->setTitle($title) 59 - ->appendChild(phutil_tag('p', array(), $text)) 60 - ->addCancelButton('/D'.$revision_id, pht('Done')); 61 - } 62 - return id(new AphrontDialogResponse())->setDialog($dialog); 63 - } 64 - 65 - $is_disabled = $this->pushStrategy->isActionDisabled( 66 - $viewer, 67 - $revision, 68 - $revision->getRepository()); 69 - if ($is_disabled) { 70 - if (is_string($is_disabled)) { 71 - $explain = $is_disabled; 72 - } else { 73 - $explain = pht('This action is not currently enabled.'); 74 - } 75 - $dialog = id(new AphrontDialogView()) 76 - ->setUser($viewer) 77 - ->setTitle(pht("Can't land revision")) 78 - ->appendChild($explain) 79 - ->addCancelButton('/D'.$revision_id); 80 - 81 - return id(new AphrontDialogResponse())->setDialog($dialog); 82 - } 83 - 84 - 85 - $prompt = hsprintf('%s<br><br>%s', 86 - pht( 87 - 'This will squash and rebase revision %s, and push it to '. 88 - 'the default / master branch.', 89 - $revision_id), 90 - pht('It is an experimental feature and may not work.')); 91 - 92 - $dialog = id(new AphrontDialogView()) 93 - ->setUser($viewer) 94 - ->setTitle(pht('Land Revision %s?', $revision_id)) 95 - ->appendChild($prompt) 96 - ->setSubmitURI($request->getRequestURI()) 97 - ->addSubmitButton(pht('Land it!')) 98 - ->addCancelButton('/D'.$revision_id); 99 - 100 - return id(new AphrontDialogResponse())->setDialog($dialog); 101 - } 102 - 103 - private function attemptLand($revision, $request) { 104 - $status = $revision->getStatus(); 105 - if ($status != ArcanistDifferentialRevisionStatus::ACCEPTED) { 106 - throw new Exception(pht('Only Accepted revisions can be landed.')); 107 - } 108 - 109 - $repository = $revision->getRepository(); 110 - 111 - if ($repository === null) { 112 - throw new Exception(pht('Revision is not attached to a repository.')); 113 - } 114 - 115 - $can_push = PhabricatorPolicyFilter::hasCapability( 116 - $request->getUser(), 117 - $repository, 118 - DiffusionPushCapability::CAPABILITY); 119 - 120 - if (!$can_push) { 121 - throw new Exception( 122 - pht('You do not have permission to push to this repository.')); 123 - } 124 - 125 - $lock = $this->lockRepository($repository); 126 - 127 - try { 128 - $response = $this->pushStrategy->processLandRequest( 129 - $request, 130 - $revision, 131 - $repository); 132 - } catch (Exception $e) { 133 - $lock->unlock(); 134 - throw $e; 135 - } 136 - 137 - $lock->unlock(); 138 - 139 - $looksoon = new ConduitCall( 140 - 'diffusion.looksoon', 141 - array( 142 - 'repositories' => array($repository->getPHID()), 143 - )); 144 - $looksoon->setUser($request->getUser()); 145 - $looksoon->execute(); 146 - 147 - return $response; 148 - } 149 - 150 - private function lockRepository($repository) { 151 - $lock_name = __CLASS__.':'.($repository->getPHID()); 152 - $lock = PhabricatorGlobalLock::newLock($lock_name); 153 - $lock->lock(); 154 - return $lock; 155 - } 156 - 157 - }
-186
src/applications/differential/landing/DifferentialGitHubLandingStrategy.php
··· 1 - <?php 2 - 3 - final class DifferentialGitHubLandingStrategy 4 - extends DifferentialLandingStrategy { 5 - 6 - private $account; 7 - private $provider; 8 - 9 - public function processLandRequest( 10 - AphrontRequest $request, 11 - DifferentialRevision $revision, 12 - PhabricatorRepository $repository) { 13 - 14 - $viewer = $request->getUser(); 15 - $this->init($viewer, $repository); 16 - 17 - $workspace = $this->getGitWorkspace($repository); 18 - 19 - try { 20 - id(new DifferentialHostedGitLandingStrategy()) 21 - ->commitRevisionToWorkspace($revision, $workspace, $viewer); 22 - } catch (Exception $e) { 23 - throw new PhutilProxyException(pht('Failed to commit patch.'), $e); 24 - } 25 - 26 - try { 27 - $this->pushWorkspaceRepository($repository, $workspace); 28 - } catch (Exception $e) { 29 - // If it's a permission problem, we know more than git. 30 - $dialog = $this->verifyRemotePermissions($viewer, $revision, $repository); 31 - if ($dialog) { 32 - return $dialog; 33 - } 34 - 35 - // Else, throw what git said. 36 - throw new PhutilProxyException( 37 - pht('Failed to push changes upstream.'), 38 - $e); 39 - } 40 - } 41 - 42 - /** 43 - * Returns PhabricatorActionView or an array of PhabricatorActionView or null. 44 - */ 45 - public function createMenuItem( 46 - PhabricatorUser $viewer, 47 - DifferentialRevision $revision, 48 - PhabricatorRepository $repository) { 49 - 50 - // TODO: This temporarily disables this action, because it doesn't work 51 - // and is confusing to users. If you want to use it, comment out this line 52 - // for now and we'll provide real support eventually. 53 - return; 54 - 55 - $vcs = $repository->getVersionControlSystem(); 56 - if ($vcs !== PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) { 57 - return; 58 - } 59 - 60 - if ($repository->isHosted()) { 61 - return; 62 - } 63 - 64 - try { 65 - // These throw when failing. 66 - $this->init($viewer, $repository); 67 - $this->findGitHubRepo($repository); 68 - } catch (Exception $e) { 69 - return; 70 - } 71 - 72 - return $this->createActionView($revision, pht('Land to GitHub')) 73 - ->setIcon('fa-cloud-upload'); 74 - } 75 - 76 - public function pushWorkspaceRepository( 77 - PhabricatorRepository $repository, 78 - ArcanistRepositoryAPI $workspace) { 79 - 80 - $token = $this->getAccessToken(); 81 - 82 - $github_repo = $this->findGitHubRepo($repository); 83 - 84 - $remote = urisprintf( 85 - 'https://%s:x-oauth-basic@%s/%s.git', 86 - $token, 87 - $this->provider->getProviderDomain(), 88 - $github_repo); 89 - 90 - $workspace->execxLocal( 91 - 'push %P HEAD:master', 92 - new PhutilOpaqueEnvelope($remote)); 93 - } 94 - 95 - private function init($viewer, $repository) { 96 - $repo_uri = $repository->getRemoteURIObject(); 97 - $repo_domain = $repo_uri->getDomain(); 98 - 99 - $this->account = id(new PhabricatorExternalAccountQuery()) 100 - ->setViewer($viewer) 101 - ->withUserPHIDs(array($viewer->getPHID())) 102 - ->withAccountTypes(array('github')) 103 - ->withAccountDomains(array($repo_domain)) 104 - ->requireCapabilities( 105 - array( 106 - PhabricatorPolicyCapability::CAN_VIEW, 107 - PhabricatorPolicyCapability::CAN_EDIT, 108 - )) 109 - ->executeOne(); 110 - 111 - if (!$this->account) { 112 - throw new Exception( 113 - pht('No matching GitHub account found for %s.', $repo_domain)); 114 - } 115 - 116 - $this->provider = PhabricatorAuthProvider::getEnabledProviderByKey( 117 - $this->account->getProviderKey()); 118 - if (!$this->provider) { 119 - throw new Exception( 120 - pht('GitHub provider for %s is not enabled.', $repo_domain)); 121 - } 122 - } 123 - 124 - private function findGitHubRepo(PhabricatorRepository $repository) { 125 - $repo_uri = $repository->getRemoteURIObject(); 126 - 127 - $repo_path = $repo_uri->getPath(); 128 - 129 - if (substr($repo_path, -4) == '.git') { 130 - $repo_path = substr($repo_path, 0, -4); 131 - } 132 - $repo_path = ltrim($repo_path, '/'); 133 - 134 - return $repo_path; 135 - } 136 - 137 - private function getAccessToken() { 138 - return $this->provider->getOAuthAccessToken($this->account); 139 - } 140 - 141 - private function verifyRemotePermissions($viewer, $revision, $repository) { 142 - $github_user = $this->account->getUsername(); 143 - $github_repo = $this->findGitHubRepo($repository); 144 - 145 - $uri = urisprintf( 146 - 'https://api.github.com/repos/%s/collaborators/%s', 147 - $github_repo, 148 - $github_user); 149 - 150 - $uri = new PhutilURI($uri); 151 - $uri->setQueryParam('access_token', $this->getAccessToken()); 152 - list($status, $body, $headers) = id(new HTTPSFuture($uri))->resolve(); 153 - 154 - // Likely status codes: 155 - // 204 No Content: Has permissions. Token might be too weak. 156 - // 404 Not Found: Not a collaborator. 157 - // 401 Unauthorized: Token is bad/revoked. 158 - 159 - $no_permission = ($status->getStatusCode() == 404); 160 - 161 - if ($no_permission) { 162 - throw new Exception( 163 - pht( 164 - "You don't have permission to push to this repository. ". 165 - "Push permissions for this repository are managed on GitHub.")); 166 - } 167 - 168 - $scopes = BaseHTTPFuture::getHeader($headers, 'X-OAuth-Scopes'); 169 - if (strpos($scopes, 'public_repo') === false) { 170 - $provider_key = $this->provider->getProviderKey(); 171 - $refresh_token_uri = new PhutilURI("/auth/refresh/{$provider_key}/"); 172 - $refresh_token_uri->setQueryParam('scope', 'public_repo'); 173 - 174 - return id(new AphrontDialogView()) 175 - ->setUser($viewer) 176 - ->setTitle(pht('Stronger token needed')) 177 - ->appendChild(pht( 178 - 'In order to complete this action, you need a '. 179 - 'stronger GitHub token.')) 180 - ->setSubmitURI($refresh_token_uri) 181 - ->addCancelButton('/D'.$revision->getId()) 182 - ->setDisableWorkflowOnSubmit(true) 183 - ->addSubmitButton(pht('Refresh Account Link')); 184 - } 185 - } 186 - }
-127
src/applications/differential/landing/DifferentialHostedGitLandingStrategy.php
··· 1 - <?php 2 - 3 - final class DifferentialHostedGitLandingStrategy 4 - extends DifferentialLandingStrategy { 5 - 6 - public function processLandRequest( 7 - AphrontRequest $request, 8 - DifferentialRevision $revision, 9 - PhabricatorRepository $repository) { 10 - 11 - $viewer = $request->getUser(); 12 - $workspace = $this->getGitWorkspace($repository); 13 - 14 - try { 15 - $this->commitRevisionToWorkspace($revision, $workspace, $viewer); 16 - } catch (Exception $e) { 17 - throw new PhutilProxyException( 18 - pht('Failed to commit patch.'), 19 - $e); 20 - } 21 - 22 - try { 23 - $this->pushWorkspaceRepository($repository, $workspace, $viewer); 24 - } catch (Exception $e) { 25 - throw new PhutilProxyException( 26 - pht('Failed to push changes upstream.'), 27 - $e); 28 - } 29 - } 30 - 31 - public function commitRevisionToWorkspace( 32 - DifferentialRevision $revision, 33 - ArcanistRepositoryAPI $workspace, 34 - PhabricatorUser $user) { 35 - 36 - $diff_id = $revision->loadActiveDiff()->getID(); 37 - 38 - $call = new ConduitCall( 39 - 'differential.getrawdiff', 40 - array( 41 - 'diffID' => $diff_id, 42 - )); 43 - 44 - $call->setUser($user); 45 - $raw_diff = $call->execute(); 46 - 47 - $missing_binary = 48 - "\nindex " 49 - ."0000000000000000000000000000000000000000.." 50 - ."0000000000000000000000000000000000000000\n"; 51 - if (strpos($raw_diff, $missing_binary) !== false) { 52 - throw new Exception(pht('Patch is missing content for a binary file')); 53 - } 54 - 55 - $future = $workspace->execFutureLocal('apply --index -'); 56 - $future->write($raw_diff); 57 - $future->resolvex(); 58 - 59 - $workspace->reloadWorkingCopy(); 60 - 61 - $call = new ConduitCall( 62 - 'differential.getcommitmessage', 63 - array( 64 - 'revision_id' => $revision->getID(), 65 - )); 66 - 67 - $call->setUser($user); 68 - $message = $call->execute(); 69 - 70 - $author = id(new PhabricatorUser())->loadOneWhere( 71 - 'phid = %s', 72 - $revision->getAuthorPHID()); 73 - 74 - $author_string = sprintf( 75 - '%s <%s>', 76 - $author->getRealName(), 77 - $author->loadPrimaryEmailAddress()); 78 - $author_date = $revision->getDateCreated(); 79 - 80 - $workspace->execxLocal( 81 - '-c user.name=%s -c user.email=%s '. 82 - 'commit --date=%s --author=%s '. 83 - '--message=%s', 84 - // -c will set the 'committer' 85 - $user->getRealName(), 86 - $user->loadPrimaryEmailAddress(), 87 - $author_date, 88 - $author_string, 89 - $message); 90 - } 91 - 92 - public function pushWorkspaceRepository( 93 - PhabricatorRepository $repository, 94 - ArcanistRepositoryAPI $workspace, 95 - PhabricatorUser $user) { 96 - 97 - $workspace->execxLocal('push origin HEAD:master'); 98 - } 99 - 100 - public function createMenuItem( 101 - PhabricatorUser $viewer, 102 - DifferentialRevision $revision, 103 - PhabricatorRepository $repository) { 104 - 105 - $vcs = $repository->getVersionControlSystem(); 106 - if ($vcs !== PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) { 107 - return; 108 - } 109 - 110 - if (!$repository->isHosted()) { 111 - return; 112 - } 113 - 114 - if (!$repository->isWorkingCopyBare()) { 115 - return; 116 - } 117 - 118 - // TODO: This temporarily disables this action, because it doesn't work 119 - // and is confusing to users. If you want to use it, comment out this line 120 - // for now and we'll provide real support eventually. 121 - return; 122 - 123 - return $this->createActionView( 124 - $revision, 125 - pht('Land to Hosted Repository')); 126 - } 127 - }
-106
src/applications/differential/landing/DifferentialHostedMercurialLandingStrategy.php
··· 1 - <?php 2 - 3 - final class DifferentialHostedMercurialLandingStrategy 4 - extends DifferentialLandingStrategy { 5 - 6 - public function processLandRequest( 7 - AphrontRequest $request, 8 - DifferentialRevision $revision, 9 - PhabricatorRepository $repository) { 10 - 11 - $viewer = $request->getUser(); 12 - 13 - $workspace = $this->getMercurialWorkspace($repository); 14 - 15 - try { 16 - $this->commitRevisionToWorkspace($revision, $workspace, $viewer); 17 - } catch (Exception $e) { 18 - throw new PhutilProxyException(pht('Failed to commit patch.'), $e); 19 - } 20 - 21 - try { 22 - $this->pushWorkspaceRepository($repository, $workspace, $viewer); 23 - } catch (Exception $e) { 24 - throw new PhutilProxyException( 25 - pht('Failed to push changes upstream.'), 26 - $e); 27 - } 28 - } 29 - 30 - public function commitRevisionToWorkspace( 31 - DifferentialRevision $revision, 32 - ArcanistRepositoryAPI $workspace, 33 - PhabricatorUser $user) { 34 - 35 - $diff_id = $revision->loadActiveDiff()->getID(); 36 - 37 - $call = new ConduitCall( 38 - 'differential.getrawdiff', 39 - array( 40 - 'diffID' => $diff_id, 41 - )); 42 - 43 - $call->setUser($user); 44 - $raw_diff = $call->execute(); 45 - 46 - $future = $workspace->execFutureLocal('patch --no-commit -'); 47 - $future->write($raw_diff); 48 - $future->resolvex(); 49 - 50 - $workspace->reloadWorkingCopy(); 51 - 52 - $call = new ConduitCall( 53 - 'differential.getcommitmessage', 54 - array( 55 - 'revision_id' => $revision->getID(), 56 - )); 57 - 58 - $call->setUser($user); 59 - $message = $call->execute(); 60 - 61 - $author = id(new PhabricatorUser())->loadOneWhere( 62 - 'phid = %s', 63 - $revision->getAuthorPHID()); 64 - 65 - $author_string = sprintf( 66 - '%s <%s>', 67 - $author->getRealName(), 68 - $author->loadPrimaryEmailAddress()); 69 - $author_date = $revision->getDateCreated(); 70 - 71 - $workspace->execxLocal( 72 - 'commit --date=%s --user=%s '. 73 - '--message=%s', 74 - $author_date.' 0', 75 - $author_string, 76 - $message); 77 - } 78 - 79 - 80 - public function pushWorkspaceRepository( 81 - PhabricatorRepository $repository, 82 - ArcanistRepositoryAPI $workspace, 83 - PhabricatorUser $user) { 84 - 85 - $workspace->execxLocal('push -b default'); 86 - } 87 - 88 - public function createMenuItem( 89 - PhabricatorUser $viewer, 90 - DifferentialRevision $revision, 91 - PhabricatorRepository $repository) { 92 - 93 - $vcs = $repository->getVersionControlSystem(); 94 - if ($vcs !== PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL) { 95 - return; 96 - } 97 - 98 - if (!$repository->isHosted()) { 99 - return; 100 - } 101 - 102 - return $this->createActionView( 103 - $revision, 104 - pht('Land to Hosted Repository')); 105 - } 106 - }
-81
src/applications/differential/landing/DifferentialLandingActionMenuEventListener.php
··· 1 - <?php 2 - 3 - /** 4 - * This class adds a "Land this" button to revision view. 5 - */ 6 - final class DifferentialLandingActionMenuEventListener 7 - extends PhabricatorEventListener { 8 - 9 - public function register() { 10 - $this->listen(PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS); 11 - } 12 - 13 - public function handleEvent(PhutilEvent $event) { 14 - switch ($event->getType()) { 15 - case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS: 16 - $this->handleActionsEvent($event); 17 - break; 18 - } 19 - } 20 - 21 - private function handleActionsEvent(PhutilEvent $event) { 22 - $object = $event->getValue('object'); 23 - if ($object instanceof DifferentialRevision) { 24 - $this->renderRevisionAction($event); 25 - } 26 - } 27 - 28 - private function renderRevisionAction(PhutilEvent $event) { 29 - $viewer = $event->getUser(); 30 - 31 - if (!$this->canUseApplication($viewer)) { 32 - return null; 33 - } 34 - 35 - $revision = $event->getValue('object'); 36 - 37 - $repository = $revision->getRepository(); 38 - if ($repository === null) { 39 - return null; 40 - } 41 - 42 - if ($repository->canPerformAutomation()) { 43 - $revision_id = $revision->getID(); 44 - 45 - $op = new DrydockLandRepositoryOperation(); 46 - $barrier = $op->getBarrierToLanding($viewer, $revision); 47 - 48 - if ($barrier) { 49 - $can_land = false; 50 - } else { 51 - $can_land = true; 52 - } 53 - 54 - $action = id(new PhabricatorActionView()) 55 - ->setName(pht('Land Revision')) 56 - ->setIcon('fa-fighter-jet') 57 - ->setHref("/differential/revision/operation/{$revision_id}/") 58 - ->setWorkflow(true) 59 - ->setDisabled(!$can_land); 60 - 61 - 62 - $this->addActionMenuItems($event, $action); 63 - } 64 - 65 - $strategies = id(new PhutilClassMapQuery()) 66 - ->setAncestorClass('DifferentialLandingStrategy') 67 - ->execute(); 68 - 69 - foreach ($strategies as $strategy) { 70 - $action = $strategy->createMenuItem($viewer, $revision, $repository); 71 - if ($action == null) { 72 - continue; 73 - } 74 - if ($strategy->isActionDisabled($viewer, $revision, $repository)) { 75 - $action->setDisabled(true); 76 - } 77 - $this->addActionMenuItems($event, $action); 78 - } 79 - } 80 - 81 - }
-87
src/applications/differential/landing/DifferentialLandingStrategy.php
··· 1 - <?php 2 - 3 - abstract class DifferentialLandingStrategy extends Phobject { 4 - 5 - abstract public function processLandRequest( 6 - AphrontRequest $request, 7 - DifferentialRevision $revision, 8 - PhabricatorRepository $repository); 9 - 10 - /** 11 - * @return PhabricatorActionView or null. 12 - */ 13 - abstract public function createMenuItem( 14 - PhabricatorUser $viewer, 15 - DifferentialRevision $revision, 16 - PhabricatorRepository $repository); 17 - 18 - /** 19 - * @return PhabricatorActionView which can be attached to the revision view. 20 - */ 21 - protected function createActionView($revision, $name) { 22 - $strategy = get_class($this); 23 - $revision_id = $revision->getId(); 24 - return id(new PhabricatorActionView()) 25 - ->setRenderAsForm(true) 26 - ->setWorkflow(true) 27 - ->setName($name) 28 - ->setHref("/differential/revision/land/{$revision_id}/{$strategy}/"); 29 - } 30 - 31 - /** 32 - * Check if this action should be disabled, and explain why. 33 - * 34 - * By default, this method checks for push permissions, and for the 35 - * revision being Accepted. 36 - * 37 - * @return False for "not disabled"; human-readable text explaining why, if 38 - * it is disabled. 39 - */ 40 - public function isActionDisabled( 41 - PhabricatorUser $viewer, 42 - DifferentialRevision $revision, 43 - PhabricatorRepository $repository) { 44 - 45 - $status = $revision->getStatus(); 46 - if ($status != ArcanistDifferentialRevisionStatus::ACCEPTED) { 47 - return pht('Only Accepted revisions can be landed.'); 48 - } 49 - 50 - if (!PhabricatorPolicyFilter::hasCapability( 51 - $viewer, 52 - $repository, 53 - DiffusionPushCapability::CAPABILITY)) { 54 - return pht('You do not have permissions to push to this repository.'); 55 - } 56 - 57 - return false; 58 - } 59 - 60 - /** 61 - * Might break if repository is not Git. 62 - */ 63 - protected function getGitWorkspace(PhabricatorRepository $repository) { 64 - try { 65 - return DifferentialGetWorkingCopy::getCleanGitWorkspace($repository); 66 - } catch (Exception $e) { 67 - throw new PhutilProxyException( 68 - pht('Failed to allocate a workspace.'), 69 - $e); 70 - } 71 - } 72 - 73 - /** 74 - * Might break if repository is not Mercurial. 75 - */ 76 - protected function getMercurialWorkspace(PhabricatorRepository $repository) { 77 - try { 78 - return DifferentialGetWorkingCopy::getCleanMercurialWorkspace( 79 - $repository); 80 - } catch (Exception $e) { 81 - throw new PhutilProxyException( 82 - pht('Failed to allocate a workspace.'), 83 - $e); 84 - } 85 - } 86 - 87 - }