mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-03 19:31:02 +01:00
Modularize almost all Harbormaster build message workflows and UI/UX
Summary: Ref T13072. Push nearly all Harbormaster build message logic into the new per-message transaction classes. Test Plan: - Issued every message to Buildables. - Issued every message to Builds. - Looked at a big pile of error messages, couldn't find any typos. - Grepped for affected symbols, etc. - Ran `bin/harbormaster restart ...`. Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T13072 Differential Revision: https://secure.phabricator.com/D21691
This commit is contained in:
parent
8bbee92139
commit
1212dc5fbe
13 changed files with 724 additions and 695 deletions
|
@ -221,8 +221,8 @@ final class HarbormasterBuildStatus extends Phobject {
|
||||||
),
|
),
|
||||||
self::STATUS_PAUSED => array(
|
self::STATUS_PAUSED => array(
|
||||||
'name' => pht('Paused'),
|
'name' => pht('Paused'),
|
||||||
'icon' => 'fa-minus-circle',
|
'icon' => 'fa-pause',
|
||||||
'color' => 'dark',
|
'color' => 'yellow',
|
||||||
'color.ansi' => 'yellow',
|
'color.ansi' => 'yellow',
|
||||||
'isBuilding' => false,
|
'isBuilding' => false,
|
||||||
'isComplete' => false,
|
'isComplete' => false,
|
||||||
|
|
|
@ -22,24 +22,13 @@ final class HarbormasterBuildActionController
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($action) {
|
$xaction =
|
||||||
case HarbormasterBuildCommand::COMMAND_RESTART:
|
HarbormasterBuildMessageTransaction::getTransactionObjectForMessageType(
|
||||||
$can_issue = $build->canRestartBuild();
|
$action);
|
||||||
break;
|
|
||||||
case HarbormasterBuildCommand::COMMAND_PAUSE:
|
|
||||||
$can_issue = $build->canPauseBuild();
|
|
||||||
break;
|
|
||||||
case HarbormasterBuildCommand::COMMAND_RESUME:
|
|
||||||
$can_issue = $build->canResumeBuild();
|
|
||||||
break;
|
|
||||||
case HarbormasterBuildCommand::COMMAND_ABORT:
|
|
||||||
$can_issue = $build->canAbortBuild();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return new Aphront400Response();
|
|
||||||
}
|
|
||||||
|
|
||||||
$build->assertCanIssueCommand($viewer, $action);
|
if (!$xaction) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
switch ($via) {
|
switch ($via) {
|
||||||
case 'buildable':
|
case 'buildable':
|
||||||
|
@ -50,100 +39,29 @@ final class HarbormasterBuildActionController
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->isDialogFormPost() && $can_issue) {
|
try {
|
||||||
$build->sendMessage($viewer, $action);
|
$xaction->assertCanSendMessage($viewer, $build);
|
||||||
|
} catch (HarbormasterRestartException $ex) {
|
||||||
|
return $this->newDialog()
|
||||||
|
->setTitle($ex->getTitle())
|
||||||
|
->appendChild($ex->getBody())
|
||||||
|
->addCancelButton($return_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->isDialogFormPost()) {
|
||||||
|
$build->sendMessage($viewer, $xaction->getHarbormasterBuildMessageType());
|
||||||
return id(new AphrontRedirectResponse())->setURI($return_uri);
|
return id(new AphrontRedirectResponse())->setURI($return_uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($action) {
|
$title = $xaction->newConfirmPromptTitle();
|
||||||
case HarbormasterBuildCommand::COMMAND_RESTART:
|
$body = $xaction->newConfirmPromptBody();
|
||||||
if ($can_issue) {
|
$submit = $xaction->getHarbormasterBuildMessageName();
|
||||||
$title = pht('Really restart build?');
|
|
||||||
$body = pht(
|
|
||||||
'Progress on this build will be discarded and the build will '.
|
|
||||||
'restart. Side effects of the build will occur again. Really '.
|
|
||||||
'restart build?');
|
|
||||||
$submit = pht('Restart Build');
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
$build->assertCanRestartBuild();
|
|
||||||
throw new Exception(pht('Expected to be unable to restart build.'));
|
|
||||||
} catch (HarbormasterRestartException $ex) {
|
|
||||||
$title = $ex->getTitle();
|
|
||||||
$body = $ex->getBody();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case HarbormasterBuildCommand::COMMAND_ABORT:
|
|
||||||
if ($can_issue) {
|
|
||||||
$title = pht('Really abort build?');
|
|
||||||
$body = pht(
|
|
||||||
'Progress on this build will be discarded. Really '.
|
|
||||||
'abort build?');
|
|
||||||
$submit = pht('Abort Build');
|
|
||||||
} else {
|
|
||||||
$title = pht('Unable to Abort Build');
|
|
||||||
$body = pht('You can not abort this build.');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case HarbormasterBuildCommand::COMMAND_PAUSE:
|
|
||||||
if ($can_issue) {
|
|
||||||
$title = pht('Really pause build?');
|
|
||||||
$body = pht(
|
|
||||||
'If you pause this build, work will halt once the current steps '.
|
|
||||||
'complete. You can resume the build later.');
|
|
||||||
$submit = pht('Pause Build');
|
|
||||||
} else {
|
|
||||||
$title = pht('Unable to Pause Build');
|
|
||||||
if ($build->isComplete()) {
|
|
||||||
$body = pht(
|
|
||||||
'This build is already complete. You can not pause a completed '.
|
|
||||||
'build.');
|
|
||||||
} else if ($build->isPaused()) {
|
|
||||||
$body = pht(
|
|
||||||
'This build is already paused. You can not pause a build which '.
|
|
||||||
'has already been paused.');
|
|
||||||
} else if ($build->isPausing()) {
|
|
||||||
$body = pht(
|
|
||||||
'This build is already pausing. You can not reissue a pause '.
|
|
||||||
'command to a pausing build.');
|
|
||||||
} else {
|
|
||||||
$body = pht(
|
|
||||||
'This build can not be paused.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case HarbormasterBuildCommand::COMMAND_RESUME:
|
|
||||||
if ($can_issue) {
|
|
||||||
$title = pht('Really resume build?');
|
|
||||||
$body = pht(
|
|
||||||
'Work will continue on the build. Really resume?');
|
|
||||||
$submit = pht('Resume Build');
|
|
||||||
} else {
|
|
||||||
$title = pht('Unable to Resume Build');
|
|
||||||
if ($build->isResuming()) {
|
|
||||||
$body = pht(
|
|
||||||
'This build is already resuming. You can not reissue a resume '.
|
|
||||||
'command to a resuming build.');
|
|
||||||
} else if (!$build->isPaused()) {
|
|
||||||
$body = pht(
|
|
||||||
'This build is not paused. You can only resume a paused '.
|
|
||||||
'build.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$dialog = $this->newDialog()
|
return $this->newDialog()
|
||||||
->setTitle($title)
|
->setTitle($title)
|
||||||
->appendChild($body)
|
->appendChild($body)
|
||||||
->addCancelButton($return_uri);
|
->addCancelButton($return_uri)
|
||||||
|
->addSubmitButton($submit);
|
||||||
if ($can_issue) {
|
|
||||||
$dialog->addSubmitButton($submit);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $dialog;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -533,64 +533,32 @@ final class HarbormasterBuildViewController
|
||||||
|
|
||||||
$curtain = $this->newCurtainView($build);
|
$curtain = $this->newCurtainView($build);
|
||||||
|
|
||||||
$can_restart =
|
$messages = array(
|
||||||
$build->canRestartBuild() &&
|
new HarbormasterBuildMessageRestartTransaction(),
|
||||||
$build->canIssueCommand(
|
new HarbormasterBuildMessagePauseTransaction(),
|
||||||
$viewer,
|
new HarbormasterBuildMessageResumeTransaction(),
|
||||||
HarbormasterBuildCommand::COMMAND_RESTART);
|
new HarbormasterBuildMessageAbortTransaction(),
|
||||||
|
);
|
||||||
|
|
||||||
$can_pause =
|
foreach ($messages as $message) {
|
||||||
$build->canPauseBuild() &&
|
$can_send = $message->canSendMessage($viewer, $build);
|
||||||
$build->canIssueCommand(
|
|
||||||
$viewer,
|
|
||||||
HarbormasterBuildCommand::COMMAND_PAUSE);
|
|
||||||
|
|
||||||
$can_resume =
|
$message_uri = urisprintf(
|
||||||
$build->canResumeBuild() &&
|
'/build/%s/%d/',
|
||||||
$build->canIssueCommand(
|
$message->getHarbormasterBuildMessageType(),
|
||||||
$viewer,
|
$id);
|
||||||
HarbormasterBuildCommand::COMMAND_RESUME);
|
$message_uri = $this->getApplicationURI($message_uri);
|
||||||
|
|
||||||
$can_abort =
|
$action = id(new PhabricatorActionView())
|
||||||
$build->canAbortBuild() &&
|
->setName($message->getHarbormasterBuildMessageName())
|
||||||
$build->canIssueCommand(
|
->setIcon($message->getIcon())
|
||||||
$viewer,
|
->setHref($message_uri)
|
||||||
HarbormasterBuildCommand::COMMAND_ABORT);
|
->setDisabled(!$can_send)
|
||||||
|
->setWorkflow(true);
|
||||||
|
|
||||||
$curtain->addAction(
|
$curtain->addAction($action);
|
||||||
id(new PhabricatorActionView())
|
|
||||||
->setName(pht('Restart Build'))
|
|
||||||
->setIcon('fa-repeat')
|
|
||||||
->setHref($this->getApplicationURI('/build/restart/'.$id.'/'))
|
|
||||||
->setDisabled(!$can_restart)
|
|
||||||
->setWorkflow(true));
|
|
||||||
|
|
||||||
if ($build->canResumeBuild()) {
|
|
||||||
$curtain->addAction(
|
|
||||||
id(new PhabricatorActionView())
|
|
||||||
->setName(pht('Resume Build'))
|
|
||||||
->setIcon('fa-play')
|
|
||||||
->setHref($this->getApplicationURI('/build/resume/'.$id.'/'))
|
|
||||||
->setDisabled(!$can_resume)
|
|
||||||
->setWorkflow(true));
|
|
||||||
} else {
|
|
||||||
$curtain->addAction(
|
|
||||||
id(new PhabricatorActionView())
|
|
||||||
->setName(pht('Pause Build'))
|
|
||||||
->setIcon('fa-pause')
|
|
||||||
->setHref($this->getApplicationURI('/build/pause/'.$id.'/'))
|
|
||||||
->setDisabled(!$can_pause)
|
|
||||||
->setWorkflow(true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$curtain->addAction(
|
|
||||||
id(new PhabricatorActionView())
|
|
||||||
->setName(pht('Abort Build'))
|
|
||||||
->setIcon('fa-exclamation-triangle')
|
|
||||||
->setHref($this->getApplicationURI('/build/abort/'.$id.'/'))
|
|
||||||
->setDisabled(!$can_abort)
|
|
||||||
->setWorkflow(true));
|
|
||||||
|
|
||||||
return $curtain;
|
return $curtain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,54 +22,89 @@ final class HarbormasterBuildableActionController
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
$issuable = array();
|
$message =
|
||||||
|
HarbormasterBuildMessageTransaction::getTransactionObjectForMessageType(
|
||||||
$builds = $buildable->getBuilds();
|
$action);
|
||||||
foreach ($builds as $key => $build) {
|
if (!$message) {
|
||||||
switch ($action) {
|
return new Aphront404Response();
|
||||||
case HarbormasterBuildCommand::COMMAND_RESTART:
|
|
||||||
if ($build->canRestartBuild()) {
|
|
||||||
$issuable[$key] = $build;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case HarbormasterBuildCommand::COMMAND_PAUSE:
|
|
||||||
if ($build->canPauseBuild()) {
|
|
||||||
$issuable[$key] = $build;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case HarbormasterBuildCommand::COMMAND_RESUME:
|
|
||||||
if ($build->canResumeBuild()) {
|
|
||||||
$issuable[$key] = $build;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case HarbormasterBuildCommand::COMMAND_ABORT:
|
|
||||||
if ($build->canAbortBuild()) {
|
|
||||||
$issuable[$key] = $build;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return new Aphront400Response();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$restricted = false;
|
|
||||||
foreach ($issuable as $key => $build) {
|
|
||||||
if (!$build->canIssueCommand($viewer, $action)) {
|
|
||||||
$restricted = true;
|
|
||||||
unset($issuable[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$building = false;
|
|
||||||
foreach ($issuable as $key => $build) {
|
|
||||||
if ($build->isBuilding()) {
|
|
||||||
$building = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$return_uri = '/'.$buildable->getMonogram();
|
$return_uri = '/'.$buildable->getMonogram();
|
||||||
if ($request->isDialogFormPost() && $issuable) {
|
|
||||||
|
// See T13348. Actions may apply to only a subset of builds, so give the
|
||||||
|
// user a preview of what will happen.
|
||||||
|
|
||||||
|
$can_send = array();
|
||||||
|
|
||||||
|
$rows = array();
|
||||||
|
$builds = $buildable->getBuilds();
|
||||||
|
foreach ($builds as $key => $build) {
|
||||||
|
$exception = null;
|
||||||
|
try {
|
||||||
|
$message->assertCanSendMessage($viewer, $build);
|
||||||
|
$can_send[$key] = $build;
|
||||||
|
} catch (HarbormasterRestartException $ex) {
|
||||||
|
$exception = $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$exception) {
|
||||||
|
$icon_icon = $message->getIcon();
|
||||||
|
$icon_color = 'green';
|
||||||
|
|
||||||
|
$title = $message->getHarbormasterBuildMessageName();
|
||||||
|
$body = $message->getHarbormasterBuildableMessageEffect();
|
||||||
|
} else {
|
||||||
|
$icon_icon = 'fa-times';
|
||||||
|
$icon_color = 'red';
|
||||||
|
|
||||||
|
$title = $ex->getTitle();
|
||||||
|
$body = $ex->getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
$icon = id(new PHUIIconView())
|
||||||
|
->setIcon($icon_icon)
|
||||||
|
->setColor($icon_color);
|
||||||
|
|
||||||
|
$build_name = phutil_tag(
|
||||||
|
'a',
|
||||||
|
array(
|
||||||
|
'href' => $build->getURI(),
|
||||||
|
'target' => '_blank',
|
||||||
|
),
|
||||||
|
pht('%s %s', $build->getObjectName(), $build->getName()));
|
||||||
|
|
||||||
|
$rows[] = array(
|
||||||
|
$icon,
|
||||||
|
$build_name,
|
||||||
|
$title,
|
||||||
|
$body,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = id(new AphrontTableView($rows))
|
||||||
|
->setHeaders(
|
||||||
|
array(
|
||||||
|
null,
|
||||||
|
pht('Build'),
|
||||||
|
pht('Action'),
|
||||||
|
pht('Details'),
|
||||||
|
))
|
||||||
|
->setColumnClasses(
|
||||||
|
array(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'pri',
|
||||||
|
'wide',
|
||||||
|
));
|
||||||
|
|
||||||
|
$table = phutil_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'class' => 'mlt mlb',
|
||||||
|
),
|
||||||
|
$table);
|
||||||
|
|
||||||
|
if ($request->isDialogFormPost() && $can_send) {
|
||||||
$editor = id(new HarbormasterBuildableTransactionEditor())
|
$editor = id(new HarbormasterBuildableTransactionEditor())
|
||||||
->setActor($viewer)
|
->setActor($viewer)
|
||||||
->setContentSourceFromRequest($request)
|
->setContentSourceFromRequest($request)
|
||||||
|
@ -88,233 +123,65 @@ final class HarbormasterBuildableActionController
|
||||||
->setContinueOnNoEffect(true)
|
->setContinueOnNoEffect(true)
|
||||||
->setContinueOnMissingFields(true);
|
->setContinueOnMissingFields(true);
|
||||||
|
|
||||||
foreach ($issuable as $build) {
|
foreach ($can_send as $build) {
|
||||||
$xaction = id(new HarbormasterBuildTransaction())
|
$build->sendMessage(
|
||||||
->setTransactionType(HarbormasterBuildTransaction::TYPE_COMMAND)
|
$viewer,
|
||||||
->setNewValue($action);
|
$message->getHarbormasterBuildMessageType());
|
||||||
$build_editor->applyTransactions($build, array($xaction));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return id(new AphrontRedirectResponse())->setURI($return_uri);
|
return id(new AphrontRedirectResponse())->setURI($return_uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
$width = AphrontDialogView::WIDTH_DEFAULT;
|
if (!$builds) {
|
||||||
|
$title = pht('No Builds');
|
||||||
|
$body = pht(
|
||||||
|
'This buildable has no builds, so you can not issue any commands.');
|
||||||
|
} else {
|
||||||
|
if ($can_send) {
|
||||||
|
$title = $message->newBuildableConfirmPromptTitle(
|
||||||
|
$builds,
|
||||||
|
$can_send);
|
||||||
|
|
||||||
switch ($action) {
|
$body = $message->newBuildableConfirmPromptBody(
|
||||||
case HarbormasterBuildCommand::COMMAND_RESTART:
|
$builds,
|
||||||
// See T13348. The "Restart Builds" action may restart only a subset
|
$can_send);
|
||||||
// of builds, so show the user a preview of which builds will actually
|
} else {
|
||||||
// restart.
|
$title = pht('Unable to Send Command');
|
||||||
|
$body = pht(
|
||||||
|
'You can not send this command to any of the current builds '.
|
||||||
|
'for this buildable.');
|
||||||
|
}
|
||||||
|
|
||||||
$body = array();
|
$body = array(
|
||||||
|
pht('Builds for this buildable:'),
|
||||||
if ($issuable) {
|
$table,
|
||||||
$title = pht('Restart Builds');
|
$body,
|
||||||
$submit = pht('Restart Builds');
|
);
|
||||||
} else {
|
|
||||||
$title = pht('Unable to Restart Builds');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($builds) {
|
|
||||||
$width = AphrontDialogView::WIDTH_FORM;
|
|
||||||
|
|
||||||
$body[] = pht('Builds for this buildable:');
|
|
||||||
|
|
||||||
$rows = array();
|
|
||||||
foreach ($builds as $key => $build) {
|
|
||||||
if (isset($issuable[$key])) {
|
|
||||||
$icon = id(new PHUIIconView())
|
|
||||||
->setIcon('fa-repeat green');
|
|
||||||
$build_note = pht('Will Restart');
|
|
||||||
} else {
|
|
||||||
$icon = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
$build->assertCanRestartBuild();
|
|
||||||
} catch (HarbormasterRestartException $ex) {
|
|
||||||
$icon = id(new PHUIIconView())
|
|
||||||
->setIcon('fa-times red');
|
|
||||||
$build_note = pht(
|
|
||||||
'%s: %s',
|
|
||||||
phutil_tag('strong', array(), pht('Not Restartable')),
|
|
||||||
$ex->getTitle());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$icon) {
|
|
||||||
try {
|
|
||||||
$build->assertCanIssueCommand($viewer, $action);
|
|
||||||
} catch (PhabricatorPolicyException $ex) {
|
|
||||||
$icon = id(new PHUIIconView())
|
|
||||||
->setIcon('fa-lock red');
|
|
||||||
$build_note = pht(
|
|
||||||
'%s: %s',
|
|
||||||
phutil_tag('strong', array(), pht('Not Restartable')),
|
|
||||||
pht('You do not have permission to restart this build.'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$icon) {
|
|
||||||
$icon = id(new PHUIIconView())
|
|
||||||
->setIcon('fa-times red');
|
|
||||||
$build_note = pht('Will Not Restart');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$build_name = phutil_tag(
|
|
||||||
'a',
|
|
||||||
array(
|
|
||||||
'href' => $build->getURI(),
|
|
||||||
'target' => '_blank',
|
|
||||||
),
|
|
||||||
pht('%s %s', $build->getObjectName(), $build->getName()));
|
|
||||||
|
|
||||||
$rows[] = array(
|
|
||||||
$icon,
|
|
||||||
$build_name,
|
|
||||||
$build_note,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$table = id(new AphrontTableView($rows))
|
|
||||||
->setHeaders(
|
|
||||||
array(
|
|
||||||
null,
|
|
||||||
pht('Build'),
|
|
||||||
pht('Action'),
|
|
||||||
))
|
|
||||||
->setColumnClasses(
|
|
||||||
array(
|
|
||||||
null,
|
|
||||||
'pri',
|
|
||||||
'wide',
|
|
||||||
));
|
|
||||||
|
|
||||||
$table = phutil_tag(
|
|
||||||
'div',
|
|
||||||
array(
|
|
||||||
'class' => 'mlt mlb',
|
|
||||||
),
|
|
||||||
$table);
|
|
||||||
|
|
||||||
$body[] = $table;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($issuable) {
|
|
||||||
$warnings = array();
|
|
||||||
|
|
||||||
if ($restricted) {
|
|
||||||
$warnings[] = pht(
|
|
||||||
'You only have permission to restart some builds.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($building) {
|
|
||||||
$warnings[] = pht(
|
|
||||||
'Progress on running builds will be discarded.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$warnings[] = pht(
|
|
||||||
'When a build is restarted, side effects associated with '.
|
|
||||||
'the build may occur again.');
|
|
||||||
|
|
||||||
$body[] = id(new PHUIInfoView())
|
|
||||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
|
||||||
->setErrors($warnings);
|
|
||||||
|
|
||||||
$body[] = pht('Really restart builds?');
|
|
||||||
} else {
|
|
||||||
if ($restricted) {
|
|
||||||
$body[] = pht('You do not have permission to restart any builds.');
|
|
||||||
} else {
|
|
||||||
$body[] = pht('No builds can be restarted.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case HarbormasterBuildCommand::COMMAND_PAUSE:
|
|
||||||
if ($issuable) {
|
|
||||||
$title = pht('Really pause builds?');
|
|
||||||
|
|
||||||
if ($restricted) {
|
|
||||||
$body = pht(
|
|
||||||
'You only have permission to pause some builds. Once the '.
|
|
||||||
'current steps complete, work will halt on builds you have '.
|
|
||||||
'permission to pause. You can resume the builds later.');
|
|
||||||
} else {
|
|
||||||
$body = pht(
|
|
||||||
'If you pause all builds, work will halt once the current steps '.
|
|
||||||
'complete. You can resume the builds later.');
|
|
||||||
}
|
|
||||||
$submit = pht('Pause Builds');
|
|
||||||
} else {
|
|
||||||
$title = pht('Unable to Pause Builds');
|
|
||||||
|
|
||||||
if ($restricted) {
|
|
||||||
$body = pht('You do not have permission to pause any builds.');
|
|
||||||
} else {
|
|
||||||
$body = pht('No builds can be paused.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case HarbormasterBuildCommand::COMMAND_ABORT:
|
|
||||||
if ($issuable) {
|
|
||||||
$title = pht('Really abort builds?');
|
|
||||||
if ($restricted) {
|
|
||||||
$body = pht(
|
|
||||||
'You only have permission to abort some builds. Work will '.
|
|
||||||
'halt immediately on builds you have permission to abort. '.
|
|
||||||
'Progress will be discarded, and builds must be completely '.
|
|
||||||
'restarted if you want them to complete.');
|
|
||||||
} else {
|
|
||||||
$body = pht(
|
|
||||||
'If you abort all builds, work will halt immediately. Work '.
|
|
||||||
'will be discarded, and builds must be completely restarted.');
|
|
||||||
}
|
|
||||||
$submit = pht('Abort Builds');
|
|
||||||
} else {
|
|
||||||
$title = pht('Unable to Abort Builds');
|
|
||||||
|
|
||||||
if ($restricted) {
|
|
||||||
$body = pht('You do not have permission to abort any builds.');
|
|
||||||
} else {
|
|
||||||
$body = pht('No builds can be aborted.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case HarbormasterBuildCommand::COMMAND_RESUME:
|
|
||||||
if ($issuable) {
|
|
||||||
$title = pht('Really resume builds?');
|
|
||||||
if ($restricted) {
|
|
||||||
$body = pht(
|
|
||||||
'You only have permission to resume some builds. Work will '.
|
|
||||||
'continue on builds you have permission to resume.');
|
|
||||||
} else {
|
|
||||||
$body = pht('Work will continue on all builds. Really resume?');
|
|
||||||
}
|
|
||||||
|
|
||||||
$submit = pht('Resume Builds');
|
|
||||||
} else {
|
|
||||||
$title = pht('Unable to Resume Builds');
|
|
||||||
if ($restricted) {
|
|
||||||
$body = pht('You do not have permission to resume any builds.');
|
|
||||||
} else {
|
|
||||||
$body = pht('No builds can be resumed.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$dialog = id(new AphrontDialogView())
|
$warnings = $message->newBuildableConfirmPromptWarnings(
|
||||||
->setUser($viewer)
|
$builds,
|
||||||
->setWidth($width)
|
$can_send);
|
||||||
|
|
||||||
|
if ($warnings) {
|
||||||
|
$body[] = id(new PHUIInfoView())
|
||||||
|
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
||||||
|
->setErrors($warnings);
|
||||||
|
}
|
||||||
|
|
||||||
|
$submit = $message->getHarbormasterBuildableMessageName();
|
||||||
|
|
||||||
|
$dialog = $this->newDialog()
|
||||||
|
->setWidth(AphrontDialogView::WIDTH_FULL)
|
||||||
->setTitle($title)
|
->setTitle($title)
|
||||||
->appendChild($body)
|
->appendChild($body)
|
||||||
->addCancelButton($return_uri);
|
->addCancelButton($return_uri);
|
||||||
|
|
||||||
if ($issuable) {
|
if ($can_send) {
|
||||||
$dialog->addSubmitButton($submit);
|
$dialog->addSubmitButton($submit);
|
||||||
}
|
}
|
||||||
|
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
return $dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,76 +87,40 @@ final class HarbormasterBuildableViewController
|
||||||
$buildable,
|
$buildable,
|
||||||
PhabricatorPolicyCapability::CAN_EDIT);
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
|
||||||
$can_restart = false;
|
$messages = array(
|
||||||
$can_resume = false;
|
new HarbormasterBuildMessageRestartTransaction(),
|
||||||
$can_pause = false;
|
new HarbormasterBuildMessagePauseTransaction(),
|
||||||
$can_abort = false;
|
new HarbormasterBuildMessageResumeTransaction(),
|
||||||
|
new HarbormasterBuildMessageAbortTransaction(),
|
||||||
|
);
|
||||||
|
|
||||||
$command_restart = HarbormasterBuildCommand::COMMAND_RESTART;
|
foreach ($messages as $message) {
|
||||||
$command_resume = HarbormasterBuildCommand::COMMAND_RESUME;
|
|
||||||
$command_pause = HarbormasterBuildCommand::COMMAND_PAUSE;
|
|
||||||
$command_abort = HarbormasterBuildCommand::COMMAND_ABORT;
|
|
||||||
|
|
||||||
foreach ($buildable->getBuilds() as $build) {
|
// Messages are enabled if they can be sent to at least one build.
|
||||||
if ($build->canRestartBuild()) {
|
$can_send = false;
|
||||||
if ($build->canIssueCommand($viewer, $command_restart)) {
|
foreach ($buildable->getBuilds() as $build) {
|
||||||
$can_restart = true;
|
$can_send = $message->canSendMessage($viewer, $build);
|
||||||
}
|
if ($can_send) {
|
||||||
}
|
break;
|
||||||
if ($build->canResumeBuild()) {
|
|
||||||
if ($build->canIssueCommand($viewer, $command_resume)) {
|
|
||||||
$can_resume = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($build->canPauseBuild()) {
|
|
||||||
if ($build->canIssueCommand($viewer, $command_pause)) {
|
|
||||||
$can_pause = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($build->canAbortBuild()) {
|
|
||||||
if ($build->canIssueCommand($viewer, $command_abort)) {
|
|
||||||
$can_abort = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$message_uri = urisprintf(
|
||||||
|
'/buildable/%d/%s/',
|
||||||
|
$id,
|
||||||
|
$message->getHarbormasterBuildMessageType());
|
||||||
|
$message_uri = $this->getApplicationURI($message_uri);
|
||||||
|
|
||||||
|
$action = id(new PhabricatorActionView())
|
||||||
|
->setName($message->getHarbormasterBuildableMessageName())
|
||||||
|
->setIcon($message->getIcon())
|
||||||
|
->setHref($message_uri)
|
||||||
|
->setDisabled(!$can_send || !$can_edit)
|
||||||
|
->setWorkflow(true);
|
||||||
|
|
||||||
|
$curtain->addAction($action);
|
||||||
}
|
}
|
||||||
|
|
||||||
$restart_uri = "buildable/{$id}/restart/";
|
|
||||||
$pause_uri = "buildable/{$id}/pause/";
|
|
||||||
$resume_uri = "buildable/{$id}/resume/";
|
|
||||||
$abort_uri = "buildable/{$id}/abort/";
|
|
||||||
|
|
||||||
$curtain->addAction(
|
|
||||||
id(new PhabricatorActionView())
|
|
||||||
->setIcon('fa-repeat')
|
|
||||||
->setName(pht('Restart Builds'))
|
|
||||||
->setHref($this->getApplicationURI($restart_uri))
|
|
||||||
->setWorkflow(true)
|
|
||||||
->setDisabled(!$can_restart || !$can_edit));
|
|
||||||
|
|
||||||
$curtain->addAction(
|
|
||||||
id(new PhabricatorActionView())
|
|
||||||
->setIcon('fa-pause')
|
|
||||||
->setName(pht('Pause Builds'))
|
|
||||||
->setHref($this->getApplicationURI($pause_uri))
|
|
||||||
->setWorkflow(true)
|
|
||||||
->setDisabled(!$can_pause || !$can_edit));
|
|
||||||
|
|
||||||
$curtain->addAction(
|
|
||||||
id(new PhabricatorActionView())
|
|
||||||
->setIcon('fa-play')
|
|
||||||
->setName(pht('Resume Builds'))
|
|
||||||
->setHref($this->getApplicationURI($resume_uri))
|
|
||||||
->setWorkflow(true)
|
|
||||||
->setDisabled(!$can_resume || !$can_edit));
|
|
||||||
|
|
||||||
$curtain->addAction(
|
|
||||||
id(new PhabricatorActionView())
|
|
||||||
->setIcon('fa-exclamation-triangle')
|
|
||||||
->setName(pht('Abort Builds'))
|
|
||||||
->setHref($this->getApplicationURI($abort_uri))
|
|
||||||
->setWorkflow(true)
|
|
||||||
->setDisabled(!$can_abort || !$can_edit));
|
|
||||||
|
|
||||||
return $curtain;
|
return $curtain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,56 +162,17 @@ final class HarbormasterBuildableViewController
|
||||||
->setUser($viewer);
|
->setUser($viewer);
|
||||||
foreach ($buildable->getBuilds() as $build) {
|
foreach ($buildable->getBuilds() as $build) {
|
||||||
$view_uri = $this->getApplicationURI('/build/'.$build->getID().'/');
|
$view_uri = $this->getApplicationURI('/build/'.$build->getID().'/');
|
||||||
|
|
||||||
$item = id(new PHUIObjectItemView())
|
$item = id(new PHUIObjectItemView())
|
||||||
->setObjectName(pht('Build %d', $build->getID()))
|
->setObjectName(pht('Build %d', $build->getID()))
|
||||||
->setHeader($build->getName())
|
->setHeader($build->getName())
|
||||||
->setHref($view_uri);
|
->setHref($view_uri);
|
||||||
|
|
||||||
$status = $build->getBuildStatus();
|
$status = $build->getBuildPendingStatusObject();
|
||||||
$status_color = HarbormasterBuildStatus::getBuildStatusColor($status);
|
|
||||||
$status_name = HarbormasterBuildStatus::getBuildStatusName($status);
|
|
||||||
$item->setStatusIcon('fa-dot-circle-o '.$status_color, $status_name);
|
|
||||||
$item->addAttribute($status_name);
|
|
||||||
|
|
||||||
if ($build->isRestarting()) {
|
$item->setStatusIcon(
|
||||||
$item->addIcon('fa-repeat', pht('Restarting'));
|
$status->getIconIcon().' '.$status->getIconColor(),
|
||||||
} else if ($build->isPausing()) {
|
$status->getName());
|
||||||
$item->addIcon('fa-pause', pht('Pausing'));
|
|
||||||
} else if ($build->isResuming()) {
|
|
||||||
$item->addIcon('fa-play', pht('Resuming'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$build_id = $build->getID();
|
|
||||||
|
|
||||||
$restart_uri = "build/restart/{$build_id}/buildable/";
|
|
||||||
$resume_uri = "build/resume/{$build_id}/buildable/";
|
|
||||||
$pause_uri = "build/pause/{$build_id}/buildable/";
|
|
||||||
$abort_uri = "build/abort/{$build_id}/buildable/";
|
|
||||||
|
|
||||||
$item->addAction(
|
|
||||||
id(new PHUIListItemView())
|
|
||||||
->setIcon('fa-repeat')
|
|
||||||
->setName(pht('Restart'))
|
|
||||||
->setHref($this->getApplicationURI($restart_uri))
|
|
||||||
->setWorkflow(true)
|
|
||||||
->setDisabled(!$build->canRestartBuild()));
|
|
||||||
|
|
||||||
if ($build->canResumeBuild()) {
|
|
||||||
$item->addAction(
|
|
||||||
id(new PHUIListItemView())
|
|
||||||
->setIcon('fa-play')
|
|
||||||
->setName(pht('Resume'))
|
|
||||||
->setHref($this->getApplicationURI($resume_uri))
|
|
||||||
->setWorkflow(true));
|
|
||||||
} else {
|
|
||||||
$item->addAction(
|
|
||||||
id(new PHUIListItemView())
|
|
||||||
->setIcon('fa-pause')
|
|
||||||
->setName(pht('Pause'))
|
|
||||||
->setHref($this->getApplicationURI($pause_uri))
|
|
||||||
->setWorkflow(true)
|
|
||||||
->setDisabled(!$build->canPauseBuild()));
|
|
||||||
}
|
|
||||||
|
|
||||||
$targets = $build->getBuildTargets();
|
$targets = $build->getBuildTargets();
|
||||||
|
|
||||||
|
|
|
@ -30,4 +30,13 @@ final class HarbormasterRestartException extends Exception {
|
||||||
return $this->body;
|
return $this->body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function newDisplayString() {
|
||||||
|
$title = $this->getTitle();
|
||||||
|
|
||||||
|
$body = $this->getBody();
|
||||||
|
$body = implode("\n\n", $body);
|
||||||
|
|
||||||
|
return pht('%s: %s', $title, $body);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,21 +61,24 @@ final class HarbormasterManagementRestartWorkflow
|
||||||
throw new ArcanistUserAbortException();
|
throw new ArcanistUserAbortException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$message = new HarbormasterBuildMessageRestartTransaction();
|
||||||
|
|
||||||
foreach ($builds as $build) {
|
foreach ($builds as $build) {
|
||||||
$this->logInfo(
|
$this->logInfo(
|
||||||
pht('RESTARTING'),
|
pht('RESTARTING'),
|
||||||
pht('Build %d: %s', $build->getID(), $build->getName()));
|
pht('Build %d: %s', $build->getID(), $build->getName()));
|
||||||
|
|
||||||
if (!$build->canRestartBuild()) {
|
try {
|
||||||
|
$message->assertCanSendMessage($viewer, $build);
|
||||||
|
} catch (HarbormasterRestartException $ex) {
|
||||||
$this->logWarn(
|
$this->logWarn(
|
||||||
pht('INVALID'),
|
pht('INVALID'),
|
||||||
pht('Build can not be restarted.'));
|
$ex->newDisplayString());
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$build->sendMessage(
|
$build->sendMessage(
|
||||||
$viewer,
|
$viewer,
|
||||||
HarbormasterBuildCommand::COMMAND_RESTART);
|
$message->getHarbormasterBuildMessageType());
|
||||||
|
|
||||||
$this->logOkay(
|
$this->logOkay(
|
||||||
pht('QUEUED'),
|
pht('QUEUED'),
|
||||||
|
|
|
@ -315,107 +315,6 @@ final class HarbormasterBuild extends HarbormasterDAO
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canRestartBuild() {
|
|
||||||
try {
|
|
||||||
$this->assertCanRestartBuild();
|
|
||||||
return true;
|
|
||||||
} catch (HarbormasterRestartException $ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function assertCanRestartBuild() {
|
|
||||||
if ($this->isAutobuild()) {
|
|
||||||
throw new HarbormasterRestartException(
|
|
||||||
pht('Can Not Restart Autobuild'),
|
|
||||||
pht(
|
|
||||||
'This build can not be restarted because it is an automatic '.
|
|
||||||
'build.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$restartable = HarbormasterBuildPlanBehavior::BEHAVIOR_RESTARTABLE;
|
|
||||||
$plan = $this->getBuildPlan();
|
|
||||||
|
|
||||||
// See T13526. Users who can't see the "BuildPlan" can end up here with
|
|
||||||
// no object. This is highly questionable.
|
|
||||||
if (!$plan) {
|
|
||||||
throw new HarbormasterRestartException(
|
|
||||||
pht('No Build Plan Permission'),
|
|
||||||
pht(
|
|
||||||
'You can not restart this build because you do not have '.
|
|
||||||
'permission to access the build plan.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$option = HarbormasterBuildPlanBehavior::getBehavior($restartable)
|
|
||||||
->getPlanOption($plan);
|
|
||||||
$option_key = $option->getKey();
|
|
||||||
|
|
||||||
$never_restartable = HarbormasterBuildPlanBehavior::RESTARTABLE_NEVER;
|
|
||||||
$is_never = ($option_key === $never_restartable);
|
|
||||||
if ($is_never) {
|
|
||||||
throw new HarbormasterRestartException(
|
|
||||||
pht('Build Plan Prevents Restart'),
|
|
||||||
pht(
|
|
||||||
'This build can not be restarted because the build plan is '.
|
|
||||||
'configured to prevent the build from restarting.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$failed_restartable = HarbormasterBuildPlanBehavior::RESTARTABLE_IF_FAILED;
|
|
||||||
$is_failed = ($option_key === $failed_restartable);
|
|
||||||
if ($is_failed) {
|
|
||||||
if (!$this->isFailed()) {
|
|
||||||
throw new HarbormasterRestartException(
|
|
||||||
pht('Only Restartable if Failed'),
|
|
||||||
pht(
|
|
||||||
'This build can not be restarted because the build plan is '.
|
|
||||||
'configured to prevent the build from restarting unless it '.
|
|
||||||
'has failed, and it has not failed.'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->isRestarting()) {
|
|
||||||
throw new HarbormasterRestartException(
|
|
||||||
pht('Already Restarting'),
|
|
||||||
pht(
|
|
||||||
'This build is already restarting. You can not reissue a restart '.
|
|
||||||
'command to a restarting build.'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function canPauseBuild() {
|
|
||||||
if ($this->isAutobuild()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !$this->isComplete() &&
|
|
||||||
!$this->isPaused() &&
|
|
||||||
!$this->isPausing() &&
|
|
||||||
!$this->isRestarting() &&
|
|
||||||
!$this->isAborting();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function canAbortBuild() {
|
|
||||||
if ($this->isAutobuild()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
!$this->isComplete() &&
|
|
||||||
!$this->isAborting();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function canResumeBuild() {
|
|
||||||
if ($this->isAutobuild()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
$this->isPaused() &&
|
|
||||||
!$this->isResuming() &&
|
|
||||||
!$this->isRestarting() &&
|
|
||||||
!$this->isAborting();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isPausing() {
|
public function isPausing() {
|
||||||
return $this->getBuildPendingStatusObject()->isPausing();
|
return $this->getBuildPendingStatusObject()->isPausing();
|
||||||
}
|
}
|
||||||
|
@ -451,51 +350,6 @@ final class HarbormasterBuild extends HarbormasterDAO
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canIssueCommand(PhabricatorUser $viewer, $command) {
|
|
||||||
try {
|
|
||||||
$this->assertCanIssueCommand($viewer, $command);
|
|
||||||
return true;
|
|
||||||
} catch (Exception $ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function assertCanIssueCommand(PhabricatorUser $viewer, $command) {
|
|
||||||
$plan = $this->getBuildPlan();
|
|
||||||
|
|
||||||
// See T13526. Users without permission to access the build plan can
|
|
||||||
// currently end up here with no "BuildPlan" object.
|
|
||||||
if (!$plan) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$need_edit = true;
|
|
||||||
switch ($command) {
|
|
||||||
case HarbormasterBuildCommand::COMMAND_RESTART:
|
|
||||||
case HarbormasterBuildCommand::COMMAND_PAUSE:
|
|
||||||
case HarbormasterBuildCommand::COMMAND_RESUME:
|
|
||||||
case HarbormasterBuildCommand::COMMAND_ABORT:
|
|
||||||
if ($plan->canRunWithoutEditCapability()) {
|
|
||||||
$need_edit = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception(
|
|
||||||
pht(
|
|
||||||
'Invalid Harbormaster build command "%s".',
|
|
||||||
$command));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issuing these commands requires that you be able to edit the build, to
|
|
||||||
// prevent enemy engineers from sabotaging your builds. See T9614.
|
|
||||||
if ($need_edit) {
|
|
||||||
PhabricatorPolicyFilter::requireCapability(
|
|
||||||
$viewer,
|
|
||||||
$plan,
|
|
||||||
PhabricatorPolicyCapability::CAN_EDIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function sendMessage(PhabricatorUser $viewer, $message_type) {
|
public function sendMessage(PhabricatorUser $viewer, $message_type) {
|
||||||
HarbormasterBuildMessage::initializeNewMessage($viewer)
|
HarbormasterBuildMessage::initializeNewMessage($viewer)
|
||||||
->setReceiverPHID($this->getPHID())
|
->setReceiverPHID($this->getPHID())
|
||||||
|
|
|
@ -4,9 +4,51 @@ final class HarbormasterBuildMessageAbortTransaction
|
||||||
extends HarbormasterBuildMessageTransaction {
|
extends HarbormasterBuildMessageTransaction {
|
||||||
|
|
||||||
const TRANSACTIONTYPE = 'message/abort';
|
const TRANSACTIONTYPE = 'message/abort';
|
||||||
|
const MESSAGETYPE = 'abort';
|
||||||
|
|
||||||
public function getMessageType() {
|
public function getHarbormasterBuildMessageName() {
|
||||||
return 'abort';
|
return pht('Abort Build');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHarbormasterBuildableMessageName() {
|
||||||
|
return pht('Abort Builds');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newConfirmPromptTitle() {
|
||||||
|
return pht('Really abort build?');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHarbormasterBuildableMessageEffect() {
|
||||||
|
return pht('Build will abort.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newConfirmPromptBody() {
|
||||||
|
return pht(
|
||||||
|
'Progress on this build will be discarded. Really abort build?');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newBuildableConfirmPromptTitle(
|
||||||
|
array $builds,
|
||||||
|
array $sendable) {
|
||||||
|
return pht(
|
||||||
|
'Really abort %s build(s)?',
|
||||||
|
phutil_count($builds));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newBuildableConfirmPromptBody(
|
||||||
|
array $builds,
|
||||||
|
array $sendable) {
|
||||||
|
|
||||||
|
if (count($sendable) === count($builds)) {
|
||||||
|
return pht(
|
||||||
|
'If you abort all builds, work will halt immediately. Work '.
|
||||||
|
'will be discarded, and builds must be completely restarted.');
|
||||||
|
} else {
|
||||||
|
return pht(
|
||||||
|
'You can only abort some builds. Work will halt immediately on '.
|
||||||
|
'builds you can abort. Progress will be discarded, and builds must '.
|
||||||
|
'be completely restarted if you want them to complete.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTitle() {
|
public function getTitle() {
|
||||||
|
@ -37,5 +79,35 @@ final class HarbormasterBuildMessageAbortTransaction
|
||||||
$build->releaseAllArtifacts($actor);
|
$build->releaseAllArtifacts($actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function newCanApplyMessageAssertion(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
HarbormasterBuild $build) {
|
||||||
|
|
||||||
|
if ($build->isAutobuild()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Unable to Abort Build'),
|
||||||
|
pht(
|
||||||
|
'You can not abort a build that uses an autoplan.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($build->isComplete()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Unable to Abort Build'),
|
||||||
|
pht(
|
||||||
|
'You can not abort this biuld because it is already complete.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newCanSendMessageAssertion(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
HarbormasterBuild $build) {
|
||||||
|
|
||||||
|
if ($build->isAborting()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Unable to Abort Build'),
|
||||||
|
pht(
|
||||||
|
'You can not abort this build because it is already aborting.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,52 @@ final class HarbormasterBuildMessagePauseTransaction
|
||||||
extends HarbormasterBuildMessageTransaction {
|
extends HarbormasterBuildMessageTransaction {
|
||||||
|
|
||||||
const TRANSACTIONTYPE = 'message/pause';
|
const TRANSACTIONTYPE = 'message/pause';
|
||||||
|
const MESSAGETYPE = 'pause';
|
||||||
|
|
||||||
public function getMessageType() {
|
public function getHarbormasterBuildMessageName() {
|
||||||
return 'pause';
|
return pht('Pause Build');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHarbormasterBuildableMessageName() {
|
||||||
|
return pht('Pause Builds');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newConfirmPromptTitle() {
|
||||||
|
return pht('Really pause build?');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHarbormasterBuildableMessageEffect() {
|
||||||
|
return pht('Build will pause.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newConfirmPromptBody() {
|
||||||
|
return pht(
|
||||||
|
'If you pause this build, work will halt once the current steps '.
|
||||||
|
'complete. You can resume the build later.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newBuildableConfirmPromptTitle(
|
||||||
|
array $builds,
|
||||||
|
array $sendable) {
|
||||||
|
return pht(
|
||||||
|
'Really pause %s build(s)?',
|
||||||
|
phutil_count($builds));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newBuildableConfirmPromptBody(
|
||||||
|
array $builds,
|
||||||
|
array $sendable) {
|
||||||
|
|
||||||
|
if (count($sendable) === count($builds)) {
|
||||||
|
return pht(
|
||||||
|
'If you pause all builds, work will halt once the current steps '.
|
||||||
|
'complete. You can resume the builds later.');
|
||||||
|
} else {
|
||||||
|
return pht(
|
||||||
|
'You can only pause some builds. Once the current steps complete, '.
|
||||||
|
'work will halt on builds you can pause. You can resume the builds '.
|
||||||
|
'later.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTitle() {
|
public function getTitle() {
|
||||||
|
@ -30,4 +73,49 @@ final class HarbormasterBuildMessagePauseTransaction
|
||||||
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_PAUSED);
|
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_PAUSED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function newCanApplyMessageAssertion(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
HarbormasterBuild $build) {
|
||||||
|
|
||||||
|
if ($build->isAutobuild()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Unable to Pause Build'),
|
||||||
|
pht('You can not pause a build that uses an autoplan.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($build->isPaused()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Unable to Pause Build'),
|
||||||
|
pht('You can not pause this build because it is already paused.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($build->isComplete()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Unable to Pause Build'),
|
||||||
|
pht('You can not pause this build because it has already completed.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newCanSendMessageAssertion(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
HarbormasterBuild $build) {
|
||||||
|
|
||||||
|
if ($build->isPausing()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Unable to Pause Build'),
|
||||||
|
pht('You can not pause this build because it is already pausing.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($build->isRestarting()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Unable to Pause Build'),
|
||||||
|
pht('You can not pause this build because it is already restarting.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($build->isAborting()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Unable to Pause Build'),
|
||||||
|
pht('You can not pause this build because it is already aborting.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,77 @@ final class HarbormasterBuildMessageRestartTransaction
|
||||||
extends HarbormasterBuildMessageTransaction {
|
extends HarbormasterBuildMessageTransaction {
|
||||||
|
|
||||||
const TRANSACTIONTYPE = 'message/restart';
|
const TRANSACTIONTYPE = 'message/restart';
|
||||||
|
const MESSAGETYPE = 'restart';
|
||||||
|
|
||||||
public function getMessageType() {
|
public function getHarbormasterBuildMessageName() {
|
||||||
return 'restart';
|
return pht('Restart Build');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHarbormasterBuildableMessageName() {
|
||||||
|
return pht('Restart Builds');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHarbormasterBuildableMessageEffect() {
|
||||||
|
return pht('Build will restart.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newConfirmPromptTitle() {
|
||||||
|
return pht('Really restart build?');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newConfirmPromptBody() {
|
||||||
|
return pht(
|
||||||
|
'Progress on this build will be discarded and the build will restart. '.
|
||||||
|
'Side effects of the build will occur again. Really restart build?');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newBuildableConfirmPromptTitle(
|
||||||
|
array $builds,
|
||||||
|
array $sendable) {
|
||||||
|
return pht(
|
||||||
|
'Really restart %s build(s)?',
|
||||||
|
phutil_count($builds));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newBuildableConfirmPromptBody(
|
||||||
|
array $builds,
|
||||||
|
array $sendable) {
|
||||||
|
|
||||||
|
if (count($sendable) === count($builds)) {
|
||||||
|
return pht(
|
||||||
|
'All builds will restart.');
|
||||||
|
} else {
|
||||||
|
return pht(
|
||||||
|
'You can only restart some builds.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newBuildableConfirmPromptWarnings(
|
||||||
|
array $builds,
|
||||||
|
array $sendable) {
|
||||||
|
|
||||||
|
$building = false;
|
||||||
|
foreach ($sendable as $build) {
|
||||||
|
if ($build->isBuilding()) {
|
||||||
|
$building = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$warnings = array();
|
||||||
|
|
||||||
|
if ($building) {
|
||||||
|
$warnings[] = pht(
|
||||||
|
'Progress on running builds will be discarded.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sendable) {
|
||||||
|
$warnings[] = pht(
|
||||||
|
'When a build is restarted, side effects associated with '.
|
||||||
|
'the build may occur again.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $warnings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTitle() {
|
public function getTitle() {
|
||||||
|
@ -16,7 +84,7 @@ final class HarbormasterBuildMessageRestartTransaction
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getIcon() {
|
public function getIcon() {
|
||||||
return 'fa-backward';
|
return 'fa-repeat';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function applyInternalEffects($object, $value) {
|
public function applyInternalEffects($object, $value) {
|
||||||
|
@ -27,4 +95,72 @@ final class HarbormasterBuildMessageRestartTransaction
|
||||||
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING);
|
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function newCanApplyMessageAssertion(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
HarbormasterBuild $build) {
|
||||||
|
|
||||||
|
if ($build->isAutobuild()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Can Not Restart Autobuild'),
|
||||||
|
pht(
|
||||||
|
'This build can not be restarted because it is an automatic '.
|
||||||
|
'build.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$restartable = HarbormasterBuildPlanBehavior::BEHAVIOR_RESTARTABLE;
|
||||||
|
$plan = $build->getBuildPlan();
|
||||||
|
|
||||||
|
// See T13526. Users who can't see the "BuildPlan" can end up here with
|
||||||
|
// no object. This is highly questionable.
|
||||||
|
if (!$plan) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('No Build Plan Permission'),
|
||||||
|
pht(
|
||||||
|
'You can not restart this build because you do not have '.
|
||||||
|
'permission to access the build plan.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$option = HarbormasterBuildPlanBehavior::getBehavior($restartable)
|
||||||
|
->getPlanOption($plan);
|
||||||
|
$option_key = $option->getKey();
|
||||||
|
|
||||||
|
$never_restartable = HarbormasterBuildPlanBehavior::RESTARTABLE_NEVER;
|
||||||
|
$is_never = ($option_key === $never_restartable);
|
||||||
|
if ($is_never) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Build Plan Prevents Restart'),
|
||||||
|
pht(
|
||||||
|
'This build can not be restarted because the build plan is '.
|
||||||
|
'configured to prevent the build from restarting.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$failed_restartable = HarbormasterBuildPlanBehavior::RESTARTABLE_IF_FAILED;
|
||||||
|
$is_failed = ($option_key === $failed_restartable);
|
||||||
|
if ($is_failed) {
|
||||||
|
if (!$this->isFailed()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Only Restartable if Failed'),
|
||||||
|
pht(
|
||||||
|
'This build can not be restarted because the build plan is '.
|
||||||
|
'configured to prevent the build from restarting unless it '.
|
||||||
|
'has failed, and it has not failed.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newCanSendMessageAssertion(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
HarbormasterBuild $build) {
|
||||||
|
|
||||||
|
if ($build->isRestarting()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Already Restarting'),
|
||||||
|
pht(
|
||||||
|
'This build is already restarting. You can not reissue a restart '.
|
||||||
|
'command to a restarting build.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,49 @@ final class HarbormasterBuildMessageResumeTransaction
|
||||||
extends HarbormasterBuildMessageTransaction {
|
extends HarbormasterBuildMessageTransaction {
|
||||||
|
|
||||||
const TRANSACTIONTYPE = 'message/resume';
|
const TRANSACTIONTYPE = 'message/resume';
|
||||||
|
const MESSAGETYPE = 'resume';
|
||||||
|
|
||||||
public function getMessageType() {
|
public function getHarbormasterBuildMessageName() {
|
||||||
return 'resume';
|
return pht('Resume Build');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHarbormasterBuildableMessageName() {
|
||||||
|
return pht('Resume Builds');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHarbormasterBuildableMessageEffect() {
|
||||||
|
return pht('Build will resume.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newConfirmPromptTitle() {
|
||||||
|
return pht('Really resume build?');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newConfirmPromptBody() {
|
||||||
|
return pht(
|
||||||
|
'Work will continue on the build. Really resume?');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newBuildableConfirmPromptTitle(
|
||||||
|
array $builds,
|
||||||
|
array $sendable) {
|
||||||
|
return pht(
|
||||||
|
'Really resume %s build(s)?',
|
||||||
|
phutil_count($builds));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newBuildableConfirmPromptBody(
|
||||||
|
array $builds,
|
||||||
|
array $sendable) {
|
||||||
|
|
||||||
|
if (count($sendable) === count($builds)) {
|
||||||
|
return pht(
|
||||||
|
'Work will continue on all builds. Really resume?');
|
||||||
|
} else {
|
||||||
|
return pht(
|
||||||
|
'You can only resume some builds. Work will continue on builds '.
|
||||||
|
'you have permission to resume.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTitle() {
|
public function getTitle() {
|
||||||
|
@ -26,4 +66,50 @@ final class HarbormasterBuildMessageResumeTransaction
|
||||||
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING);
|
$build->setBuildStatus(HarbormasterBuildStatus::STATUS_BUILDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function newCanApplyMessageAssertion(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
HarbormasterBuild $build) {
|
||||||
|
|
||||||
|
if ($build->isAutobuild()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Unable to Resume Build'),
|
||||||
|
pht(
|
||||||
|
'You can not resume a build that uses an autoplan.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$build->isPaused()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Unable to Resume Build'),
|
||||||
|
pht(
|
||||||
|
'You can not resume this build because it is not paused. You can '.
|
||||||
|
'only resume a paused build.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newCanSendMessageAssertion(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
HarbormasterBuild $build) {
|
||||||
|
|
||||||
|
if ($build->isResuming()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Unable to Resume Build'),
|
||||||
|
pht(
|
||||||
|
'You can not resume this build beacuse it is already resuming.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($build->isRestarting()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Unable to Resume Build'),
|
||||||
|
pht('You can not resume this build because it is already restarting.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($build->isAborting()) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Unable to Resume Build'),
|
||||||
|
pht('You can not resume this build because it is already aborting.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,31 @@
|
||||||
abstract class HarbormasterBuildMessageTransaction
|
abstract class HarbormasterBuildMessageTransaction
|
||||||
extends HarbormasterBuildTransactionType {
|
extends HarbormasterBuildTransactionType {
|
||||||
|
|
||||||
|
final public function getHarbormasterBuildMessageType() {
|
||||||
|
return $this->getPhobjectClassConstant('MESSAGETYPE');
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function getHarbormasterBuildMessageName();
|
||||||
|
abstract public function getHarbormasterBuildableMessageName();
|
||||||
|
abstract public function getHarbormasterBuildableMessageEffect();
|
||||||
|
|
||||||
|
abstract public function newConfirmPromptTitle();
|
||||||
|
abstract public function newConfirmPromptBody();
|
||||||
|
|
||||||
|
abstract public function newBuildableConfirmPromptTitle(
|
||||||
|
array $builds,
|
||||||
|
array $sendable);
|
||||||
|
|
||||||
|
abstract public function newBuildableConfirmPromptBody(
|
||||||
|
array $builds,
|
||||||
|
array $sendable);
|
||||||
|
|
||||||
|
public function newBuildableConfirmPromptWarnings(
|
||||||
|
array $builds,
|
||||||
|
array $sendable) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
final public function generateOldValue($object) {
|
final public function generateOldValue($object) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -17,32 +42,110 @@ abstract class HarbormasterBuildMessageTransaction
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final public static function getTransactionTypeForMessageType($message_type) {
|
final public static function getTransactionObjectForMessageType(
|
||||||
|
$message_type) {
|
||||||
$message_xactions = id(new PhutilClassMapQuery())
|
$message_xactions = id(new PhutilClassMapQuery())
|
||||||
->setAncestorClass(__CLASS__)
|
->setAncestorClass(__CLASS__)
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
foreach ($message_xactions as $message_xaction) {
|
foreach ($message_xactions as $message_xaction) {
|
||||||
if ($message_xaction->getMessageType() === $message_type) {
|
$xaction_type = $message_xaction->getHarbormasterBuildMessageType();
|
||||||
return $message_xaction->getTransactionTypeConstant();
|
if ($xaction_type === $message_type) {
|
||||||
|
return $message_xaction;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract public function getMessageType();
|
final public static function getTransactionTypeForMessageType($message_type) {
|
||||||
|
$message_xaction = self::getTransactionObjectForMessageType($message_type);
|
||||||
|
|
||||||
public function validateTransactions($object, array $xactions) {
|
if ($message_xaction) {
|
||||||
$errors = array();
|
return $message_xaction->getTransactionTypeConstant();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Restore logic that tests if the command can issue without causing
|
return null;
|
||||||
// anything to lapse into an invalid state. This should not be the same
|
|
||||||
// as the logic which powers the web UI: for example, if an "abort" is
|
|
||||||
// queued we want to disable "Abort" in the web UI, but should obviously
|
|
||||||
// process it here.
|
|
||||||
|
|
||||||
return $errors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public function getTransactionHasEffect($object, $old, $new) {
|
||||||
|
return $this->canApplyMessage($this->getActor(), $object);
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function canApplyMessage(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
HarbormasterBuild $build) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->assertCanApplyMessage($viewer, $build);
|
||||||
|
return true;
|
||||||
|
} catch (HarbormasterRestartException $ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function canSendMessage(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
HarbormasterBuild $build) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->assertCanSendMessage($viewer, $build);
|
||||||
|
return true;
|
||||||
|
} catch (HarbormasterRestartException $ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function assertCanApplyMessage(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
HarbormasterBuild $build) {
|
||||||
|
$this->newCanApplyMessageAssertion($viewer, $build);
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function assertCanSendMessage(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
HarbormasterBuild $build) {
|
||||||
|
$plan = $build->getBuildPlan();
|
||||||
|
|
||||||
|
// See T13526. Users without permission to access the build plan can
|
||||||
|
// currently end up here with no "BuildPlan" object.
|
||||||
|
if (!$plan) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('No Build Plan Permission'),
|
||||||
|
pht(
|
||||||
|
'You can not issue this command because you do not have '.
|
||||||
|
'permission to access the build plan for this build.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issuing these commands requires that you be able to edit the build, to
|
||||||
|
// prevent enemy engineers from sabotaging your builds. See T9614.
|
||||||
|
if (!$plan->canRunWithoutEditCapability()) {
|
||||||
|
try {
|
||||||
|
PhabricatorPolicyFilter::requireCapability(
|
||||||
|
$viewer,
|
||||||
|
$plan,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
} catch (PhabricatorPolicyException $ex) {
|
||||||
|
throw new HarbormasterRestartException(
|
||||||
|
pht('Insufficent Build Plan Permission'),
|
||||||
|
pht(
|
||||||
|
'The build plan for this build is configured to prevent '.
|
||||||
|
'users who can not edit it from issuing commands to the '.
|
||||||
|
'build, and you do not have permission to edit the build '.
|
||||||
|
'plan.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->newCanSendMessageAssertion($viewer, $build);
|
||||||
|
$this->assertCanApplyMessage($viewer, $build);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected function newCanSendMessageAssertion(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
HarbormasterBuild $build);
|
||||||
|
|
||||||
|
abstract protected function newCanApplyMessageAssertion(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
HarbormasterBuild $build);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue