/** * Update the table which links Differential revisions to paths they affect, * so Diffusion can efficiently find pending revisions for a given file. */ private function updateAffectedPathTable(DifferentialRevision $revision, DifferentialDiff $diff) { $repository = $revision->getRepository(); if (!$repository) { // The repository where the code lives is untracked. return; } $path_prefix = null; $local_root = $diff->getSourceControlPath(); if ($local_root) { // We're in a working copy which supports subdirectory checkouts (e.g., // SVN) so we need to figure out what prefix we should add to each path // (e.g., trunk/projects/example/) to get the absolute path from the // root of the repository. DVCS systems like Git and Mercurial are not // affected. // Normalize both paths and check if the repository root is a prefix of // the local root. If so, throw it away. Note that this correctly handles // the case where the remote path is "/". $local_root = id(new PhutilURI($local_root))->getPath(); $local_root = rtrim($local_root, '/'); $repo_root = id(new PhutilURI($repository->getRemoteURI()))->getPath(); $repo_root = rtrim($repo_root, '/'); if (!strncmp($repo_root, $local_root, strlen($repo_root))) { $path_prefix = substr($local_root, strlen($repo_root)); } } $changesets = $diff->getChangesets(); $paths = array(); foreach ($changesets as $changeset) { $paths[] = $path_prefix . '/' . $changeset->getFilename(); } // Mark this as also touching all parent paths, so you can see all pending // changes to any file within a directory. $all_paths = array(); foreach ($paths as $local) { foreach (DiffusionPathIDQuery::expandPathToRoot($local) as $path) { $all_paths[$path] = true; } } $all_paths = array_keys($all_paths); $path_ids = PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths($all_paths); $table = new DifferentialAffectedPath(); $conn_w = $table->establishConnection('w'); $sql = array(); foreach ($path_ids as $path_id) { $sql[] = qsprintf($conn_w, '(%d, %d, %d, %d)', $repository->getID(), $path_id, time(), $revision->getID()); } queryfx($conn_w, 'DELETE FROM %T WHERE revisionID = %d', $table->getTableName(), $revision->getID()); foreach (array_chunk($sql, 256) as $chunk) { queryfx($conn_w, 'INSERT INTO %T (repositoryID, pathID, epoch, revisionID) VALUES %Q', $table->getTableName(), implode(', ', $chunk)); } }
private function buildOperationsBox(DifferentialRevision $revision) { $viewer = $this->getViewer(); // Save a query if we can't possibly have pending operations. $repository = $revision->getRepository(); if (!$repository || !$repository->canPerformAutomation()) { return null; } $operations = id(new DrydockRepositoryOperationQuery())->setViewer($viewer)->withObjectPHIDs(array($revision->getPHID()))->withOperationStates(array(DrydockRepositoryOperation::STATE_WAIT, DrydockRepositoryOperation::STATE_WORK, DrydockRepositoryOperation::STATE_FAIL))->execute(); if (!$operations) { return null; } $operation = head(msort($operations, 'getID')); $box_view = id(new PHUIObjectBoxView())->setHeaderText(pht('Active Operations')); return id(new DrydockRepositoryOperationStatusView())->setUser($viewer)->setBoxView($box_view)->setOperation($operation); }
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) { switch ($revision->getStatus()) { case ArcanistDifferentialRevisionStatus::CLOSED: return array('title' => pht('Revision Closed'), 'body' => pht('This revision has already been closed. Only open, accepted ' . 'revisions may land.')); case ArcanistDifferentialRevisionStatus::ABANDONED: return array('title' => pht('Revision Abandoned'), 'body' => pht('This revision has been abandoned. Only accepted revisions ' . 'may land.')); default: 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; }
private function buildOperationsBox(DifferentialRevision $revision) { $viewer = $this->getViewer(); // Save a query if we can't possibly have pending operations. $repository = $revision->getRepository(); if (!$repository || !$repository->canPerformAutomation()) { return null; } $operations = id(new DrydockRepositoryOperationQuery())->setViewer($viewer)->withObjectPHIDs(array($revision->getPHID()))->withIsDismissed(false)->withOperationTypes(array(DrydockLandRepositoryOperation::OPCONST))->execute(); if (!$operations) { return null; } $state_fail = DrydockRepositoryOperation::STATE_FAIL; // We're going to show the oldest operation which hasn't failed, or the // most recent failure if they're all failures. $operations = msort($operations, 'getID'); foreach ($operations as $operation) { if ($operation->getOperationState() != $state_fail) { break; } } // If we found a completed operation, don't render anything. We don't want // to show an older error after the thing worked properly. if ($operation->isDone()) { return null; } $box_view = id(new PHUIObjectBoxView())->setHeaderText(pht('Active Operations')); return id(new DrydockRepositoryOperationStatusView())->setUser($viewer)->setBoxView($box_view)->setOperation($operation); }
private function buildOperationsBox(DifferentialRevision $revision) { $viewer = $this->getViewer(); // Save a query if we can't possibly have pending operations. $repository = $revision->getRepository(); if (!$repository || !$repository->canPerformAutomation()) { return null; } $operations = id(new DrydockRepositoryOperationQuery())->setViewer($viewer)->withObjectPHIDs(array($revision->getPHID()))->withOperationStates(array(DrydockRepositoryOperation::STATE_WAIT, DrydockRepositoryOperation::STATE_WORK, DrydockRepositoryOperation::STATE_FAIL))->execute(); if (!$operations) { return null; } $operation = head(msort($operations, 'getID')); // TODO: This is completely made up for now, give it useful information and // a sweet progress bar. switch ($operation->getOperationState()) { case DrydockRepositoryOperation::STATE_WAIT: case DrydockRepositoryOperation::STATE_WORK: $severity = PHUIInfoView::SEVERITY_NOTICE; $text = pht('Some sort of repository operation is currently running.'); break; default: $severity = PHUIInfoView::SEVERITY_ERROR; $text = pht('Some sort of repository operation failed.'); break; } $info_view = id(new PHUIInfoView())->setSeverity($severity)->appendChild($text); return id(new PHUIObjectBoxView())->setHeaderText(pht('Active Operations (EXPERIMENTAL!)'))->setInfoView($info_view); }