mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-23 07:12:41 +01:00
Make "Land Revision" show merge conflicts more clearly
Summary: Ref T182. We just show "an error happened" right now. Improve this behavior. This error handling chain is a bit ad-hoc for now but we can formalize it as we hit other cases. Test Plan: {F910247} {F910248} Reviewers: chad Reviewed By: chad Maniphest Tasks: T182 Differential Revision: https://secure.phabricator.com/D14343
This commit is contained in:
parent
2326d5f8d0
commit
0b24a6e200
4 changed files with 153 additions and 10 deletions
|
@ -3,6 +3,8 @@
|
||||||
final class DrydockWorkingCopyBlueprintImplementation
|
final class DrydockWorkingCopyBlueprintImplementation
|
||||||
extends DrydockBlueprintImplementation {
|
extends DrydockBlueprintImplementation {
|
||||||
|
|
||||||
|
const PHASE_SQUASHMERGE = 'squashmerge';
|
||||||
|
|
||||||
public function isEnabled() {
|
public function isEnabled() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -288,7 +290,7 @@ final class DrydockWorkingCopyBlueprintImplementation
|
||||||
$merges = idx($spec, 'merges');
|
$merges = idx($spec, 'merges');
|
||||||
if ($merges) {
|
if ($merges) {
|
||||||
foreach ($merges as $merge) {
|
foreach ($merges as $merge) {
|
||||||
$this->applyMerge($interface, $merge);
|
$this->applyMerge($lease, $interface, $merge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,6 +418,7 @@ final class DrydockWorkingCopyBlueprintImplementation
|
||||||
}
|
}
|
||||||
|
|
||||||
private function applyMerge(
|
private function applyMerge(
|
||||||
|
DrydockLease $lease,
|
||||||
DrydockCommandInterface $interface,
|
DrydockCommandInterface $interface,
|
||||||
array $merge) {
|
array $merge) {
|
||||||
|
|
||||||
|
@ -428,15 +431,48 @@ final class DrydockWorkingCopyBlueprintImplementation
|
||||||
$src_ref,
|
$src_ref,
|
||||||
$src_ref);
|
$src_ref);
|
||||||
|
|
||||||
|
$command = csprintf(
|
||||||
|
'git merge --no-stat --squash --ff-only -- %R',
|
||||||
|
$src_ref);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$interface->execx(
|
$interface->execx('%C', $command);
|
||||||
'git merge --no-stat --squash --ff-only -- %s',
|
|
||||||
$src_ref);
|
|
||||||
} catch (CommandException $ex) {
|
} catch (CommandException $ex) {
|
||||||
// TODO: Specifically note this as a merge conflict.
|
$this->setWorkingCopyVCSErrorFromCommandException(
|
||||||
|
$lease,
|
||||||
|
self::PHASE_SQUASHMERGE,
|
||||||
|
$command,
|
||||||
|
$ex);
|
||||||
|
|
||||||
throw $ex;
|
throw $ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function setWorkingCopyVCSErrorFromCommandException(
|
||||||
|
DrydockLease $lease,
|
||||||
|
$phase,
|
||||||
|
$command,
|
||||||
|
CommandException $ex) {
|
||||||
|
|
||||||
|
$error = array(
|
||||||
|
'phase' => $phase,
|
||||||
|
'command' => (string)$command,
|
||||||
|
'raw' => (string)$ex->getCommand(),
|
||||||
|
'err' => $ex->getError(),
|
||||||
|
'stdout' => $ex->getStdout(),
|
||||||
|
'stderr' => $ex->getStderr(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$lease->setAttribute('workingcopy.vcs.error', $error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWorkingCopyVCSError(DrydockLease $lease) {
|
||||||
|
$error = $lease->getAttribute('workingcopy.vcs.error');
|
||||||
|
if (!$error) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return $error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,6 +175,14 @@ final class DrydockRepositoryOperation extends DrydockDAO
|
||||||
return $this->getProperty('exec.leasePHID');
|
return $this->getProperty('exec.leasePHID');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setWorkingCopyVCSError(array $error) {
|
||||||
|
return $this->setProperty('exec.workingcopy.error', $error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWorkingCopyVCSError() {
|
||||||
|
return $this->getProperty('exec.workingcopy.error');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
|
@ -73,12 +73,104 @@ final class DrydockRepositoryOperationStatusView
|
||||||
if ($state != DrydockRepositoryOperation::STATE_FAIL) {
|
if ($state != DrydockRepositoryOperation::STATE_FAIL) {
|
||||||
$item->addAttribute($operation->getOperationCurrentStatus($viewer));
|
$item->addAttribute($operation->getOperationCurrentStatus($viewer));
|
||||||
} else {
|
} else {
|
||||||
// TODO: Make this more useful.
|
$vcs_error = $operation->getWorkingCopyVCSError();
|
||||||
$item->addAttribute(pht('Operation encountered an error.'));
|
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.'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return id(new PHUIObjectItemListView())
|
return id(new PHUIObjectItemListView())
|
||||||
->addItem($item);
|
->addItem($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function renderVCSErrorTable(array $vcs_error) {
|
||||||
|
$rows = array();
|
||||||
|
$rows[] = array(pht('Command'), $vcs_error['command']);
|
||||||
|
$rows[] = array(pht('Error'), $vcs_error['err']);
|
||||||
|
$rows[] = array(pht('Stdout'), $vcs_error['stdout']);
|
||||||
|
$rows[] = array(pht('Stderr'), $vcs_error['stderr']);
|
||||||
|
|
||||||
|
$table = id(new AphrontTableView($rows))
|
||||||
|
->setColumnClasses(
|
||||||
|
array(
|
||||||
|
'header',
|
||||||
|
'wide',
|
||||||
|
));
|
||||||
|
|
||||||
|
return $table;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderDetailToggles(AphrontTableView $table) {
|
||||||
|
$show_id = celerity_generate_unique_node_id();
|
||||||
|
$hide_id = celerity_generate_unique_node_id();
|
||||||
|
$info_id = celerity_generate_unique_node_id();
|
||||||
|
|
||||||
|
Javelin::initBehavior('phabricator-reveal-content');
|
||||||
|
|
||||||
|
$show_details = javelin_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'id' => $show_id,
|
||||||
|
'href' => '#',
|
||||||
|
'sigil' => 'reveal-content',
|
||||||
|
'mustcapture' => true,
|
||||||
|
'meta' => array(
|
||||||
|
'hideIDs' => array($show_id),
|
||||||
|
'showIDs' => array($hide_id, $info_id),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pht('Show Details'));
|
||||||
|
|
||||||
|
$hide_details = javelin_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'id' => $hide_id,
|
||||||
|
'href' => '#',
|
||||||
|
'sigil' => 'reveal-content',
|
||||||
|
'mustcapture' => true,
|
||||||
|
'style' => 'display: none',
|
||||||
|
'meta' => array(
|
||||||
|
'hideIDs' => array($hide_id, $info_id),
|
||||||
|
'showIDs' => array($show_id),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pht('Hide Details'));
|
||||||
|
|
||||||
|
$info = javelin_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'id' => $info_id,
|
||||||
|
'style' => 'display: none',
|
||||||
|
),
|
||||||
|
$table);
|
||||||
|
|
||||||
|
$links = array(
|
||||||
|
$show_details,
|
||||||
|
$hide_details,
|
||||||
|
);
|
||||||
|
|
||||||
|
return array($links, $info);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,9 @@ final class DrydockRepositoryOperationUpdateWorker
|
||||||
// TODO: This is very similar to leasing in Harbormaster, maybe we can
|
// TODO: This is very similar to leasing in Harbormaster, maybe we can
|
||||||
// share some of the logic?
|
// share some of the logic?
|
||||||
|
|
||||||
|
$working_copy = new DrydockWorkingCopyBlueprintImplementation();
|
||||||
|
$working_copy_type = $working_copy->getType();
|
||||||
|
|
||||||
$lease_phid = $operation->getProperty('exec.leasePHID');
|
$lease_phid = $operation->getProperty('exec.leasePHID');
|
||||||
if ($lease_phid) {
|
if ($lease_phid) {
|
||||||
$lease = id(new DrydockLeaseQuery())
|
$lease = id(new DrydockLeaseQuery())
|
||||||
|
@ -103,9 +106,6 @@ final class DrydockRepositoryOperationUpdateWorker
|
||||||
$lease_phid));
|
$lease_phid));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$working_copy_type = id(new DrydockWorkingCopyBlueprintImplementation())
|
|
||||||
->getType();
|
|
||||||
|
|
||||||
$repository = $operation->getRepository();
|
$repository = $operation->getRepository();
|
||||||
|
|
||||||
$allowed_phids = $repository->getAutomationBlueprintPHIDs();
|
$allowed_phids = $repository->getAutomationBlueprintPHIDs();
|
||||||
|
@ -138,6 +138,13 @@ final class DrydockRepositoryOperationUpdateWorker
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$lease->isActive()) {
|
if (!$lease->isActive()) {
|
||||||
|
$vcs_error = $working_copy->getWorkingCopyVCSError($lease);
|
||||||
|
if ($vcs_error) {
|
||||||
|
$operation
|
||||||
|
->setWorkingCopyVCSError($vcs_error)
|
||||||
|
->save();
|
||||||
|
}
|
||||||
|
|
||||||
throw new PhabricatorWorkerPermanentFailureException(
|
throw new PhabricatorWorkerPermanentFailureException(
|
||||||
pht(
|
pht(
|
||||||
'Lease "%s" never activated.',
|
'Lease "%s" never activated.',
|
||||||
|
|
Loading…
Reference in a new issue