1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 16:52:41 +01:00

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
This commit is contained in:
epriestley 2017-06-23 07:51:32 -07:00
parent 58df1b7d3b
commit 219ae8b6c9
8 changed files with 0 additions and 764 deletions

View file

@ -453,13 +453,10 @@ phutil_register_library_map(array(
'DifferentialGetRevisionCommentsConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetRevisionCommentsConduitAPIMethod.php',
'DifferentialGetRevisionConduitAPIMethod' => 'applications/differential/conduit/DifferentialGetRevisionConduitAPIMethod.php',
'DifferentialGetWorkingCopy' => 'applications/differential/DifferentialGetWorkingCopy.php',
'DifferentialGitHubLandingStrategy' => 'applications/differential/landing/DifferentialGitHubLandingStrategy.php',
'DifferentialGitSVNIDCommitMessageField' => 'applications/differential/field/DifferentialGitSVNIDCommitMessageField.php',
'DifferentialHarbormasterField' => 'applications/differential/customfield/DifferentialHarbormasterField.php',
'DifferentialHiddenComment' => 'applications/differential/storage/DifferentialHiddenComment.php',
'DifferentialHostField' => 'applications/differential/customfield/DifferentialHostField.php',
'DifferentialHostedGitLandingStrategy' => 'applications/differential/landing/DifferentialHostedGitLandingStrategy.php',
'DifferentialHostedMercurialLandingStrategy' => 'applications/differential/landing/DifferentialHostedMercurialLandingStrategy.php',
'DifferentialHovercardEngineExtension' => 'applications/differential/engineextension/DifferentialHovercardEngineExtension.php',
'DifferentialHunk' => 'applications/differential/storage/DifferentialHunk.php',
'DifferentialHunkParser' => 'applications/differential/parser/DifferentialHunkParser.php',
@ -472,8 +469,6 @@ phutil_register_library_map(array(
'DifferentialInlineCommentQuery' => 'applications/differential/query/DifferentialInlineCommentQuery.php',
'DifferentialJIRAIssuesCommitMessageField' => 'applications/differential/field/DifferentialJIRAIssuesCommitMessageField.php',
'DifferentialJIRAIssuesField' => 'applications/differential/customfield/DifferentialJIRAIssuesField.php',
'DifferentialLandingActionMenuEventListener' => 'applications/differential/landing/DifferentialLandingActionMenuEventListener.php',
'DifferentialLandingStrategy' => 'applications/differential/landing/DifferentialLandingStrategy.php',
'DifferentialLegacyHunk' => 'applications/differential/storage/DifferentialLegacyHunk.php',
'DifferentialLineAdjustmentMap' => 'applications/differential/parser/DifferentialLineAdjustmentMap.php',
'DifferentialLintField' => 'applications/differential/customfield/DifferentialLintField.php',
@ -548,7 +543,6 @@ phutil_register_library_map(array(
'DifferentialRevisionHeraldFieldGroup' => 'applications/differential/herald/DifferentialRevisionHeraldFieldGroup.php',
'DifferentialRevisionIDCommitMessageField' => 'applications/differential/field/DifferentialRevisionIDCommitMessageField.php',
'DifferentialRevisionInlinesController' => 'applications/differential/controller/DifferentialRevisionInlinesController.php',
'DifferentialRevisionLandController' => 'applications/differential/controller/DifferentialRevisionLandController.php',
'DifferentialRevisionListController' => 'applications/differential/controller/DifferentialRevisionListController.php',
'DifferentialRevisionListView' => 'applications/differential/view/DifferentialRevisionListView.php',
'DifferentialRevisionMailReceiver' => 'applications/differential/mail/DifferentialRevisionMailReceiver.php',
@ -5411,13 +5405,10 @@ phutil_register_library_map(array(
'DifferentialGetRevisionCommentsConduitAPIMethod' => 'DifferentialConduitAPIMethod',
'DifferentialGetRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod',
'DifferentialGetWorkingCopy' => 'Phobject',
'DifferentialGitHubLandingStrategy' => 'DifferentialLandingStrategy',
'DifferentialGitSVNIDCommitMessageField' => 'DifferentialCommitMessageField',
'DifferentialHarbormasterField' => 'DifferentialCustomField',
'DifferentialHiddenComment' => 'DifferentialDAO',
'DifferentialHostField' => 'DifferentialCustomField',
'DifferentialHostedGitLandingStrategy' => 'DifferentialLandingStrategy',
'DifferentialHostedMercurialLandingStrategy' => 'DifferentialLandingStrategy',
'DifferentialHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
'DifferentialHunk' => array(
'DifferentialDAO',
@ -5436,8 +5427,6 @@ phutil_register_library_map(array(
'DifferentialInlineCommentQuery' => 'PhabricatorOffsetPagedQuery',
'DifferentialJIRAIssuesCommitMessageField' => 'DifferentialCommitMessageCustomField',
'DifferentialJIRAIssuesField' => 'DifferentialStoredCustomField',
'DifferentialLandingActionMenuEventListener' => 'PhabricatorEventListener',
'DifferentialLandingStrategy' => 'Phobject',
'DifferentialLegacyHunk' => 'DifferentialHunk',
'DifferentialLineAdjustmentMap' => 'Phobject',
'DifferentialLintField' => 'DifferentialHarbormasterField',
@ -5529,7 +5518,6 @@ phutil_register_library_map(array(
'DifferentialRevisionHeraldFieldGroup' => 'HeraldFieldGroup',
'DifferentialRevisionIDCommitMessageField' => 'DifferentialCommitMessageField',
'DifferentialRevisionInlinesController' => 'DifferentialController',
'DifferentialRevisionLandController' => 'DifferentialController',
'DifferentialRevisionListController' => 'DifferentialController',
'DifferentialRevisionListView' => 'AphrontView',
'DifferentialRevisionMailReceiver' => 'PhabricatorObjectMailReceiver',

View file

@ -41,12 +41,6 @@ final class PhabricatorDifferentialApplication extends PhabricatorApplication {
return "\xE2\x9A\x99";
}
public function getEventListeners() {
return array(
new DifferentialLandingActionMenuEventListener(),
);
}
public function getOverview() {
return pht(
'Differential is a **code review application** which allows '.
@ -69,8 +63,6 @@ final class PhabricatorDifferentialApplication extends PhabricatorApplication {
=> 'DifferentialRevisionEditController',
$this->getEditRoutePattern('attach/(?P<diffID>[^/]+)/to/')
=> 'DifferentialRevisionEditController',
'land/(?:(?P<id>[1-9]\d*))/(?P<strategy>[^/]+)/'
=> 'DifferentialRevisionLandController',
'closedetails/(?P<phid>[^/]+)/'
=> 'DifferentialRevisionCloseDetailsController',
'update/(?P<revisionID>[1-9]\d*)/'

View file

@ -1,157 +0,0 @@
<?php
final class DifferentialRevisionLandController extends DifferentialController {
private $pushStrategy;
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$revision_id = $request->getURIData('id');
$strategy_class = $request->getURIData('strategy');
$revision = id(new DifferentialRevisionQuery())
->withIDs(array($revision_id))
->setViewer($viewer)
->executeOne();
if (!$revision) {
return new Aphront404Response();
}
if (is_subclass_of($strategy_class, 'DifferentialLandingStrategy')) {
$this->pushStrategy = newv($strategy_class, array());
} else {
throw new Exception(
pht(
"Strategy type must be a valid class name and must subclass ".
"%s. '%s' is not a subclass of %s",
'DifferentialLandingStrategy',
$strategy_class,
'DifferentialLandingStrategy'));
}
if ($request->isDialogFormPost()) {
$response = null;
$text = '';
try {
$response = $this->attemptLand($revision, $request);
$title = pht('Success!');
$text = pht('Revision was successfully landed.');
} catch (Exception $ex) {
$title = pht('Failed to land revision');
if ($ex instanceof PhutilProxyException) {
$text = hsprintf(
'%s:<br><pre>%s</pre>',
$ex->getMessage(),
$ex->getPreviousException()->getMessage());
} else {
$text = phutil_tag('pre', array(), $ex->getMessage());
}
$text = id(new PHUIInfoView())
->appendChild($text);
}
if ($response instanceof AphrontDialogView) {
$dialog = $response;
} else {
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle($title)
->appendChild(phutil_tag('p', array(), $text))
->addCancelButton('/D'.$revision_id, pht('Done'));
}
return id(new AphrontDialogResponse())->setDialog($dialog);
}
$is_disabled = $this->pushStrategy->isActionDisabled(
$viewer,
$revision,
$revision->getRepository());
if ($is_disabled) {
if (is_string($is_disabled)) {
$explain = $is_disabled;
} else {
$explain = pht('This action is not currently enabled.');
}
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle(pht("Can't land revision"))
->appendChild($explain)
->addCancelButton('/D'.$revision_id);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
$prompt = hsprintf('%s<br><br>%s',
pht(
'This will squash and rebase revision %s, and push it to '.
'the default / master branch.',
$revision_id),
pht('It is an experimental feature and may not work.'));
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle(pht('Land Revision %s?', $revision_id))
->appendChild($prompt)
->setSubmitURI($request->getRequestURI())
->addSubmitButton(pht('Land it!'))
->addCancelButton('/D'.$revision_id);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
private function attemptLand($revision, $request) {
$status = $revision->getStatus();
if ($status != ArcanistDifferentialRevisionStatus::ACCEPTED) {
throw new Exception(pht('Only Accepted revisions can be landed.'));
}
$repository = $revision->getRepository();
if ($repository === null) {
throw new Exception(pht('Revision is not attached to a repository.'));
}
$can_push = PhabricatorPolicyFilter::hasCapability(
$request->getUser(),
$repository,
DiffusionPushCapability::CAPABILITY);
if (!$can_push) {
throw new Exception(
pht('You do not have permission to push to this repository.'));
}
$lock = $this->lockRepository($repository);
try {
$response = $this->pushStrategy->processLandRequest(
$request,
$revision,
$repository);
} catch (Exception $e) {
$lock->unlock();
throw $e;
}
$lock->unlock();
$looksoon = new ConduitCall(
'diffusion.looksoon',
array(
'repositories' => array($repository->getPHID()),
));
$looksoon->setUser($request->getUser());
$looksoon->execute();
return $response;
}
private function lockRepository($repository) {
$lock_name = __CLASS__.':'.($repository->getPHID());
$lock = PhabricatorGlobalLock::newLock($lock_name);
$lock->lock();
return $lock;
}
}

View file

@ -1,186 +0,0 @@
<?php
final class DifferentialGitHubLandingStrategy
extends DifferentialLandingStrategy {
private $account;
private $provider;
public function processLandRequest(
AphrontRequest $request,
DifferentialRevision $revision,
PhabricatorRepository $repository) {
$viewer = $request->getUser();
$this->init($viewer, $repository);
$workspace = $this->getGitWorkspace($repository);
try {
id(new DifferentialHostedGitLandingStrategy())
->commitRevisionToWorkspace($revision, $workspace, $viewer);
} catch (Exception $e) {
throw new PhutilProxyException(pht('Failed to commit patch.'), $e);
}
try {
$this->pushWorkspaceRepository($repository, $workspace);
} catch (Exception $e) {
// If it's a permission problem, we know more than git.
$dialog = $this->verifyRemotePermissions($viewer, $revision, $repository);
if ($dialog) {
return $dialog;
}
// Else, throw what git said.
throw new PhutilProxyException(
pht('Failed to push changes upstream.'),
$e);
}
}
/**
* Returns PhabricatorActionView or an array of PhabricatorActionView or null.
*/
public function createMenuItem(
PhabricatorUser $viewer,
DifferentialRevision $revision,
PhabricatorRepository $repository) {
// TODO: This temporarily disables this action, because it doesn't work
// and is confusing to users. If you want to use it, comment out this line
// for now and we'll provide real support eventually.
return;
$vcs = $repository->getVersionControlSystem();
if ($vcs !== PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) {
return;
}
if ($repository->isHosted()) {
return;
}
try {
// These throw when failing.
$this->init($viewer, $repository);
$this->findGitHubRepo($repository);
} catch (Exception $e) {
return;
}
return $this->createActionView($revision, pht('Land to GitHub'))
->setIcon('fa-cloud-upload');
}
public function pushWorkspaceRepository(
PhabricatorRepository $repository,
ArcanistRepositoryAPI $workspace) {
$token = $this->getAccessToken();
$github_repo = $this->findGitHubRepo($repository);
$remote = urisprintf(
'https://%s:x-oauth-basic@%s/%s.git',
$token,
$this->provider->getProviderDomain(),
$github_repo);
$workspace->execxLocal(
'push %P HEAD:master',
new PhutilOpaqueEnvelope($remote));
}
private function init($viewer, $repository) {
$repo_uri = $repository->getRemoteURIObject();
$repo_domain = $repo_uri->getDomain();
$this->account = id(new PhabricatorExternalAccountQuery())
->setViewer($viewer)
->withUserPHIDs(array($viewer->getPHID()))
->withAccountTypes(array('github'))
->withAccountDomains(array($repo_domain))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$this->account) {
throw new Exception(
pht('No matching GitHub account found for %s.', $repo_domain));
}
$this->provider = PhabricatorAuthProvider::getEnabledProviderByKey(
$this->account->getProviderKey());
if (!$this->provider) {
throw new Exception(
pht('GitHub provider for %s is not enabled.', $repo_domain));
}
}
private function findGitHubRepo(PhabricatorRepository $repository) {
$repo_uri = $repository->getRemoteURIObject();
$repo_path = $repo_uri->getPath();
if (substr($repo_path, -4) == '.git') {
$repo_path = substr($repo_path, 0, -4);
}
$repo_path = ltrim($repo_path, '/');
return $repo_path;
}
private function getAccessToken() {
return $this->provider->getOAuthAccessToken($this->account);
}
private function verifyRemotePermissions($viewer, $revision, $repository) {
$github_user = $this->account->getUsername();
$github_repo = $this->findGitHubRepo($repository);
$uri = urisprintf(
'https://api.github.com/repos/%s/collaborators/%s',
$github_repo,
$github_user);
$uri = new PhutilURI($uri);
$uri->setQueryParam('access_token', $this->getAccessToken());
list($status, $body, $headers) = id(new HTTPSFuture($uri))->resolve();
// Likely status codes:
// 204 No Content: Has permissions. Token might be too weak.
// 404 Not Found: Not a collaborator.
// 401 Unauthorized: Token is bad/revoked.
$no_permission = ($status->getStatusCode() == 404);
if ($no_permission) {
throw new Exception(
pht(
"You don't have permission to push to this repository. ".
"Push permissions for this repository are managed on GitHub."));
}
$scopes = BaseHTTPFuture::getHeader($headers, 'X-OAuth-Scopes');
if (strpos($scopes, 'public_repo') === false) {
$provider_key = $this->provider->getProviderKey();
$refresh_token_uri = new PhutilURI("/auth/refresh/{$provider_key}/");
$refresh_token_uri->setQueryParam('scope', 'public_repo');
return id(new AphrontDialogView())
->setUser($viewer)
->setTitle(pht('Stronger token needed'))
->appendChild(pht(
'In order to complete this action, you need a '.
'stronger GitHub token.'))
->setSubmitURI($refresh_token_uri)
->addCancelButton('/D'.$revision->getId())
->setDisableWorkflowOnSubmit(true)
->addSubmitButton(pht('Refresh Account Link'));
}
}
}

View file

@ -1,127 +0,0 @@
<?php
final class DifferentialHostedGitLandingStrategy
extends DifferentialLandingStrategy {
public function processLandRequest(
AphrontRequest $request,
DifferentialRevision $revision,
PhabricatorRepository $repository) {
$viewer = $request->getUser();
$workspace = $this->getGitWorkspace($repository);
try {
$this->commitRevisionToWorkspace($revision, $workspace, $viewer);
} catch (Exception $e) {
throw new PhutilProxyException(
pht('Failed to commit patch.'),
$e);
}
try {
$this->pushWorkspaceRepository($repository, $workspace, $viewer);
} catch (Exception $e) {
throw new PhutilProxyException(
pht('Failed to push changes upstream.'),
$e);
}
}
public function commitRevisionToWorkspace(
DifferentialRevision $revision,
ArcanistRepositoryAPI $workspace,
PhabricatorUser $user) {
$diff_id = $revision->loadActiveDiff()->getID();
$call = new ConduitCall(
'differential.getrawdiff',
array(
'diffID' => $diff_id,
));
$call->setUser($user);
$raw_diff = $call->execute();
$missing_binary =
"\nindex "
."0000000000000000000000000000000000000000.."
."0000000000000000000000000000000000000000\n";
if (strpos($raw_diff, $missing_binary) !== false) {
throw new Exception(pht('Patch is missing content for a binary file'));
}
$future = $workspace->execFutureLocal('apply --index -');
$future->write($raw_diff);
$future->resolvex();
$workspace->reloadWorkingCopy();
$call = new ConduitCall(
'differential.getcommitmessage',
array(
'revision_id' => $revision->getID(),
));
$call->setUser($user);
$message = $call->execute();
$author = id(new PhabricatorUser())->loadOneWhere(
'phid = %s',
$revision->getAuthorPHID());
$author_string = sprintf(
'%s <%s>',
$author->getRealName(),
$author->loadPrimaryEmailAddress());
$author_date = $revision->getDateCreated();
$workspace->execxLocal(
'-c user.name=%s -c user.email=%s '.
'commit --date=%s --author=%s '.
'--message=%s',
// -c will set the 'committer'
$user->getRealName(),
$user->loadPrimaryEmailAddress(),
$author_date,
$author_string,
$message);
}
public function pushWorkspaceRepository(
PhabricatorRepository $repository,
ArcanistRepositoryAPI $workspace,
PhabricatorUser $user) {
$workspace->execxLocal('push origin HEAD:master');
}
public function createMenuItem(
PhabricatorUser $viewer,
DifferentialRevision $revision,
PhabricatorRepository $repository) {
$vcs = $repository->getVersionControlSystem();
if ($vcs !== PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) {
return;
}
if (!$repository->isHosted()) {
return;
}
if (!$repository->isWorkingCopyBare()) {
return;
}
// TODO: This temporarily disables this action, because it doesn't work
// and is confusing to users. If you want to use it, comment out this line
// for now and we'll provide real support eventually.
return;
return $this->createActionView(
$revision,
pht('Land to Hosted Repository'));
}
}

View file

@ -1,106 +0,0 @@
<?php
final class DifferentialHostedMercurialLandingStrategy
extends DifferentialLandingStrategy {
public function processLandRequest(
AphrontRequest $request,
DifferentialRevision $revision,
PhabricatorRepository $repository) {
$viewer = $request->getUser();
$workspace = $this->getMercurialWorkspace($repository);
try {
$this->commitRevisionToWorkspace($revision, $workspace, $viewer);
} catch (Exception $e) {
throw new PhutilProxyException(pht('Failed to commit patch.'), $e);
}
try {
$this->pushWorkspaceRepository($repository, $workspace, $viewer);
} catch (Exception $e) {
throw new PhutilProxyException(
pht('Failed to push changes upstream.'),
$e);
}
}
public function commitRevisionToWorkspace(
DifferentialRevision $revision,
ArcanistRepositoryAPI $workspace,
PhabricatorUser $user) {
$diff_id = $revision->loadActiveDiff()->getID();
$call = new ConduitCall(
'differential.getrawdiff',
array(
'diffID' => $diff_id,
));
$call->setUser($user);
$raw_diff = $call->execute();
$future = $workspace->execFutureLocal('patch --no-commit -');
$future->write($raw_diff);
$future->resolvex();
$workspace->reloadWorkingCopy();
$call = new ConduitCall(
'differential.getcommitmessage',
array(
'revision_id' => $revision->getID(),
));
$call->setUser($user);
$message = $call->execute();
$author = id(new PhabricatorUser())->loadOneWhere(
'phid = %s',
$revision->getAuthorPHID());
$author_string = sprintf(
'%s <%s>',
$author->getRealName(),
$author->loadPrimaryEmailAddress());
$author_date = $revision->getDateCreated();
$workspace->execxLocal(
'commit --date=%s --user=%s '.
'--message=%s',
$author_date.' 0',
$author_string,
$message);
}
public function pushWorkspaceRepository(
PhabricatorRepository $repository,
ArcanistRepositoryAPI $workspace,
PhabricatorUser $user) {
$workspace->execxLocal('push -b default');
}
public function createMenuItem(
PhabricatorUser $viewer,
DifferentialRevision $revision,
PhabricatorRepository $repository) {
$vcs = $repository->getVersionControlSystem();
if ($vcs !== PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL) {
return;
}
if (!$repository->isHosted()) {
return;
}
return $this->createActionView(
$revision,
pht('Land to Hosted Repository'));
}
}

View file

@ -1,81 +0,0 @@
<?php
/**
* This class adds a "Land this" button to revision view.
*/
final class DifferentialLandingActionMenuEventListener
extends PhabricatorEventListener {
public function register() {
$this->listen(PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS);
}
public function handleEvent(PhutilEvent $event) {
switch ($event->getType()) {
case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS:
$this->handleActionsEvent($event);
break;
}
}
private function handleActionsEvent(PhutilEvent $event) {
$object = $event->getValue('object');
if ($object instanceof DifferentialRevision) {
$this->renderRevisionAction($event);
}
}
private function renderRevisionAction(PhutilEvent $event) {
$viewer = $event->getUser();
if (!$this->canUseApplication($viewer)) {
return null;
}
$revision = $event->getValue('object');
$repository = $revision->getRepository();
if ($repository === null) {
return null;
}
if ($repository->canPerformAutomation()) {
$revision_id = $revision->getID();
$op = new DrydockLandRepositoryOperation();
$barrier = $op->getBarrierToLanding($viewer, $revision);
if ($barrier) {
$can_land = false;
} else {
$can_land = true;
}
$action = id(new PhabricatorActionView())
->setName(pht('Land Revision'))
->setIcon('fa-fighter-jet')
->setHref("/differential/revision/operation/{$revision_id}/")
->setWorkflow(true)
->setDisabled(!$can_land);
$this->addActionMenuItems($event, $action);
}
$strategies = id(new PhutilClassMapQuery())
->setAncestorClass('DifferentialLandingStrategy')
->execute();
foreach ($strategies as $strategy) {
$action = $strategy->createMenuItem($viewer, $revision, $repository);
if ($action == null) {
continue;
}
if ($strategy->isActionDisabled($viewer, $revision, $repository)) {
$action->setDisabled(true);
}
$this->addActionMenuItems($event, $action);
}
}
}

View file

@ -1,87 +0,0 @@
<?php
abstract class DifferentialLandingStrategy extends Phobject {
abstract public function processLandRequest(
AphrontRequest $request,
DifferentialRevision $revision,
PhabricatorRepository $repository);
/**
* @return PhabricatorActionView or null.
*/
abstract public function createMenuItem(
PhabricatorUser $viewer,
DifferentialRevision $revision,
PhabricatorRepository $repository);
/**
* @return PhabricatorActionView which can be attached to the revision view.
*/
protected function createActionView($revision, $name) {
$strategy = get_class($this);
$revision_id = $revision->getId();
return id(new PhabricatorActionView())
->setRenderAsForm(true)
->setWorkflow(true)
->setName($name)
->setHref("/differential/revision/land/{$revision_id}/{$strategy}/");
}
/**
* Check if this action should be disabled, and explain why.
*
* By default, this method checks for push permissions, and for the
* revision being Accepted.
*
* @return False for "not disabled"; human-readable text explaining why, if
* it is disabled.
*/
public function isActionDisabled(
PhabricatorUser $viewer,
DifferentialRevision $revision,
PhabricatorRepository $repository) {
$status = $revision->getStatus();
if ($status != ArcanistDifferentialRevisionStatus::ACCEPTED) {
return pht('Only Accepted revisions can be landed.');
}
if (!PhabricatorPolicyFilter::hasCapability(
$viewer,
$repository,
DiffusionPushCapability::CAPABILITY)) {
return pht('You do not have permissions to push to this repository.');
}
return false;
}
/**
* Might break if repository is not Git.
*/
protected function getGitWorkspace(PhabricatorRepository $repository) {
try {
return DifferentialGetWorkingCopy::getCleanGitWorkspace($repository);
} catch (Exception $e) {
throw new PhutilProxyException(
pht('Failed to allocate a workspace.'),
$e);
}
}
/**
* Might break if repository is not Mercurial.
*/
protected function getMercurialWorkspace(PhabricatorRepository $repository) {
try {
return DifferentialGetWorkingCopy::getCleanMercurialWorkspace(
$repository);
} catch (Exception $e) {
throw new PhutilProxyException(
pht('Failed to allocate a workspace.'),
$e);
}
}
}