diff --git a/src/applications/diffusion/herald/DiffusionCommitRevisionAcceptedHeraldField.php b/src/applications/diffusion/herald/DiffusionCommitRevisionAcceptedHeraldField.php index 810b58f2a1..a125b580eb 100644 --- a/src/applications/diffusion/herald/DiffusionCommitRevisionAcceptedHeraldField.php +++ b/src/applications/diffusion/herald/DiffusionCommitRevisionAcceptedHeraldField.php @@ -1,61 +1,40 @@ getAdapter()->loadDifferentialRevision(); if (!$revision) { return null; } - $status = $revision->getStatus(); - - switch ($status) { - case ArcanistDifferentialRevisionStatus::ACCEPTED: - return $revision->getPHID(); - case ArcanistDifferentialRevisionStatus::CLOSED: - if ($revision->hasRevisionProperty( - DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED)) { - - if ($revision->getProperty( - DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED)) { - return $revision->getPHID(); - } else { - return null; - } - } else { - // continue on to old-style precommitRevisionStatus - break; - } - default: - return null; + if ($revision->isAccepted()) { + return $revision->getPHID(); } - $data = $object->getCommitData(); - $status = $data->getCommitDetail('precommitRevisionStatus'); - - switch ($status) { - case ArcanistDifferentialRevisionStatus::ACCEPTED: - case ArcanistDifferentialRevisionStatus::CLOSED: + $was_accepted = DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED; + if ($revision->isPublished()) { + if ($revision->getProperty($was_accepted)) { return $revision->getPHID(); + } } return null; } protected function getHeraldFieldStandardType() { return self::STANDARD_PHID_BOOL; } } diff --git a/src/applications/diffusion/herald/DiffusionPreCommitContentRevisionAcceptedHeraldField.php b/src/applications/diffusion/herald/DiffusionPreCommitContentRevisionAcceptedHeraldField.php index e19878e627..63dac6baa6 100644 --- a/src/applications/diffusion/herald/DiffusionPreCommitContentRevisionAcceptedHeraldField.php +++ b/src/applications/diffusion/herald/DiffusionPreCommitContentRevisionAcceptedHeraldField.php @@ -1,42 +1,40 @@ getAdapter()->getRevision(); - if (!$revision) { return null; } - switch ($revision->getStatus()) { - case ArcanistDifferentialRevisionStatus::ACCEPTED: - return $revision->getPHID(); - case ArcanistDifferentialRevisionStatus::CLOSED: - if ($revision->getProperty( - DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED)) { + if ($revision->isAccepted()) { + return $revision->getPHID(); + } - return $revision->getPHID(); - } - break; + $was_accepted = DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED; + if ($revision->isPublished()) { + if ($revision->getProperty($was_accepted)) { + return $revision->getPHID(); + } } return null; } protected function getHeraldFieldStandardType() { return self::STANDARD_PHID_BOOL; } } diff --git a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php index 0d0ff28d95..fe8abd5925 100644 --- a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php +++ b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php @@ -1,417 +1,412 @@ shouldSkipImportStep()) { $viewer = PhabricatorUser::getOmnipotentUser(); $refs_raw = DiffusionQuery::callConduitWithDiffusionRequest( $viewer, DiffusionRequest::newFromDictionary( array( 'repository' => $repository, 'user' => $viewer, )), 'diffusion.querycommits', array( 'repositoryPHID' => $repository->getPHID(), 'phids' => array($commit->getPHID()), 'bypassCache' => true, 'needMessages' => true, )); if (empty($refs_raw['data'])) { throw new Exception( pht( 'Unable to retrieve details for commit "%s"!', $commit->getPHID())); } $ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data'])); $this->updateCommitData($ref); } if ($this->shouldQueueFollowupTasks()) { $this->queueTask( $this->getFollowupTaskClass(), array( 'commitID' => $commit->getID(), ), array( // We queue followup tasks at default priority so that the queue // finishes work it has started before starting more work. If // followups are queued at the same priority level, we do all // message parses first, then all change parses, etc. This makes // progress uneven. See T11677 for discussion. 'priority' => PhabricatorWorker::PRIORITY_DEFAULT, )); } } final protected function updateCommitData(DiffusionCommitRef $ref) { $commit = $this->commit; $author = $ref->getAuthor(); $message = $ref->getMessage(); $committer = $ref->getCommitter(); $hashes = $ref->getHashes(); $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere( 'commitID = %d', $commit->getID()); if (!$data) { $data = new PhabricatorRepositoryCommitData(); } $data->setCommitID($commit->getID()); $data->setAuthorName(id(new PhutilUTF8StringTruncator()) ->setMaximumBytes(255) ->truncateString((string)$author)); $data->setCommitDetail('authorEpoch', $ref->getAuthorEpoch()); $data->setCommitDetail('authorName', $ref->getAuthorName()); $data->setCommitDetail('authorEmail', $ref->getAuthorEmail()); $data->setCommitDetail( 'authorPHID', $this->resolveUserPHID($commit, $author)); $data->setCommitMessage($message); if (strlen($committer)) { $data->setCommitDetail('committer', $committer); $data->setCommitDetail('committerName', $ref->getCommitterName()); $data->setCommitDetail('committerEmail', $ref->getCommitterEmail()); $data->setCommitDetail( 'committerPHID', $this->resolveUserPHID($commit, $committer)); } $repository = $this->repository; $author_phid = $data->getCommitDetail('authorPHID'); $committer_phid = $data->getCommitDetail('committerPHID'); $user = new PhabricatorUser(); if ($author_phid) { $user = $user->loadOneWhere( 'phid = %s', $author_phid); } $differential_app = 'PhabricatorDifferentialApplication'; $revision_id = null; $low_level_query = null; if (PhabricatorApplication::isClassInstalled($differential_app)) { $low_level_query = id(new DiffusionLowLevelCommitFieldsQuery()) ->setRepository($repository) ->withCommitRef($ref); $field_values = $low_level_query->execute(); $revision_id = idx($field_values, 'revisionID'); if (!empty($field_values['reviewedByPHIDs'])) { $data->setCommitDetail( 'reviewerPHID', reset($field_values['reviewedByPHIDs'])); } $data->setCommitDetail('differential.revisionID', $revision_id); } if ($author_phid != $commit->getAuthorPHID()) { $commit->setAuthorPHID($author_phid); } $commit->setSummary($data->getSummary()); $commit->save(); // Figure out if we're going to try to "autoclose" related objects (e.g., // close linked tasks and related revisions) and, if not, record why we // aren't. Autoclose can be disabled for various reasons at the repository // or commit levels. $force_autoclose = idx($this->getTaskData(), 'forceAutoclose', false); if ($force_autoclose) { $autoclose_reason = PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED; } else { $autoclose_reason = $repository->shouldSkipAutocloseCommit($commit); } $data->setCommitDetail('autocloseReason', $autoclose_reason); $should_autoclose = $force_autoclose || $repository->shouldAutocloseCommit($commit); // When updating related objects, we'll act under an omnipotent user to // ensure we can see them, but take actions as either the committer or // author (if we recognize their accounts) or the Diffusion application // (if we do not). $actor = PhabricatorUser::getOmnipotentUser(); $acting_as_phid = nonempty( $committer_phid, $author_phid, id(new PhabricatorDiffusionApplication())->getPHID()); $acting_user = $this->loadActingUser($actor, $acting_as_phid); $conn_w = id(new DifferentialRevision())->establishConnection('w'); // NOTE: The `differential_commit` table has a unique ID on `commitPHID`, // preventing more than one revision from being associated with a commit. // Generally this is good and desirable, but with the advent of hash // tracking we may end up in a situation where we match several different // revisions. We just kind of ignore this and pick one, we might want to // revisit this and do something differently. (If we match several revisions // someone probably did something very silly, though.) $revision = null; if ($revision_id) { $revision_query = id(new DifferentialRevisionQuery()) ->withIDs(array($revision_id)) ->setViewer($actor) ->needReviewers(true) ->needActiveDiffs(true); $revision = $revision_query->executeOne(); if ($revision) { - if (!$data->getCommitDetail('precommitRevisionStatus')) { - $data->setCommitDetail( - 'precommitRevisionStatus', - $revision->getStatus()); - } $commit_drev = DiffusionCommitHasRevisionEdgeType::EDGECONST; id(new PhabricatorEdgeEditor()) ->addEdge($commit->getPHID(), $commit_drev, $revision->getPHID()) ->save(); queryfx( $conn_w, 'INSERT IGNORE INTO %T (revisionID, commitPHID) VALUES (%d, %s)', DifferentialRevision::TABLE_COMMIT, $revision->getID(), $commit->getPHID()); $should_close = !$revision->isPublished() && $should_autoclose; if ($should_close) { $type_close = DifferentialRevisionCloseTransaction::TRANSACTIONTYPE; $commit_close_xaction = id(new DifferentialTransaction()) ->setTransactionType($type_close) ->setNewValue(true) ->setMetadataValue('isCommitClose', true); $commit_close_xaction->setMetadataValue( 'commitPHID', $commit->getPHID()); $commit_close_xaction->setMetadataValue( 'committerPHID', $committer_phid); $commit_close_xaction->setMetadataValue( 'committerName', $data->getCommitDetail('committer')); $commit_close_xaction->setMetadataValue( 'authorPHID', $author_phid); $commit_close_xaction->setMetadataValue( 'authorName', $data->getAuthorName()); if ($low_level_query) { $commit_close_xaction->setMetadataValue( 'revisionMatchData', $low_level_query->getRevisionMatchData()); $data->setCommitDetail( 'revisionMatchData', $low_level_query->getRevisionMatchData()); } $extraction_engine = id(new DifferentialDiffExtractionEngine()) ->setViewer($actor) ->setAuthorPHID($acting_as_phid); $content_source = $this->newContentSource(); $update_data = $extraction_engine->updateRevisionWithCommit( $revision, $commit, array( $commit_close_xaction, ), $content_source); foreach ($update_data as $key => $value) { $data->setCommitDetail($key, $value); } } } } if ($should_autoclose) { $this->closeTasks( $actor, $acting_as_phid, $repository, $commit, $message, $acting_user); } $data->save(); $commit->writeImportStatusFlag( PhabricatorRepositoryCommit::IMPORTED_MESSAGE); } private function resolveUserPHID( PhabricatorRepositoryCommit $commit, $user_name) { return id(new DiffusionResolveUserQuery()) ->withCommit($commit) ->withName($user_name) ->execute(); } private function closeTasks( PhabricatorUser $actor, $acting_as, PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit, $message, PhabricatorUser $acting_user = null) { // If we we were able to identify an author for the commit, we try to act // as that user when loading tasks marked with "Fixes Txxx". This prevents // mistakes where a user accidentally writes the wrong task IDs and affects // tasks they can't see (and thus can't undo the status changes for). // This is just a guard rail, not a security measure. An attacker can still // forge another user's identity trivially by forging author or committer // emails. We also let commits with unrecognized authors act on any task to // make behavior less confusing for new installs. if (!$acting_user) { $acting_user = $actor; } $maniphest = 'PhabricatorManiphestApplication'; if (!PhabricatorApplication::isClassInstalled($maniphest)) { return; } $prefixes = ManiphestTaskStatus::getStatusPrefixMap(); $suffixes = ManiphestTaskStatus::getStatusSuffixMap(); $matches = id(new ManiphestCustomFieldStatusParser()) ->parseCorpus($message); $task_statuses = array(); foreach ($matches as $match) { $prefix = phutil_utf8_strtolower($match['prefix']); $suffix = phutil_utf8_strtolower($match['suffix']); $status = idx($suffixes, $suffix); if (!$status) { $status = idx($prefixes, $prefix); } foreach ($match['monograms'] as $task_monogram) { $task_id = (int)trim($task_monogram, 'tT'); $task_statuses[$task_id] = $status; } } if (!$task_statuses) { return; } $tasks = id(new ManiphestTaskQuery()) ->setViewer($acting_user) ->withIDs(array_keys($task_statuses)) ->needProjectPHIDs(true) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->execute(); foreach ($tasks as $task_id => $task) { $xactions = array(); $edge_type = ManiphestTaskHasCommitEdgeType::EDGECONST; $edge_xaction = id(new ManiphestTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) ->setMetadataValue('edge:type', $edge_type) ->setNewValue( array( '+' => array( $commit->getPHID() => $commit->getPHID(), ), )); $status = $task_statuses[$task_id]; if ($status) { if ($task->getStatus() != $status) { $xactions[] = id(new ManiphestTransaction()) ->setTransactionType( ManiphestTaskStatusTransaction::TRANSACTIONTYPE) ->setMetadataValue('commitPHID', $commit->getPHID()) ->setNewValue($status); $edge_xaction->setMetadataValue('commitPHID', $commit->getPHID()); } } $xactions[] = $edge_xaction; $content_source = $this->newContentSource(); $editor = id(new ManiphestTransactionEditor()) ->setActor($actor) ->setActingAsPHID($acting_as) ->setContinueOnNoEffect(true) ->setContinueOnMissingFields(true) ->setUnmentionablePHIDMap( array($commit->getPHID() => $commit->getPHID())) ->setContentSource($content_source); $editor->applyTransactions($task, $xactions); } } private function loadActingUser(PhabricatorUser $viewer, $user_phid) { if (!$user_phid) { return null; } $user_type = PhabricatorPeopleUserPHIDType::TYPECONST; if (phid_get_type($user_phid) != $user_type) { return null; } $user = id(new PhabricatorPeopleQuery()) ->setViewer($viewer) ->withPHIDs(array($user_phid)) ->executeOne(); if (!$user) { return null; } return $user; } }