private function buildPropertyListView(DrydockRepositoryOperation $operation) { $viewer = $this->getViewer(); $view = new PHUIPropertyListView(); $view->addProperty(pht('Repository'), $viewer->renderHandle($operation->getRepositoryPHID())); $view->addProperty(pht('Object'), $viewer->renderHandle($operation->getObjectPHID())); return $view; }
private function buildPropertyListView(DrydockRepositoryOperation $operation) { $viewer = $this->getViewer(); $view = new PHUIPropertyListView(); $view->addProperty(pht('Repository'), $viewer->renderHandle($operation->getRepositoryPHID())); $view->addProperty(pht('Object'), $viewer->renderHandle($operation->getObjectPHID())); $lease_phid = $operation->getWorkingCopyLeasePHID(); if ($lease_phid) { $lease_display = $viewer->renderHandle($lease_phid); } else { $lease_display = phutil_tag('em', array(), pht('None')); } $view->addProperty(pht('Working Copy'), $lease_display); return $view; }
public function renderUnderwayState() { $viewer = $this->getUser(); $operation = $this->getOperation(); $id = $operation->getID(); $state = $operation->getOperationState(); $icon = DrydockRepositoryOperation::getOperationStateIcon($state); $name = DrydockRepositoryOperation::getOperationStateName($state); $item = id(new PHUIObjectItemView())->setHref("/drydock/operation/{$id}/")->setObjectName(pht('Operation %d', $id))->setHeader($operation->getOperationDescription($viewer))->setStatusIcon($icon, $name); if ($state != DrydockRepositoryOperation::STATE_FAIL) { $item->addAttribute($operation->getOperationCurrentStatus($viewer)); } else { $vcs_error = $operation->getWorkingCopyVCSError(); if ($vcs_error) { switch ($vcs_error['phase']) { case DrydockWorkingCopyBlueprintImplementation::PHASE_SQUASHMERGE: $message = pht('This change did not merge cleanly. This usually indicates ' . 'that the change is out of date and needs to be updated.'); break; default: $message = pht('Operation encountered an error while performing repository ' . 'operations.'); break; } $item->addAttribute($message); $table = $this->renderVCSErrorTable($vcs_error); list($links, $info) = $this->renderDetailToggles($table); $item->addAttribute($links); $item->appendChild($info); } else { $item->addAttribute(pht('Operation encountered an error.')); } $is_dismissed = $operation->getIsDismissed(); $item->addAction(id(new PHUIListItemView())->setName('Dismiss')->setIcon('fa-times')->setDisabled($is_dismissed)->setWorkflow(true)->setHref("/drydock/operation/{$id}/dismiss/")); } return id(new PHUIObjectItemListView())->addItem($item); }
public function applyOperation(DrydockRepositoryOperation $operation, DrydockInterface $interface) { $repository = $operation->getRepository(); if ($repository->isGit()) { $interface->execx('git status'); } else { if ($repository->isHg()) { $interface->execx('hg status'); } else { if ($repository->isSVN()) { $interface->execx('svn status'); } else { throw new PhutilMethodNotImplementedException(); } } } }
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' => '*****@*****.**'); }
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.')); } } 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(); $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')); }
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}"; $op = new DrydockLandRepositoryOperation(); $barrier = $op->getBarrierToLanding($viewer, $revision); if ($barrier) { return $this->newDialog()->setTitle($barrier['title'])->appendParagraph($barrier['body'])->addCancelButton($detail_uri); } $diff = $revision->getActiveDiff(); $repository = $revision->getRepository(); $default_ref = $this->loadDefaultRef($repository, $diff); if ($default_ref) { $v_ref = array($default_ref->getPHID()); } else { $v_ref = array(); } $e_ref = true; $errors = array(); if ($request->isFormPost()) { $v_ref = $request->getArr('refPHIDs'); $ref_phid = head($v_ref); if (!strlen($ref_phid)) { $e_ref = pht('Required'); $errors[] = pht('You must select a branch to land this revision onto.'); } else { $ref = $this->newRefQuery($repository)->withPHIDs(array($ref_phid))->executeOne(); if (!$ref) { $e_ref = pht('Invalid'); $errors[] = pht('You must select a branch from this repository to land this ' . 'revision onto.'); } } if (!$errors) { // NOTE: The operation is locked to the current active diff, so if the // revision is updated before the operation applies nothing sneaky // occurs. $target = 'branch:' . $ref->getRefName(); $operation = DrydockRepositoryOperation::initializeNewOperation($op)->setAuthorPHID($viewer->getPHID())->setObjectPHID($revision->getPHID())->setRepositoryPHID($repository->getPHID())->setRepositoryTarget($target)->setProperty('differential.diffPHID', $diff->getPHID()); $operation->save(); $operation->scheduleUpdate(); return id(new AphrontRedirectResponse())->setURI($detail_uri); } } $ref_datasource = id(new DiffusionRefDatasource())->setParameters(array('repositoryPHIDs' => array($repository->getPHID()), 'refTypes' => $this->getTargetableRefTypes())); $form = id(new AphrontFormView())->setUser($viewer)->appendRemarkupInstructions(pht('In theory, this will do approximately what `arc land` would do. ' . 'In practice, you will have a riveting adventure instead.'))->appendControl(id(new AphrontFormTokenizerControl())->setLabel(pht('Onto Branch'))->setName('refPHIDs')->setLimit(1)->setError($e_ref)->setValue($v_ref)->setDatasource($ref_datasource))->appendRemarkupInstructions(pht('(WARNING) THIS FEATURE IS EXPERIMENTAL AND DANGEROUS! USE IT AT ' . 'YOUR OWN RISK!')); return $this->newDialog()->setWidth(AphrontDialogView::WIDTH_FORM)->setTitle(pht('Land Revision'))->setErrors($errors)->appendForm($form)->addCancelButton($detail_uri)->addSubmitButton(pht('Mutate Repository Unpredictably')); }
public function renderUnderwayState() { $viewer = $this->getUser(); $operation = $this->getOperation(); $id = $operation->getID(); $state = $operation->getOperationState(); $icon = DrydockRepositoryOperation::getOperationStateIcon($state); $name = DrydockRepositoryOperation::getOperationStateName($state); $item = id(new PHUIObjectItemView())->setHref("/drydock/operation/{$id}/")->setHeader($operation->getOperationDescription($viewer))->setStatusIcon($icon, $name); if ($state != DrydockRepositoryOperation::STATE_FAIL) { $item->addAttribute($operation->getOperationCurrentStatus($viewer)); } else { // TODO: Make this more useful. $item->addAttribute(pht('Operation encountered an error.')); } return id(new PHUIObjectItemListView())->addItem($item); }
protected function renderResultList(array $operations, PhabricatorSavedQuery $query, array $handles) { assert_instances_of($operations, 'DrydockRepositoryOperation'); $viewer = $this->requireViewer(); $view = new PHUIObjectItemListView(); foreach ($operations as $operation) { $id = $operation->getID(); $item = id(new PHUIObjectItemView())->setHeader($operation->getOperationDescription($viewer))->setHref($this->getApplicationURI("operation/{$id}/"))->setObjectName(pht('Repository Operation %d', $id)); $state = $operation->getOperationState(); $icon = DrydockRepositoryOperation::getOperationStateIcon($state); $name = DrydockRepositoryOperation::getOperationStateName($state); $item->addIcon($icon, $name); $item->addByline(array(pht('Via:'), ' ', $viewer->renderHandle($operation->getAuthorPHID()))); $item->addAttribute($viewer->renderHandle($operation->getObjectPHID())); $item->addAttribute($viewer->renderHandle($operation->getRepositoryPHID())); $view->addItem($item); } $result = id(new PhabricatorApplicationSearchResultView())->setObjectList($view)->setNoDataString(pht('No matching operations.')); return $result; }
public function handleRequest(AphrontRequest $request) { $response = $this->loadDiffusionContextForEdit(); if ($response) { return $response; } $viewer = $this->getViewer(); $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $panel_uri = id(new DiffusionRepositoryAutomationManagementPanel())->setRepository($repository)->getPanelURI(); if (!$repository->canPerformAutomation()) { return $this->newDialog()->setTitle(pht('Automation Not Configured'))->appendParagraph(pht('You can not run a configuration test for this repository ' . 'because you have not configured repository automation yet. ' . 'Configure it first, then test the configuration.'))->addCancelButton($panel_uri); } if ($request->isFormPost()) { $op = new DrydockTestRepositoryOperation(); $operation = DrydockRepositoryOperation::initializeNewOperation($op)->setAuthorPHID($viewer->getPHID())->setObjectPHID($repository->getPHID())->setRepositoryPHID($repository->getPHID())->setRepositoryTarget('none:')->save(); $operation->scheduleUpdate(); $operation_id = $operation->getID(); $operation_uri = "/drydock/operation/{$operation_id}/"; return id(new AphrontRedirectResponse())->setURI($operation_uri); } return $this->newDialog()->setTitle(pht('Test Automation Configuration'))->appendParagraph(pht('This configuration test will build a working copy of the ' . 'repository and perform some basic validation. If it works, ' . 'your configuration is substantially correct.'))->appendParagraph(pht('The test will not perform any writes against the repository, so ' . 'write operations may still fail even if the test passes. This ' . 'test covers building and reading working copies, but not writing ' . 'to them.'))->appendParagraph(pht('If you run into write failures despite passing this test, ' . 'it suggests that your setup is nearly correct but authentication ' . 'is probably not fully configured.'))->addCancelButton($panel_uri)->addSubmitButton(pht('Start Test')); }
protected function processDiffusionRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $drequest = $this->diffusionRequest; $repository = $drequest->getRepository(); $repository = id(new PhabricatorRepositoryQuery())->setViewer($viewer)->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->withIDs(array($repository->getID()))->executeOne(); if (!$repository) { return new Aphront404Response(); } $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/'); if (!$repository->canPerformAutomation()) { return $this->newDialog()->setTitle(pht('Automation Not Configured'))->appendParagraph(pht('You can not run a configuration test for this repository ' . 'because you have not configured repository automation yet. ' . 'Configure it first, then test the configuration.'))->addCancelButton($edit_uri); } if ($request->isFormPost()) { $op = new DrydockTestRepositoryOperation(); $operation = DrydockRepositoryOperation::initializeNewOperation($op)->setAuthorPHID($viewer->getPHID())->setObjectPHID($repository->getPHID())->setRepositoryPHID($repository->getPHID())->setRepositoryTarget('none:')->save(); $operation->scheduleUpdate(); $operation_id = $operation->getID(); $operation_uri = "/drydock/operation/{$operation_id}/"; return id(new AphrontRedirectResponse())->setURI($operation_uri); } return $this->newDialog()->setTitle(pht('Test Automation Configuration'))->appendParagraph(pht('This configuration test will build a working copy of the ' . 'repository and perform some basic validation. If it works, ' . 'your configuration is substantially correct.'))->appendParagraph(pht('The test will not perform any writes against the repository, so ' . 'write operations may still fail even if the test passes. This ' . 'test covers building and reading working copies, but not writing ' . 'to them.'))->appendParagraph(pht('If you run into write failures despite passing this test, ' . 'it suggests that your setup is nearly correct but authentication ' . 'is probably not fully configured.'))->addCancelButton($edit_uri)->addSubmitButton(pht('Start Test')); }
private function buildRepositoryMap(DrydockRepositoryOperation $operation) { $repository = $operation->getRepository(); $target = $operation->getRepositoryTarget(); list($type, $name) = explode(':', $target, 2); switch ($type) { case 'branch': $spec = array('branch' => $name); break; case 'none': $spec = array(); break; default: throw new Exception(pht('Unknown repository operation target type "%s" (in target "%s").', $type, $target)); } $spec['merges'] = $operation->getWorkingCopyMerges(); $map = array(); $map[$repository->getCloneName()] = array('phid' => $repository->getPHID(), 'default' => true) + $spec; return $map; }
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; }