1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-31 17:08:22 +01:00

Use TransactionEditor when closing revisions in response to commits

Summary: Ref T2222. When we discover a commit associated with a revision, close it using modern transactions.

Test Plan: {F123848}

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2222

Differential Revision: https://secure.phabricator.com/D8441
This commit is contained in:
epriestley 2014-03-07 17:43:58 -08:00
parent 458a28eed3
commit b04f706c0a
3 changed files with 134 additions and 55 deletions

View file

@ -4,6 +4,26 @@ final class DifferentialTransactionEditor
extends PhabricatorApplicationTransactionEditor { extends PhabricatorApplicationTransactionEditor {
private $heraldEmailPHIDs; private $heraldEmailPHIDs;
private $changedPriorToCommitURI;
private $isCloseByCommit;
public function setIsCloseByCommit($is_close_by_commit) {
$this->isCloseByCommit = $is_close_by_commit;
return $this;
}
public function getIsCloseByCommit() {
return $this->isCloseByCommit;
}
public function setChangedPriorToCommitURI($uri) {
$this->changedPriorToCommitURI = $uri;
return $this;
}
public function getChangedPriorToCommitURI() {
return $this->changedPriorToCommitURI;
}
public function getTransactionTypes() { public function getTransactionTypes() {
$types = parent::getTransactionTypes(); $types = parent::getTransactionTypes();
@ -158,7 +178,9 @@ final class DifferentialTransactionEditor
case PhabricatorTransactions::TYPE_EDGE: case PhabricatorTransactions::TYPE_EDGE:
return; return;
case DifferentialTransaction::TYPE_UPDATE: case DifferentialTransaction::TYPE_UPDATE:
$object->setStatus($status_review); if (!$this->getIsCloseByCommit()) {
$object->setStatus($status_review);
}
// TODO: Update the `diffPHID` once we add that. // TODO: Update the `diffPHID` once we add that.
return; return;
case DifferentialTransaction::TYPE_ACTION: case DifferentialTransaction::TYPE_ACTION:
@ -209,6 +231,12 @@ final class DifferentialTransactionEditor
$results = parent::expandTransaction($object, $xaction); $results = parent::expandTransaction($object, $xaction);
switch ($xaction->getTransactionType()) { switch ($xaction->getTransactionType()) {
case DifferentialTransaction::TYPE_UPDATE: case DifferentialTransaction::TYPE_UPDATE:
if ($this->getIsCloseByCommit()) {
// Don't bother with any of this if this update is a side effect of
// commit detection.
break;
}
$new_accept = DifferentialReviewerStatus::STATUS_ACCEPTED; $new_accept = DifferentialReviewerStatus::STATUS_ACCEPTED;
$new_reject = DifferentialReviewerStatus::STATUS_REJECTED; $new_reject = DifferentialReviewerStatus::STATUS_REJECTED;
$old_accept = DifferentialReviewerStatus::STATUS_ACCEPTED_OLDER; $old_accept = DifferentialReviewerStatus::STATUS_ACCEPTED_OLDER;
@ -784,19 +812,22 @@ final class DifferentialTransactionEditor
break; break;
case DifferentialAction::ACTION_CLOSE: case DifferentialAction::ACTION_CLOSE:
// We force revisions closed when we discover a corresponding commit.
// In this case, revisions are allowed to transition to closed from
// any state. This is an automated action taken by the daemons.
// TODO: Permit the daemons to take this action in all cases. if (!$this->getIsCloseByCommit()) {
if (!$actor_is_author && !$always_allow_close) {
return pht(
"You can not close this revision because you do not own it. To ".
"close a revision, you must be its owner.");
}
if (!$actor_is_author && !$always_allow_close) { if ($revision_status != $status_accepted) {
return pht( return pht(
"You can not close this revision because you do not own it. To ". "You can not close this revision because it has not been ".
"close a revision, you must be its owner."); "accepted. You can only close accepted revisions.");
} }
if ($revision_status != $status_accepted) {
return pht(
"You can not close this revision because it has not been ".
"accepted. You can only close accepted revisions.");
} }
break; break;
} }
@ -909,6 +940,13 @@ final class DifferentialTransactionEditor
} }
} }
$changed_uri = $this->getChangedPriorToCommitURI();
if ($changed_uri) {
$body->addTextSection(
pht('CHANGED PRIOR TO COMMIT'),
$changed_uri);
}
if ($inlines) { if ($inlines) {
$body->addTextSection( $body->addTextSection(
pht('INLINE COMMENTS'), pht('INLINE COMMENTS'),
@ -1099,7 +1137,9 @@ final class DifferentialTransactionEditor
foreach ($xactions as $xaction) { foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) { switch ($xaction->getTransactionType()) {
case DifferentialTransaction::TYPE_UPDATE: case DifferentialTransaction::TYPE_UPDATE:
return true; if (!$this->getIsCloseByCommit()) {
return true;
}
} }
} }

View file

@ -12,6 +12,7 @@ final class PhabricatorContentSource {
const SOURCE_CONSOLE = 'console'; const SOURCE_CONSOLE = 'console';
const SOURCE_HERALD = 'herald'; const SOURCE_HERALD = 'herald';
const SOURCE_LEGACY = 'legacy'; const SOURCE_LEGACY = 'legacy';
const SOURCE_DAEMON = 'daemon';
private $source; private $source;
private $params = array(); private $params = array();
@ -72,6 +73,7 @@ final class PhabricatorContentSource {
self::SOURCE_CONSOLE => pht('Console'), self::SOURCE_CONSOLE => pht('Console'),
self::SOURCE_LEGACY => pht('Legacy'), self::SOURCE_LEGACY => pht('Legacy'),
self::SOURCE_HERALD => pht('Herald'), self::SOURCE_HERALD => pht('Herald'),
self::SOURCE_DAEMON => pht('Daemons'),
self::SOURCE_UNKNOWN => pht('Old World'), self::SOURCE_UNKNOWN => pht('Old World'),
); );
} }

View file

@ -78,16 +78,18 @@ abstract class PhabricatorRepositoryCommitMessageParserWorker
$should_autoclose = $repository->shouldAutocloseCommit($commit, $data); $should_autoclose = $repository->shouldAutocloseCommit($commit, $data);
if ($revision_id) { if ($revision_id) {
$lock = PhabricatorGlobalLock::newLock(get_class($this).':'.$revision_id);
$lock->lock(5 * 60);
// TODO: Check if a more restrictive viewer could be set here // TODO: Check if a more restrictive viewer could be set here
$revision = id(new DifferentialRevisionQuery()) $revision_query = id(new DifferentialRevisionQuery())
->withIDs(array($revision_id)) ->withIDs(array($revision_id))
->setViewer(PhabricatorUser::getOmnipotentUser()) ->setViewer(PhabricatorUser::getOmnipotentUser())
->needRelationships(true)
->needReviewerStatus(true) ->needReviewerStatus(true)
->executeOne(); ->needActiveDiffs(true);
// TODO: Remove this once we swap to new CustomFields. This is only
// required by the old FieldSpecifications, lower on in this worker.
$revision_query->needRelationships(true);
$revision = $revision_query->executeOne();
if ($revision) { if ($revision) {
$commit_drev = PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV; $commit_drev = PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV;
@ -112,29 +114,10 @@ abstract class PhabricatorRepositoryCommitMessageParserWorker
$committer_phid, $committer_phid,
$author_phid, $author_phid,
$revision->getAuthorPHID()); $revision->getAuthorPHID());
$actor = id(new PhabricatorUser()) $actor = id(new PhabricatorUser())
->loadOneWhere('phid = %s', $actor_phid); ->loadOneWhere('phid = %s', $actor_phid);
$diff = $this->attachToRevision($revision, $actor_phid);
$editor = new DifferentialCommentEditor(
$revision,
DifferentialAction::ACTION_CLOSE);
$editor->setActor($actor);
$editor->setIsDaemonWorkflow(true);
$vs_diff = $this->loadChangedByCommit($diff);
if ($vs_diff) {
$data->setCommitDetail('vsDiff', $vs_diff->getID());
$changed_by_commit = PhabricatorEnv::getProductionURI(
'/D'.$revision->getID().
'?vs='.$vs_diff->getID().
'&id='.$diff->getID().
'#toc');
$editor->setChangedByCommit($changed_by_commit);
}
$commit_name = $repository->formatCommitName( $commit_name = $repository->formatCommitName(
$commit->getCommitIdentifier()); $commit->getCommitIdentifier());
@ -148,24 +131,77 @@ abstract class PhabricatorRepositoryCommitMessageParserWorker
$data->getAuthorName(), $data->getAuthorName(),
$actor); $actor);
$info = array();
$info[] = "authored by {$author_name}";
if ($committer_name && ($committer_name != $author_name)) { if ($committer_name && ($committer_name != $author_name)) {
$info[] = "committed by {$committer_name}"; $message = pht(
'Closed by commit %s (authored by %s, committed by %s).',
$commit_name,
$author_name,
$committer_name);
} else {
$message = pht(
'Closed by commit %s (authored by %s).',
$commit_name,
$author_name);
} }
$info = implode(', ', $info);
$editor $diff = $this->generateFinalDiff($revision, $actor_phid);
->setMessage("Closed by commit {$commit_name} ({$info}).")
->save(); $vs_diff = $this->loadChangedByCommit($revision, $diff);
$changed_uri = null;
if ($vs_diff) {
$data->setCommitDetail('vsDiff', $vs_diff->getID());
$changed_uri = PhabricatorEnv::getProductionURI(
'/D'.$revision->getID().
'?vs='.$vs_diff->getID().
'&id='.$diff->getID().
'#toc');
}
$xactions = array();
$xactions[] = id(new DifferentialTransaction())
->setTransactionType(DifferentialTransaction::TYPE_ACTION)
->setNewValue(DifferentialAction::ACTION_CLOSE);
$xactions[] = id(new DifferentialTransaction())
->setTransactionType(DifferentialTransaction::TYPE_UPDATE)
->setIgnoreOnNoEffect(true)
->setNewValue($diff->getPHID());
$xactions[] = id(new DifferentialTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->setIgnoreOnNoEffect(true)
->attachComment(
id(new DifferentialTransactionComment())
->setContent($message));
$content_source = PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_DAEMON,
array());
$editor = id(new DifferentialTransactionEditor())
->setActor($actor)
->setContinueOnMissingFields(true)
->setContentSource($content_source)
->setChangedPriorToCommitURI($changed_uri)
->setIsCloseByCommit(true);
try {
$editor->applyTransactions($revision, $xactions);
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
// NOTE: We've marked transactions other than the CLOSE transaction
// as ignored when they don't have an effect, so this means that we
// lost a race to close the revision. That's perfectly fine, we can
// just continue normally.
}
} }
} }
$lock->unlock();
} }
if ($should_autoclose) { if ($should_autoclose) {
// TODO: When this is moved to CustomFields, remove the additional
// call above in query construction.
$fields = DifferentialFieldSelector::newSelector() $fields = DifferentialFieldSelector::newSelector()
->getFieldSpecifications(); ->getFieldSpecifications();
foreach ($fields as $key => $field) { foreach ($fields as $key => $field) {
@ -200,7 +236,7 @@ abstract class PhabricatorRepositoryCommitMessageParserWorker
return '@'.$handle->getName(); return '@'.$handle->getName();
} }
private function attachToRevision( private function generateFinalDiff(
DifferentialRevision $revision, DifferentialRevision $revision,
$actor_phid) { $actor_phid) {
@ -232,7 +268,7 @@ abstract class PhabricatorRepositoryCommitMessageParserWorker
} }
$diff = DifferentialDiff::newFromRawChanges($changes) $diff = DifferentialDiff::newFromRawChanges($changes)
->setRevisionID($revision->getID()) ->setRepositoryPHID($this->repository->getPHID())
->setAuthorPHID($actor_phid) ->setAuthorPHID($actor_phid)
->setCreationMethod('commit') ->setCreationMethod('commit')
->setSourceControlSystem($this->repository->getVersionControlSystem()) ->setSourceControlSystem($this->repository->getVersionControlSystem())
@ -265,18 +301,19 @@ abstract class PhabricatorRepositoryCommitMessageParserWorker
// TODO: Attach binary files. // TODO: Attach binary files.
$revision->setLineCount($diff->getLineCount());
return $diff->save(); return $diff->save();
} }
private function loadChangedByCommit(DifferentialDiff $diff) { private function loadChangedByCommit(
DifferentialRevision $revision,
DifferentialDiff $diff) {
$repository = $this->repository; $repository = $this->repository;
$vs_changesets = array(); $vs_changesets = array();
$vs_diff = id(new DifferentialDiff())->loadOneWhere( $vs_diff = id(new DifferentialDiff())->loadOneWhere(
'revisionID = %d AND creationMethod != %s ORDER BY id DESC LIMIT 1', 'revisionID = %d AND creationMethod != %s ORDER BY id DESC LIMIT 1',
$diff->getRevisionID(), $revision->getID(),
'commit'); 'commit');
foreach ($vs_diff->loadChangesets() as $changeset) { foreach ($vs_diff->loadChangesets() as $changeset) {
$path = $changeset->getAbsoluteRepositoryPath($repository, $vs_diff); $path = $changeset->getAbsoluteRepositoryPath($repository, $vs_diff);