Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommit.php b/src/applications/repository/storage/PhabricatorRepositoryCommit.php
index 6b8ac687b3..b5c2fde82a 100644
--- a/src/applications/repository/storage/PhabricatorRepositoryCommit.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryCommit.php
@@ -1,900 +1,912 @@
<?php
final class PhabricatorRepositoryCommit
extends PhabricatorRepositoryDAO
implements
PhabricatorPolicyInterface,
PhabricatorFlaggableInterface,
PhabricatorProjectInterface,
PhabricatorTokenReceiverInterface,
PhabricatorSubscribableInterface,
PhabricatorMentionableInterface,
HarbormasterBuildableInterface,
HarbormasterCircleCIBuildableInterface,
HarbormasterBuildkiteBuildableInterface,
PhabricatorCustomFieldInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorTimelineInterface,
PhabricatorFulltextInterface,
PhabricatorFerretInterface,
PhabricatorConduitResultInterface,
PhabricatorDraftInterface {
protected $repositoryID;
protected $phid;
protected $authorIdentityPHID;
protected $committerIdentityPHID;
protected $commitIdentifier;
protected $epoch;
protected $authorPHID;
protected $auditStatus = DiffusionCommitAuditStatus::NONE;
protected $summary = '';
protected $importStatus = 0;
const IMPORTED_MESSAGE = 1;
const IMPORTED_CHANGE = 2;
const IMPORTED_PUBLISH = 8;
const IMPORTED_ALL = 11;
const IMPORTED_CLOSEABLE = 1024;
const IMPORTED_UNREACHABLE = 2048;
private $commitData = self::ATTACHABLE;
private $audits = self::ATTACHABLE;
private $repository = self::ATTACHABLE;
private $customFields = self::ATTACHABLE;
private $authorIdentity = self::ATTACHABLE;
private $committerIdentity = self::ATTACHABLE;
private $drafts = array();
private $auditAuthorityPHIDs = array();
public function attachRepository(PhabricatorRepository $repository) {
$this->repository = $repository;
return $this;
}
public function getRepository($assert_attached = true) {
if ($assert_attached) {
return $this->assertAttached($this->repository);
}
return $this->repository;
}
public function isPartiallyImported($mask) {
return (($mask & $this->getImportStatus()) == $mask);
}
public function isImported() {
return $this->isPartiallyImported(self::IMPORTED_ALL);
}
public function isUnreachable() {
return $this->isPartiallyImported(self::IMPORTED_UNREACHABLE);
}
public function writeImportStatusFlag($flag) {
return $this->adjustImportStatusFlag($flag, true);
}
public function clearImportStatusFlag($flag) {
return $this->adjustImportStatusFlag($flag, false);
}
private function adjustImportStatusFlag($flag, $set) {
$conn_w = $this->establishConnection('w');
$table_name = $this->getTableName();
$id = $this->getID();
if ($set) {
queryfx(
$conn_w,
'UPDATE %T SET importStatus = (importStatus | %d) WHERE id = %d',
$table_name,
$flag,
$id);
$this->setImportStatus($this->getImportStatus() | $flag);
} else {
queryfx(
$conn_w,
'UPDATE %T SET importStatus = (importStatus & ~%d) WHERE id = %d',
$table_name,
$flag,
$id);
$this->setImportStatus($this->getImportStatus() & ~$flag);
}
return $this;
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_COLUMN_SCHEMA => array(
'commitIdentifier' => 'text40',
'authorPHID' => 'phid?',
'authorIdentityPHID' => 'phid?',
'committerIdentityPHID' => 'phid?',
'auditStatus' => 'text32',
'summary' => 'text255',
'importStatus' => 'uint32',
),
self::CONFIG_KEY_SCHEMA => array(
'key_phid' => null,
'phid' => array(
'columns' => array('phid'),
'unique' => true,
),
'repositoryID' => array(
'columns' => array('repositoryID', 'importStatus'),
),
'authorPHID' => array(
'columns' => array('authorPHID', 'auditStatus', 'epoch'),
),
'repositoryID_2' => array(
'columns' => array('repositoryID', 'epoch'),
),
'key_commit_identity' => array(
'columns' => array('commitIdentifier', 'repositoryID'),
'unique' => true,
),
'key_epoch' => array(
'columns' => array('epoch'),
),
'key_author' => array(
'columns' => array('authorPHID', 'epoch'),
),
),
self::CONFIG_NO_MUTATE => array(
'importStatus',
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorRepositoryCommitPHIDType::TYPECONST);
}
public function loadCommitData() {
if (!$this->getID()) {
return null;
}
return id(new PhabricatorRepositoryCommitData())->loadOneWhere(
'commitID = %d',
$this->getID());
}
public function attachCommitData(
PhabricatorRepositoryCommitData $data = null) {
$this->commitData = $data;
return $this;
}
public function getCommitData() {
return $this->assertAttached($this->commitData);
}
public function attachAudits(array $audits) {
assert_instances_of($audits, 'PhabricatorRepositoryAuditRequest');
$this->audits = $audits;
return $this;
}
public function getAudits() {
return $this->assertAttached($this->audits);
}
public function hasAttachedAudits() {
return ($this->audits !== self::ATTACHABLE);
}
public function attachIdentities(
PhabricatorRepositoryIdentity $author = null,
PhabricatorRepositoryIdentity $committer = null) {
$this->authorIdentity = $author;
$this->committerIdentity = $committer;
return $this;
}
public function getAuthorIdentity() {
return $this->assertAttached($this->authorIdentity);
}
public function getCommitterIdentity() {
return $this->assertAttached($this->committerIdentity);
}
public function attachAuditAuthority(
PhabricatorUser $user,
array $authority) {
$user_phid = $user->getPHID();
if (!$user->getPHID()) {
throw new Exception(
pht('You can not attach audit authority for a user with no PHID.'));
}
$this->auditAuthorityPHIDs[$user_phid] = $authority;
return $this;
}
public function hasAuditAuthority(
PhabricatorUser $user,
PhabricatorRepositoryAuditRequest $audit) {
$user_phid = $user->getPHID();
if (!$user_phid) {
return false;
}
$map = $this->assertAttachedKey($this->auditAuthorityPHIDs, $user_phid);
return isset($map[$audit->getAuditorPHID()]);
}
public function writeOwnersEdges(array $package_phids) {
$src_phid = $this->getPHID();
$edge_type = DiffusionCommitHasPackageEdgeType::EDGECONST;
$editor = new PhabricatorEdgeEditor();
$dst_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$src_phid,
$edge_type);
foreach ($dst_phids as $dst_phid) {
$editor->removeEdge($src_phid, $edge_type, $dst_phid);
}
foreach ($package_phids as $package_phid) {
$editor->addEdge($src_phid, $edge_type, $package_phid);
}
$editor->save();
return $this;
}
public function getAuditorPHIDsForEdit() {
$audits = $this->getAudits();
return mpull($audits, 'getAuditorPHID');
}
public function delete() {
$data = $this->loadCommitData();
$audits = id(new PhabricatorRepositoryAuditRequest())
->loadAllWhere('commitPHID = %s', $this->getPHID());
$this->openTransaction();
if ($data) {
$data->delete();
}
foreach ($audits as $audit) {
$audit->delete();
}
$result = parent::delete();
$this->saveTransaction();
return $result;
}
public function getDateCreated() {
// This is primarily to make analysis of commits with the Fact engine work.
return $this->getEpoch();
}
public function getURI() {
return '/'.$this->getMonogram();
}
/**
* Synchronize a commit's overall audit status with the individual audit
* triggers.
*/
public function updateAuditStatus(array $requests) {
assert_instances_of($requests, 'PhabricatorRepositoryAuditRequest');
$any_concern = false;
$any_accept = false;
$any_need = false;
foreach ($requests as $request) {
switch ($request->getAuditStatus()) {
case PhabricatorAuditStatusConstants::AUDIT_REQUIRED:
case PhabricatorAuditStatusConstants::AUDIT_REQUESTED:
$any_need = true;
break;
case PhabricatorAuditStatusConstants::ACCEPTED:
$any_accept = true;
break;
case PhabricatorAuditStatusConstants::CONCERNED:
$any_concern = true;
break;
}
}
if ($any_concern) {
if ($this->isAuditStatusNeedsVerification()) {
// If the change is in "Needs Verification", we keep it there as
// long as any auditors still have concerns.
$status = DiffusionCommitAuditStatus::NEEDS_VERIFICATION;
} else {
$status = DiffusionCommitAuditStatus::CONCERN_RAISED;
}
} else if ($any_accept) {
if ($any_need) {
$status = DiffusionCommitAuditStatus::PARTIALLY_AUDITED;
} else {
$status = DiffusionCommitAuditStatus::AUDITED;
}
} else if ($any_need) {
$status = DiffusionCommitAuditStatus::NEEDS_AUDIT;
} else {
$status = DiffusionCommitAuditStatus::NONE;
}
return $this->setAuditStatus($status);
}
public function getMonogram() {
$repository = $this->getRepository();
$callsign = $repository->getCallsign();
$identifier = $this->getCommitIdentifier();
if ($callsign !== null) {
return "r{$callsign}{$identifier}";
} else {
$id = $repository->getID();
return "R{$id}:{$identifier}";
}
}
public function getDisplayName() {
$repository = $this->getRepository();
$identifier = $this->getCommitIdentifier();
return $repository->formatCommitName($identifier);
}
/**
* Return a local display name for use in the context of the containing
* repository.
*
* In Git and Mercurial, this returns only a short hash, like "abcdef012345".
* See @{method:getDisplayName} for a short name that always includes
* repository context.
*
* @return string Short human-readable name for use inside a repository.
*/
public function getLocalName() {
$repository = $this->getRepository();
$identifier = $this->getCommitIdentifier();
return $repository->formatCommitName($identifier, $local = true);
}
/**
* Make a strong effort to find a way to render this commit's committer.
* This currently attempts to use @{PhabricatorRepositoryIdentity}, and
* falls back to examining the commit detail information. After we force
* the migration to using identities, update this method to remove the
* fallback. See T12164 for details.
*/
public function renderAnyCommitter(PhabricatorUser $viewer, $handles) {
$committer = $this->renderCommitter($viewer, $handles);
if ($committer) {
return $committer;
}
return $this->renderAuthor($viewer, $handles);
}
public function renderCommitter(PhabricatorUser $viewer, $handles) {
$committer_phid = $this->getCommitterDisplayPHID();
if ($committer_phid) {
return $handles[$committer_phid]->renderLink();
}
$data = $this->getCommitData();
$committer_name = $data->getCommitDetail('committer');
if (strlen($committer_name)) {
return DiffusionView::renderName($committer_name);
}
return null;
}
public function renderAuthor(PhabricatorUser $viewer, $handles) {
$author_phid = $this->getAuthorDisplayPHID();
if ($author_phid) {
return $handles[$author_phid]->renderLink();
}
$data = $this->getCommitData();
$author_name = $data->getAuthorName();
if (strlen($author_name)) {
return DiffusionView::renderName($author_name);
}
return null;
}
public function loadIdentities(PhabricatorUser $viewer) {
if ($this->authorIdentity !== self::ATTACHABLE) {
return $this;
}
$commit = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withIDs(array($this->getID()))
->needIdentities(true)
->executeOne();
$author_identity = $commit->getAuthorIdentity();
$committer_identity = $commit->getCommitterIdentity();
return $this->attachIdentities($author_identity, $committer_identity);
}
public function hasCommitterIdentity() {
return ($this->getCommitterIdentity() !== null);
}
public function hasAuthorIdentity() {
return ($this->getAuthorIdentity() !== null);
}
public function getCommitterDisplayPHID() {
if ($this->hasCommitterIdentity()) {
return $this->getCommitterIdentity()->getIdentityDisplayPHID();
}
$data = $this->getCommitData();
return $data->getCommitDetail('committerPHID');
}
public function getAuthorDisplayPHID() {
if ($this->hasAuthorIdentity()) {
return $this->getAuthorIdentity()->getIdentityDisplayPHID();
}
$data = $this->getCommitData();
return $data->getCommitDetail('authorPHID');
}
+ public function getEffectiveAuthorPHID() {
+ if ($this->hasAuthorIdentity()) {
+ $identity = $this->getAuthorIdentity();
+ if ($identity->hasEffectiveUser()) {
+ return $identity->getCurrentEffectiveUserPHID();
+ }
+ }
+
+ $data = $this->getCommitData();
+ return $data->getCommitDetail('authorPHID');
+ }
+
public function getAuditStatusObject() {
$status = $this->getAuditStatus();
return DiffusionCommitAuditStatus::newForStatus($status);
}
public function isAuditStatusNoAudit() {
return $this->getAuditStatusObject()->isNoAudit();
}
public function isAuditStatusNeedsAudit() {
return $this->getAuditStatusObject()->isNeedsAudit();
}
public function isAuditStatusConcernRaised() {
return $this->getAuditStatusObject()->isConcernRaised();
}
public function isAuditStatusNeedsVerification() {
return $this->getAuditStatusObject()->isNeedsVerification();
}
public function isAuditStatusPartiallyAudited() {
return $this->getAuditStatusObject()->isPartiallyAudited();
}
public function isAuditStatusAudited() {
return $this->getAuditStatusObject()->isAudited();
}
public function isPermanentCommit() {
return (bool)$this->isPartiallyImported(self::IMPORTED_CLOSEABLE);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getRepository()->getPolicy($capability);
case PhabricatorPolicyCapability::CAN_EDIT:
return PhabricatorPolicies::POLICY_USER;
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getRepository()->hasAutomaticCapability($capability, $viewer);
}
public function describeAutomaticCapability($capability) {
return pht(
'Commits inherit the policies of the repository they belong to.');
}
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
public function getUsersToNotifyOfTokenGiven() {
return array(
$this->getAuthorPHID(),
);
}
/* -( Stuff for serialization )---------------------------------------------- */
/**
* NOTE: this is not a complete serialization; only the 'protected' fields are
* involved. This is due to ease of (ab)using the Lisk abstraction to get this
* done, as well as complexity of the other fields.
*/
public function toDictionary() {
return array(
'repositoryID' => $this->getRepositoryID(),
'phid' => $this->getPHID(),
'commitIdentifier' => $this->getCommitIdentifier(),
'epoch' => $this->getEpoch(),
'authorPHID' => $this->getAuthorPHID(),
'auditStatus' => $this->getAuditStatus(),
'summary' => $this->getSummary(),
'importStatus' => $this->getImportStatus(),
);
}
public static function newFromDictionary(array $dict) {
return id(new PhabricatorRepositoryCommit())
->loadFromArray($dict);
}
/* -( HarbormasterBuildableInterface )------------------------------------- */
public function getHarbormasterBuildableDisplayPHID() {
return $this->getHarbormasterBuildablePHID();
}
public function getHarbormasterBuildablePHID() {
return $this->getPHID();
}
public function getHarbormasterContainerPHID() {
return $this->getRepository()->getPHID();
}
public function getBuildVariables() {
$results = array();
$results['buildable.commit'] = $this->getCommitIdentifier();
$repo = $this->getRepository();
$results['repository.callsign'] = $repo->getCallsign();
$results['repository.phid'] = $repo->getPHID();
$results['repository.vcs'] = $repo->getVersionControlSystem();
$results['repository.uri'] = $repo->getPublicCloneURI();
return $results;
}
public function getAvailableBuildVariables() {
return array(
'buildable.commit' => pht('The commit identifier, if applicable.'),
'repository.callsign' =>
pht('The callsign of the repository in Phabricator.'),
'repository.phid' =>
pht('The PHID of the repository in Phabricator.'),
'repository.vcs' =>
pht('The version control system, either "svn", "hg" or "git".'),
'repository.uri' =>
pht('The URI to clone or checkout the repository from.'),
);
}
public function newBuildableEngine() {
return new DiffusionBuildableEngine();
}
/* -( HarbormasterCircleCIBuildableInterface )----------------------------- */
public function getCircleCIGitHubRepositoryURI() {
$repository = $this->getRepository();
$commit_phid = $this->getPHID();
$repository_phid = $repository->getPHID();
if ($repository->isHosted()) {
throw new Exception(
pht(
'This commit ("%s") is associated with a hosted repository '.
'("%s"). Repositories must be imported from GitHub to be built '.
'with CircleCI.',
$commit_phid,
$repository_phid));
}
$remote_uri = $repository->getRemoteURI();
$path = HarbormasterCircleCIBuildStepImplementation::getGitHubPath(
$remote_uri);
if (!$path) {
throw new Exception(
pht(
'This commit ("%s") is associated with a repository ("%s") that '.
'with a remote URI ("%s") that does not appear to be hosted on '.
'GitHub. Repositories must be hosted on GitHub to be built with '.
'CircleCI.',
$commit_phid,
$repository_phid,
$remote_uri));
}
return $remote_uri;
}
public function getCircleCIBuildIdentifierType() {
return 'revision';
}
public function getCircleCIBuildIdentifier() {
return $this->getCommitIdentifier();
}
/* -( HarbormasterBuildkiteBuildableInterface )---------------------------- */
public function getBuildkiteBranch() {
$viewer = PhabricatorUser::getOmnipotentUser();
$repository = $this->getRepository();
$branches = DiffusionQuery::callConduitWithDiffusionRequest(
$viewer,
DiffusionRequest::newFromDictionary(
array(
'repository' => $repository,
'user' => $viewer,
)),
'diffusion.branchquery',
array(
'contains' => $this->getCommitIdentifier(),
'repository' => $repository->getPHID(),
));
if (!$branches) {
throw new Exception(
pht(
'Commit "%s" is not an ancestor of any branch head, so it can not '.
'be built with Buildkite.',
$this->getCommitIdentifier()));
}
$branch = head($branches);
return 'refs/heads/'.$branch['shortName'];
}
public function getBuildkiteCommit() {
return $this->getCommitIdentifier();
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
public function getCustomFieldSpecificationForRole($role) {
return PhabricatorEnv::getEnvConfig('diffusion.fields');
}
public function getCustomFieldBaseClass() {
return 'PhabricatorCommitCustomField';
}
public function getCustomFields() {
return $this->assertAttached($this->customFields);
}
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
$this->customFields = $fields;
return $this;
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */
public function isAutomaticallySubscribed($phid) {
// TODO: This should also list auditors, but handling that is a bit messy
// right now because we are not guaranteed to have the data. (It should not
// include resigned auditors.)
return ($phid == $this->getAuthorPHID());
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorAuditEditor();
}
public function getApplicationTransactionTemplate() {
return new PhabricatorAuditTransaction();
}
/* -( PhabricatorFulltextInterface )--------------------------------------- */
public function newFulltextEngine() {
return new DiffusionCommitFulltextEngine();
}
/* -( PhabricatorFerretInterface )----------------------------------------- */
public function newFerretEngine() {
return new DiffusionCommitFerretEngine();
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('identifier')
->setType('string')
->setDescription(pht('The commit identifier.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('repositoryPHID')
->setType('phid')
->setDescription(pht('The repository this commit belongs to.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('author')
->setType('map<string, wild>')
->setDescription(pht('Information about the commit author.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('committer')
->setType('map<string, wild>')
->setDescription(pht('Information about the committer.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('isImported')
->setType('bool')
->setDescription(pht('True if the commit is fully imported.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('isUnreachable')
->setType('bool')
->setDescription(
pht(
'True if the commit is not the ancestor of any tag, branch, or '.
'ref.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('auditStatus')
->setType('map<string, wild>')
->setDescription(pht('Information about the current audit status.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('message')
->setType('string')
->setDescription(pht('The commit message.')),
);
}
public function getFieldValuesForConduit() {
$data = $this->getCommitData();
$author_identity = $this->getAuthorIdentity();
if ($author_identity) {
$author_name = $author_identity->getIdentityDisplayName();
$author_email = $author_identity->getIdentityEmailAddress();
$author_raw = $author_identity->getIdentityName();
$author_identity_phid = $author_identity->getPHID();
$author_user_phid = $author_identity->getCurrentEffectiveUserPHID();
} else {
$author_name = null;
$author_email = null;
$author_raw = null;
$author_identity_phid = null;
$author_user_phid = null;
}
$committer_identity = $this->getCommitterIdentity();
if ($committer_identity) {
$committer_name = $committer_identity->getIdentityDisplayName();
$committer_email = $committer_identity->getIdentityEmailAddress();
$committer_raw = $committer_identity->getIdentityName();
$committer_identity_phid = $committer_identity->getPHID();
$committer_user_phid = $committer_identity->getCurrentEffectiveUserPHID();
} else {
$committer_name = null;
$committer_email = null;
$committer_raw = null;
$committer_identity_phid = null;
$committer_user_phid = null;
}
$author_epoch = $data->getCommitDetail('authorEpoch');
if ($author_epoch) {
$author_epoch = (int)$author_epoch;
} else {
$author_epoch = null;
}
$audit_status = $this->getAuditStatusObject();
return array(
'identifier' => $this->getCommitIdentifier(),
'repositoryPHID' => $this->getRepository()->getPHID(),
'author' => array(
'name' => $author_name,
'email' => $author_email,
'raw' => $author_raw,
'epoch' => $author_epoch,
'identityPHID' => $author_identity_phid,
'userPHID' => $author_user_phid,
),
'committer' => array(
'name' => $committer_name,
'email' => $committer_email,
'raw' => $committer_raw,
'epoch' => (int)$this->getEpoch(),
'identityPHID' => $committer_identity_phid,
'userPHID' => $committer_user_phid,
),
'isUnreachable' => (bool)$this->isUnreachable(),
'isImported' => (bool)$this->isImported(),
'auditStatus' => array(
'value' => $audit_status->getKey(),
'name' => $audit_status->getName(),
'closed' => (bool)$audit_status->getIsClosed(),
'color.ansi' => $audit_status->getAnsiColor(),
),
'message' => $data->getCommitMessage(),
);
}
public function getConduitSearchAttachments() {
return array();
}
/* -( PhabricatorDraftInterface )------------------------------------------ */
public function newDraftEngine() {
return new DiffusionCommitDraftEngine();
}
public function getHasDraft(PhabricatorUser $viewer) {
return $this->assertAttachedKey($this->drafts, $viewer->getCacheFragment());
}
public function attachHasDraft(PhabricatorUser $viewer, $has_draft) {
$this->drafts[$viewer->getCacheFragment()] = $has_draft;
return $this;
}
/* -( PhabricatorTimelineInterface )--------------------------------------- */
public function newTimelineEngine() {
return new DiffusionCommitTimelineEngine();
}
}
diff --git a/src/applications/repository/worker/PhabricatorRepositoryCommitPublishWorker.php b/src/applications/repository/worker/PhabricatorRepositoryCommitPublishWorker.php
index 3865050395..0b0a194806 100644
--- a/src/applications/repository/worker/PhabricatorRepositoryCommitPublishWorker.php
+++ b/src/applications/repository/worker/PhabricatorRepositoryCommitPublishWorker.php
@@ -1,394 +1,394 @@
<?php
final class PhabricatorRepositoryCommitPublishWorker
extends PhabricatorRepositoryCommitParserWorker {
protected function getImportStepFlag() {
return PhabricatorRepositoryCommit::IMPORTED_PUBLISH;
}
public function getRequiredLeaseTime() {
// Herald rules may take a long time to process.
return phutil_units('4 hours in seconds');
}
protected function parseCommit(
PhabricatorRepository $repository,
PhabricatorRepositoryCommit $commit) {
if (!$this->shouldSkipImportStep()) {
$this->publishCommit($repository, $commit);
$commit->writeImportStatusFlag($this->getImportStepFlag());
}
// This is the last task in the sequence, so we don't need to queue any
// followup workers.
}
private function publishCommit(
PhabricatorRepository $repository,
PhabricatorRepositoryCommit $commit) {
$viewer = PhabricatorUser::getOmnipotentUser();
$publisher = $repository->newPublisher();
if (!$publisher->shouldPublishCommit($commit)) {
return;
}
$commit_phid = $commit->getPHID();
// Reload the commit to get the commit data, identities, and any
// outstanding audit requests.
$commit = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withPHIDs(array($commit_phid))
->needCommitData(true)
->needIdentities(true)
->needAuditRequests(true)
->executeOne();
if (!$commit) {
throw new PhabricatorWorkerPermanentFailureException(
pht(
'Failed to reload commit "%s".',
$commit_phid));
}
$xactions = array(
$this->newAuditTransactions($commit),
$this->newPublishTransactions($commit),
);
$xactions = array_mergev($xactions);
$acting_phid = $this->getPublishAsPHID($commit);
$content_source = $this->newContentSource();
$revision = DiffusionCommitRevisionQuery::loadRevisionForCommit(
$viewer,
$commit);
// Prevent the commit from generating a mention of the associated
// revision, if one exists, so we don't double up because of the URI
// in the commit message.
$unmentionable_phids = array();
if ($revision) {
$unmentionable_phids[] = $revision->getPHID();
}
$editor = $commit->getApplicationTransactionEditor()
->setActor($viewer)
->setActingAsPHID($acting_phid)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setContentSource($content_source)
->addUnmentionablePHIDs($unmentionable_phids);
try {
$raw_patch = $this->loadRawPatchText($repository, $commit);
} catch (Exception $ex) {
$raw_patch = pht('Unable to generate patch: %s', $ex->getMessage());
}
$editor->setRawPatch($raw_patch);
$editor->applyTransactions($commit, $xactions);
}
private function getPublishAsPHID(PhabricatorRepositoryCommit $commit) {
if ($commit->hasCommitterIdentity()) {
return $commit->getCommitterIdentity()->getIdentityDisplayPHID();
}
if ($commit->hasAuthorIdentity()) {
return $commit->getAuthorIdentity()->getIdentityDisplayPHID();
}
return id(new PhabricatorDiffusionApplication())->getPHID();
}
private function newPublishTransactions(PhabricatorRepositoryCommit $commit) {
$data = $commit->getCommitData();
$xactions = array();
$xactions[] = $commit->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorAuditTransaction::TYPE_COMMIT)
->setDateCreated($commit->getEpoch())
->setNewValue(
array(
'description' => $data->getCommitMessage(),
'summary' => $data->getSummary(),
'authorName' => $data->getAuthorName(),
'authorPHID' => $commit->getAuthorPHID(),
'committerName' => $data->getCommitDetail('committer'),
'committerPHID' => $data->getCommitDetail('committerPHID'),
));
return $xactions;
}
private function newAuditTransactions(PhabricatorRepositoryCommit $commit) {
$viewer = PhabricatorUser::getOmnipotentUser();
$repository = $commit->getRepository();
$affected_paths = PhabricatorOwnerPathQuery::loadAffectedPaths(
$repository,
$commit,
PhabricatorUser::getOmnipotentUser());
$affected_packages = PhabricatorOwnersPackage::loadAffectedPackages(
$repository,
$affected_paths);
$commit->writeOwnersEdges(mpull($affected_packages, 'getPHID'));
if (!$affected_packages) {
return array();
}
$data = $commit->getCommitData();
- $author_phid = $data->getCommitDetail('authorPHID');
+ $author_phid = $commit->getEffectiveAuthorPHID();
$revision = DiffusionCommitRevisionQuery::loadRevisionForCommit(
$viewer,
$commit);
$requests = $commit->getAudits();
$requests = mpull($requests, null, 'getAuditorPHID');
$auditor_phids = array();
foreach ($affected_packages as $package) {
$request = idx($requests, $package->getPHID());
if ($request) {
// Don't update request if it exists already.
continue;
}
$should_audit = $this->shouldTriggerAudit(
$commit,
$package,
$author_phid,
$revision);
if (!$should_audit) {
continue;
}
$auditor_phids[] = $package->getPHID();
}
// If none of the packages are triggering audits, we're all done.
if (!$auditor_phids) {
return array();
}
$audit_type = DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE;
$xactions = array();
$xactions[] = $commit->getApplicationTransactionTemplate()
->setTransactionType($audit_type)
->setNewValue(
array(
'+' => array_fuse($auditor_phids),
));
return $xactions;
}
private function shouldTriggerAudit(
PhabricatorRepositoryCommit $commit,
PhabricatorOwnersPackage $package,
$author_phid,
$revision) {
$audit_uninvolved = false;
$audit_unreviewed = false;
$rule = $package->newAuditingRule();
switch ($rule->getKey()) {
case PhabricatorOwnersAuditRule::AUDITING_NONE:
return false;
case PhabricatorOwnersAuditRule::AUDITING_ALL:
return true;
case PhabricatorOwnersAuditRule::AUDITING_NO_OWNER:
$audit_uninvolved = true;
break;
case PhabricatorOwnersAuditRule::AUDITING_UNREVIEWED:
$audit_unreviewed = true;
break;
case PhabricatorOwnersAuditRule::AUDITING_NO_OWNER_AND_UNREVIEWED:
$audit_uninvolved = true;
$audit_unreviewed = true;
break;
}
// If auditing is configured to trigger on unreviewed changes, check if
// the revision was "Accepted" when it landed. If not, trigger an audit.
// We may be running before the revision actually closes, so we'll count
// either an "Accepted" or a "Closed, Previously Accepted" revision as
// good enough.
if ($audit_unreviewed) {
$commit_unreviewed = true;
if ($revision) {
if ($revision->isAccepted()) {
$commit_unreviewed = false;
} else {
$was_accepted = DifferentialRevision::PROPERTY_CLOSED_FROM_ACCEPTED;
if ($revision->isPublished()) {
if ($revision->getProperty($was_accepted)) {
$commit_unreviewed = false;
}
}
}
}
if ($commit_unreviewed) {
return true;
}
}
// If auditing is configured to trigger on changes with no involved owner,
// check for an owner. If we don't find one, trigger an audit.
if ($audit_uninvolved) {
$owner_involved = $this->isOwnerInvolved(
$commit,
$package,
$author_phid,
$revision);
if (!$owner_involved) {
return true;
}
}
// We can't find any reason to trigger an audit for this commit.
return false;
}
private function isOwnerInvolved(
PhabricatorRepositoryCommit $commit,
PhabricatorOwnersPackage $package,
$author_phid,
$revision) {
$owner_phids = PhabricatorOwnersOwner::loadAffiliatedUserPHIDs(
array(
$package->getID(),
));
$owner_phids = array_fuse($owner_phids);
// For the purposes of deciding whether the owners were involved in the
// revision or not, consider a review by the package itself to count as
// involvement. This can happen when human reviewers force-accept on
// behalf of packages they don't own but have authority over.
$owner_phids[$package->getPHID()] = $package->getPHID();
// If the commit author is identifiable and a package owner, they're
// involved.
if ($author_phid) {
if (isset($owner_phids[$author_phid])) {
return true;
}
}
// Otherwise, we need to find an owner as a reviewer.
// If we don't have a revision, this is hopeless: no owners are involved.
if (!$revision) {
return true;
}
$accepted_statuses = array(
DifferentialReviewerStatus::STATUS_ACCEPTED,
DifferentialReviewerStatus::STATUS_ACCEPTED_OLDER,
);
$accepted_statuses = array_fuse($accepted_statuses);
$found_accept = false;
foreach ($revision->getReviewers() as $reviewer) {
$reviewer_phid = $reviewer->getReviewerPHID();
// If this reviewer isn't a package owner or the package itself,
// just ignore them.
if (empty($owner_phids[$reviewer_phid])) {
continue;
}
// If this reviewer accepted the revision and owns the package (or is
// the package), we've found an involved owner.
if (isset($accepted_statuses[$reviewer->getReviewerStatus()])) {
$found_accept = true;
break;
}
}
if ($found_accept) {
return true;
}
return false;
}
private function loadRawPatchText(
PhabricatorRepository $repository,
PhabricatorRepositoryCommit $commit) {
$viewer = PhabricatorUser::getOmnipotentUser();
$identifier = $commit->getCommitIdentifier();
$drequest = DiffusionRequest::newFromDictionary(
array(
'user' => $viewer,
'repository' => $repository,
));
$time_key = 'metamta.diffusion.time-limit';
$byte_key = 'metamta.diffusion.byte-limit';
$time_limit = PhabricatorEnv::getEnvConfig($time_key);
$byte_limit = PhabricatorEnv::getEnvConfig($byte_key);
$diff_info = DiffusionQuery::callConduitWithDiffusionRequest(
$viewer,
$drequest,
'diffusion.rawdiffquery',
array(
'commit' => $identifier,
'linesOfContext' => 3,
'timeout' => $time_limit,
'byteLimit' => $byte_limit,
));
if ($diff_info['tooSlow']) {
throw new Exception(
pht(
'Patch generation took longer than configured limit ("%s") of '.
'%s second(s).',
$time_key,
new PhutilNumber($time_limit)));
}
if ($diff_info['tooHuge']) {
$pretty_limit = phutil_format_bytes($byte_limit);
throw new Exception(
pht(
'Patch size exceeds configured byte size limit ("%s") of %s.',
$byte_key,
$pretty_limit));
}
$file_phid = $diff_info['filePHID'];
$file = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($file_phid))
->executeOne();
if (!$file) {
throw new Exception(
pht(
'Failed to load file ("%s") returned by "%s".',
$file_phid,
'diffusion.rawdiffquery'));
}
return $file->loadFileData();
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jun 10, 5:39 PM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
140635
Default Alt Text
(39 KB)

Event Timeline