Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/differential/controller/DifferentialRevisionOperationController.php b/src/applications/differential/controller/DifferentialRevisionOperationController.php
index 85f2a0b1b2..a5d5b5f98f 100644
--- a/src/applications/differential/controller/DifferentialRevisionOperationController.php
+++ b/src/applications/differential/controller/DifferentialRevisionOperationController.php
@@ -1,153 +1,67 @@
<?php
final class DifferentialRevisionOperationController
extends DifferentialController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$id = $request->getURIData('id');
$revision = id(new DifferentialRevisionQuery())
->withIDs(array($id))
->setViewer($viewer)
->needActiveDiffs(true)
->executeOne();
if (!$revision) {
return new Aphront404Response();
}
$detail_uri = "/D{$id}";
- $repository = $revision->getRepository();
- if (!$repository) {
- return $this->rejectOperation(
- $revision,
- pht('No Repository'),
- pht(
- 'This revision is not associated with a known repository. Only '.
- 'revisions associated with a tracked repository can be landed '.
- 'automatically.'));
- }
-
- if (!$repository->canPerformAutomation()) {
- return $this->rejectOperation(
- $revision,
- pht('No Repository Automation'),
- pht(
- 'The repository this revision is associated with ("%s") is not '.
- 'configured to support automation. Configure automation for the '.
- 'repository to enable revisions to be landed automatically.',
- $repository->getMonogram()));
- }
-
- // TODO: At some point we should allow installs to give "land reviewed
- // code" permission to more users than "push any commit", because it is
- // a much less powerful operation. For now, just require push so this
- // doesn't do anything users can't do on their own.
- $can_push = PhabricatorPolicyFilter::hasCapability(
- $viewer,
- $repository,
- DiffusionPushCapability::CAPABILITY);
- if (!$can_push) {
- return $this->rejectOperation(
- $revision,
- pht('Unable to Push'),
- pht(
- 'You do not have permission to push to the repository this '.
- 'revision is associated with ("%s"), so you can not land it.',
- $repository->getMonogram()));
- }
-
$op = new DrydockLandRepositoryOperation();
-
- // Check for other operations. Eventually this should probably be more
- // general (e.g., it's OK to land to multiple different branches
- // simultaneously) but just put this in as a sanity check for now.
- $other_operations = id(new DrydockRepositoryOperationQuery())
- ->setViewer($viewer)
- ->withObjectPHIDs(array($revision->getPHID()))
- ->withOperationTypes(
- array(
- $op->getOperationConstant(),
- ))
- ->withOperationStates(
- array(
- DrydockRepositoryOperation::STATE_WAIT,
- DrydockRepositoryOperation::STATE_WORK,
- DrydockRepositoryOperation::STATE_DONE,
- ))
- ->execute();
-
- if ($other_operations) {
- $any_done = false;
- foreach ($other_operations as $operation) {
- if ($operation->isDone()) {
- $any_done = true;
- break;
- }
- }
-
- if ($any_done) {
- return $this->rejectOperation(
- $revision,
- pht('Already Complete'),
- pht('This revision has already landed.'));
- } else {
- return $this->rejectOperation(
- $revision,
- pht('Already In Flight'),
- pht('This revision is already landing.'));
- }
+ $barrier = $op->getBarrierToLanding($viewer, $revision);
+ if ($barrier) {
+ return $this->newDialog()
+ ->setTitle($barrier['title'])
+ ->appendParagraph($barrier['body'])
+ ->addCancelButton($detail_uri);
}
if ($request->isFormPost()) {
// NOTE: The operation is locked to the current active diff, so if the
// revision is updated before the operation applies nothing sneaky
// occurs.
$diff = $revision->getActiveDiff();
+ $repository = $revision->getRepository();
$operation = DrydockRepositoryOperation::initializeNewOperation($op)
->setAuthorPHID($viewer->getPHID())
->setObjectPHID($revision->getPHID())
->setRepositoryPHID($repository->getPHID())
->setRepositoryTarget('branch:master')
->setProperty('differential.diffPHID', $diff->getPHID());
$operation->save();
$operation->scheduleUpdate();
return id(new AphrontRedirectResponse())
->setURI($detail_uri);
}
return $this->newDialog()
->setTitle(pht('Land Revision'))
->appendParagraph(
pht(
'In theory, this will do approximately what `arc land` would do. '.
'In practice, that is almost certainly not what it will actually '.
'do.'))
->appendParagraph(
pht(
'THIS FEATURE IS EXPERIMENTAL AND DANGEROUS! USE IT AT YOUR '.
'OWN RISK!'))
->addCancelButton($detail_uri)
->addSubmitButton(pht('Mutate Repository Unpredictably'));
}
- private function rejectOperation(
- DifferentialRevision $revision,
- $title,
- $body) {
-
- $id = $revision->getID();
- $detail_uri = "/D{$id}";
-
- return $this->newDialog()
- ->setTitle($title)
- ->appendParagraph($body)
- ->addCancelButton($detail_uri);
- }
-
}
diff --git a/src/applications/differential/landing/DifferentialLandingActionMenuEventListener.php b/src/applications/differential/landing/DifferentialLandingActionMenuEventListener.php
index 7dcf3f462d..2cf3eaa4e5 100644
--- a/src/applications/differential/landing/DifferentialLandingActionMenuEventListener.php
+++ b/src/applications/differential/landing/DifferentialLandingActionMenuEventListener.php
@@ -1,69 +1,81 @@
<?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) {
- if (!$this->canUseApplication($event->getUser())) {
+ $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())
- ->setWorkflow(true)
->setName(pht('Land Revision'))
->setIcon('fa-fighter-jet')
- ->setHref("/differential/revision/operation/{$revision_id}/");
+ ->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) {
- $viewer = $event->getUser();
$action = $strategy->createMenuItem($viewer, $revision, $repository);
if ($action == null) {
continue;
}
if ($strategy->isActionDisabled($viewer, $revision, $repository)) {
$action->setDisabled(true);
}
$this->addActionMenuItems($event, $action);
}
}
}
diff --git a/src/applications/drydock/operation/DrydockLandRepositoryOperation.php b/src/applications/drydock/operation/DrydockLandRepositoryOperation.php
index 00b5c71181..6da4c2ae5d 100644
--- a/src/applications/drydock/operation/DrydockLandRepositoryOperation.php
+++ b/src/applications/drydock/operation/DrydockLandRepositoryOperation.php
@@ -1,192 +1,289 @@
<?php
final class DrydockLandRepositoryOperation
extends DrydockRepositoryOperationType {
const OPCONST = 'land';
public function getOperationDescription(
DrydockRepositoryOperation $operation,
PhabricatorUser $viewer) {
return pht('Land Revision');
}
public function getOperationCurrentStatus(
DrydockRepositoryOperation $operation,
PhabricatorUser $viewer) {
$target = $operation->getRepositoryTarget();
$repository = $operation->getRepository();
switch ($operation->getOperationState()) {
case DrydockRepositoryOperation::STATE_WAIT:
return pht(
'Waiting to land revision into %s on %s...',
$repository->getMonogram(),
$target);
case DrydockRepositoryOperation::STATE_WORK:
return pht(
'Landing revision into %s on %s...',
$repository->getMonogram(),
$target);
case DrydockRepositoryOperation::STATE_DONE:
return pht(
'Revision landed into %s.',
$repository->getMonogram());
}
}
public function getWorkingCopyMerges(DrydockRepositoryOperation $operation) {
$repository = $operation->getRepository();
$merges = array();
$object = $operation->getObject();
if ($object instanceof DifferentialRevision) {
$diff = $this->loadDiff($operation);
$merges[] = array(
'src.uri' => $repository->getStagingURI(),
'src.ref' => $diff->getStagingRef(),
);
} else {
throw new Exception(
pht(
'Invalid or unknown object ("%s") for land operation, expected '.
'Differential Revision.',
$operation->getObjectPHID()));
}
return $merges;
}
public function applyOperation(
DrydockRepositoryOperation $operation,
DrydockInterface $interface) {
$viewer = $this->getViewer();
$repository = $operation->getRepository();
$cmd = array();
$arg = array();
$object = $operation->getObject();
if ($object instanceof DifferentialRevision) {
$revision = $object;
$diff = $this->loadDiff($operation);
$dict = $diff->getDiffAuthorshipDict();
$author_name = idx($dict, 'authorName');
$author_email = idx($dict, 'authorEmail');
$api_method = 'differential.getcommitmessage';
$api_params = array(
'revision_id' => $revision->getID(),
);
$commit_message = id(new ConduitCall($api_method, $api_params))
->setUser($viewer)
->execute();
} else {
throw new Exception(
pht(
'Invalid or unknown object ("%s") for land operation, expected '.
'Differential Revision.',
$operation->getObjectPHID()));
}
$target = $operation->getRepositoryTarget();
list($type, $name) = explode(':', $target, 2);
switch ($type) {
case 'branch':
$push_dst = 'refs/heads/'.$name;
break;
default:
throw new Exception(
pht(
'Unknown repository operation target type "%s" (in target "%s").',
$type,
$target));
}
$committer_info = $this->getCommitterInfo($operation);
// NOTE: We're doing this commit with "-F -" so we don't run into trouble
// with enormous commit messages which might otherwise exceed the maximum
// size of a command.
$future = $interface->getExecFuture(
'git -c user.name=%s -c user.email=%s commit --author %s -F - --',
$committer_info['name'],
$committer_info['email'],
"{$author_name} <{$author_email}>");
$future
->write($commit_message)
->resolvex();
$interface->execx(
'git push origin -- %s:%s',
'HEAD',
$push_dst);
}
private function getCommitterInfo(DrydockRepositoryOperation $operation) {
$viewer = $this->getViewer();
$committer_name = null;
$author_phid = $operation->getAuthorPHID();
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs(array($author_phid))
->executeOne();
if ($object) {
if ($object instanceof PhabricatorUser) {
$committer_name = $object->getUsername();
}
}
if (!strlen($committer_name)) {
$committer_name = pht('autocommitter');
}
// TODO: Probably let users choose a VCS email address in settings. For
// now just make something up so we don't leak anyone's stuff.
return array(
'name' => $committer_name,
'email' => 'autocommitter@example.com',
);
}
private function loadDiff(DrydockRepositoryOperation $operation) {
$viewer = $this->getViewer();
$revision = $operation->getObject();
$diff_phid = $operation->getProperty('differential.diffPHID');
$diff = id(new DifferentialDiffQuery())
->setViewer($viewer)
->withPHIDs(array($diff_phid))
->executeOne();
if (!$diff) {
throw new Exception(
pht(
'Unable to load diff "%s".',
$diff_phid));
}
$diff_revid = $diff->getRevisionID();
$revision_id = $revision->getID();
if ($diff_revid != $revision_id) {
throw new Exception(
pht(
'Diff ("%s") has wrong revision ID ("%s", expected "%s").',
$diff_phid,
$diff_revid,
$revision_id));
}
return $diff;
}
+ public function getBarrierToLanding(
+ PhabricatorUser $viewer,
+ DifferentialRevision $revision) {
+
+ $repository = $revision->getRepository();
+ if (!$repository) {
+ return array(
+ 'title' => pht('No Repository'),
+ 'body' => pht(
+ 'This revision is not associated with a known repository. Only '.
+ 'revisions associated with a tracked repository can be landed '.
+ 'automatically.'),
+ );
+ }
+
+ if (!$repository->canPerformAutomation()) {
+ return array(
+ 'title' => pht('No Repository Automation'),
+ 'body' => pht(
+ 'The repository this revision is associated with ("%s") is not '.
+ 'configured to support automation. Configure automation for the '.
+ 'repository to enable revisions to be landed automatically.',
+ $repository->getMonogram()),
+ );
+ }
+
+ // TODO: At some point we should allow installs to give "land reviewed
+ // code" permission to more users than "push any commit", because it is
+ // a much less powerful operation. For now, just require push so this
+ // doesn't do anything users can't do on their own.
+ $can_push = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $repository,
+ DiffusionPushCapability::CAPABILITY);
+ if (!$can_push) {
+ return array(
+ 'title' => pht('Unable to Push'),
+ 'body' => pht(
+ 'You do not have permission to push to the repository this '.
+ 'revision is associated with ("%s"), so you can not land it.',
+ $repository->getMonogram()),
+ );
+ }
+
+ $status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED;
+ if ($revision->getStatus() !== $status_accepted) {
+ return array(
+ 'title' => pht('Revision Not Accepted'),
+ 'body' => pht(
+ 'This revision is still under review. Only revisions which have '.
+ 'been accepted may land.'),
+ );
+ }
+
+ // Check for other operations. Eventually this should probably be more
+ // general (e.g., it's OK to land to multiple different branches
+ // simultaneously) but just put this in as a sanity check for now.
+ $other_operations = id(new DrydockRepositoryOperationQuery())
+ ->setViewer($viewer)
+ ->withObjectPHIDs(array($revision->getPHID()))
+ ->withOperationTypes(
+ array(
+ $this->getOperationConstant(),
+ ))
+ ->withOperationStates(
+ array(
+ DrydockRepositoryOperation::STATE_WAIT,
+ DrydockRepositoryOperation::STATE_WORK,
+ DrydockRepositoryOperation::STATE_DONE,
+ ))
+ ->execute();
+
+ if ($other_operations) {
+ $any_done = false;
+ foreach ($other_operations as $operation) {
+ if ($operation->isDone()) {
+ $any_done = true;
+ break;
+ }
+ }
+
+ if ($any_done) {
+ return array(
+ 'title' => pht('Already Complete'),
+ 'body' => pht('This revision has already landed.'),
+ );
+ } else {
+ return array(
+ 'title' => pht('Already In Flight'),
+ 'body' => pht('This revision is already landing.'),
+ );
+ }
+ }
+
+ return null;
+ }
+
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Mar 17, 2:51 AM (2 h, 50 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
964338
Default Alt Text
(17 KB)

Event Timeline