mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 00:32:42 +01:00
Replace "Cancel Build" with "Stop", "Resume" and "Restart"
Summary: Ref T1049. Currently you can cancel a build, but now that we're tracking a lot more state we can stop, resume, and restart builds. When the user issues a command against a build, I'm writing it into an auxiliary queue (`HarbormasterBuildCommand`) and then reading them out in the worker. This is mostly to avoid race messes where we try to `save()` the object in multiple places: basically, the BuildEngine is the //only// thing that writes to Build objects, and it holds a lock while it does it. Test Plan: - Created a plan which runs "sleep 2" a bunch of times in a row. - Stopped, resumed, and restarted it. Reviewers: btrahan Reviewed By: btrahan CC: aran, chad Maniphest Tasks: T1049 Differential Revision: https://secure.phabricator.com/D7892
This commit is contained in:
parent
4d5e8a149a
commit
1786093c6e
18 changed files with 386 additions and 128 deletions
18
resources/sql/autopatches/20140104.harbormastercmd.sql
Normal file
18
resources/sql/autopatches/20140104.harbormastercmd.sql
Normal file
|
@ -0,0 +1,18 @@
|
|||
CREATE TABLE {$NAMESPACE}_harbormaster.harbormaster_buildcommand (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
targetPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
command VARCHAR(128) NOT NULL COLLATE utf8_bin,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
KEY `key_target` (targetPHID)
|
||||
) ENGINE=InnoDB, COLLATE utf8_general_ci;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_build
|
||||
DROP cancelRequested;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildtarget
|
||||
ADD targetStatus VARCHAR(64) NOT NULL COLLATE utf8_bin;
|
||||
|
||||
UPDATE {$NAMESPACE}_harbormaster.harbormaster_buildtarget
|
||||
SET targetStatus = 'target/pending' WHERE targetStatus = '';
|
|
@ -704,9 +704,10 @@ phutil_register_library_map(array(
|
|||
'FileMailReceiver' => 'applications/files/mail/FileMailReceiver.php',
|
||||
'FileReplyHandler' => 'applications/files/mail/FileReplyHandler.php',
|
||||
'HarbormasterBuild' => 'applications/harbormaster/storage/build/HarbormasterBuild.php',
|
||||
'HarbormasterBuildActionController' => 'applications/harbormaster/controller/HarbormasterBuildActionController.php',
|
||||
'HarbormasterBuildArtifact' => 'applications/harbormaster/storage/build/HarbormasterBuildArtifact.php',
|
||||
'HarbormasterBuildArtifactQuery' => 'applications/harbormaster/query/HarbormasterBuildArtifactQuery.php',
|
||||
'HarbormasterBuildCancelController' => 'applications/harbormaster/controller/HarbormasterBuildCancelController.php',
|
||||
'HarbormasterBuildCommand' => 'applications/harbormaster/storage/HarbormasterBuildCommand.php',
|
||||
'HarbormasterBuildEngine' => 'applications/harbormaster/engine/HarbormasterBuildEngine.php',
|
||||
'HarbormasterBuildItem' => 'applications/harbormaster/storage/build/HarbormasterBuildItem.php',
|
||||
'HarbormasterBuildItemQuery' => 'applications/harbormaster/query/HarbormasterBuildItemQuery.php',
|
||||
|
@ -3155,13 +3156,14 @@ phutil_register_library_map(array(
|
|||
0 => 'HarbormasterDAO',
|
||||
1 => 'PhabricatorPolicyInterface',
|
||||
),
|
||||
'HarbormasterBuildActionController' => 'HarbormasterController',
|
||||
'HarbormasterBuildArtifact' =>
|
||||
array(
|
||||
0 => 'HarbormasterDAO',
|
||||
1 => 'PhabricatorPolicyInterface',
|
||||
),
|
||||
'HarbormasterBuildArtifactQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'HarbormasterBuildCancelController' => 'HarbormasterController',
|
||||
'HarbormasterBuildCommand' => 'HarbormasterDAO',
|
||||
'HarbormasterBuildEngine' => 'Phobject',
|
||||
'HarbormasterBuildItem' => 'HarbormasterDAO',
|
||||
'HarbormasterBuildItemQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
|
@ -3226,7 +3228,7 @@ phutil_register_library_map(array(
|
|||
'HarbormasterPHIDTypeBuildStep' => 'PhabricatorPHIDType',
|
||||
'HarbormasterPHIDTypeBuildTarget' => 'PhabricatorPHIDType',
|
||||
'HarbormasterPHIDTypeBuildable' => 'PhabricatorPHIDType',
|
||||
'HarbormasterPlanController' => 'PhabricatorController',
|
||||
'HarbormasterPlanController' => 'HarbormasterController',
|
||||
'HarbormasterPlanDisableController' => 'HarbormasterPlanController',
|
||||
'HarbormasterPlanEditController' => 'HarbormasterPlanController',
|
||||
'HarbormasterPlanListController' =>
|
||||
|
|
|
@ -55,7 +55,8 @@ final class PhabricatorApplicationHarbormaster extends PhabricatorApplication {
|
|||
),
|
||||
'build/' => array(
|
||||
'(?:(?P<id>\d+)/)?' => 'HarbormasterBuildViewController',
|
||||
'cancel/(?:(?P<id>\d+)/)?' => 'HarbormasterBuildCancelController',
|
||||
'(?P<action>stop|resume|restart)/(?:(?P<id>\d+)/)?'
|
||||
=> 'HarbormasterBuildActionController',
|
||||
),
|
||||
'plan/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?'
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterBuildActionController
|
||||
extends HarbormasterController {
|
||||
|
||||
private $id;
|
||||
private $action;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->id = $data['id'];
|
||||
$this->action = $data['action'];
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
$command = $this->action;
|
||||
|
||||
$build = id(new HarbormasterBuildQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($this->id))
|
||||
->executeOne();
|
||||
if (!$build) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
switch ($command) {
|
||||
case HarbormasterBuildCommand::COMMAND_RESTART:
|
||||
$can_issue = $build->canRestartBuild();
|
||||
break;
|
||||
case HarbormasterBuildCommand::COMMAND_STOP:
|
||||
$can_issue = $build->canStopBuild();
|
||||
break;
|
||||
case HarbormasterBuildCommand::COMMAND_RESUME:
|
||||
$can_issue = $build->canResumeBuild();
|
||||
break;
|
||||
default:
|
||||
return new Aphront400Response();
|
||||
}
|
||||
|
||||
$build_uri = $this->getApplicationURI('/build/'.$build->getID().'/');
|
||||
|
||||
if ($request->isDialogFormPost() && $can_issue) {
|
||||
|
||||
// Issue the new build command.
|
||||
id(new HarbormasterBuildCommand())
|
||||
->setAuthorPHID($viewer->getPHID())
|
||||
->setTargetPHID($build->getPHID())
|
||||
->setCommand($command)
|
||||
->save();
|
||||
|
||||
// Schedule a build update. We may already have stuff in queue (in which
|
||||
// case this will just no-op), but we might also be dealing with a
|
||||
// stopped build, which won't restart unless we deal with this.
|
||||
PhabricatorWorker::scheduleTask(
|
||||
'HarbormasterBuildWorker',
|
||||
array(
|
||||
'buildID' => $build->getID()
|
||||
));
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($build_uri);
|
||||
}
|
||||
|
||||
switch ($command) {
|
||||
case HarbormasterBuildCommand::COMMAND_RESTART:
|
||||
if ($can_issue) {
|
||||
$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 {
|
||||
$title = pht('Unable to Restart Build');
|
||||
if ($build->isRestarting()) {
|
||||
$body = pht(
|
||||
'This build is already restarting. You can not reissue a '.
|
||||
'restart command to a restarting build.');
|
||||
} else {
|
||||
$body = pht(
|
||||
'You can not restart this build.');
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HarbormasterBuildCommand::COMMAND_STOP:
|
||||
if ($can_issue) {
|
||||
$title = pht('Really stop build?');
|
||||
$body = pht(
|
||||
'If you stop this build, work will halt once the current steps '.
|
||||
'complete. You can resume the build later.');
|
||||
$submit = pht('Stop Build');
|
||||
} else {
|
||||
$title = pht('Unable to Stop Build');
|
||||
if ($build->isComplete()) {
|
||||
$body = pht(
|
||||
'This build is already complete. You can not stop a completed '.
|
||||
'build.');
|
||||
} else if ($build->isStopped()) {
|
||||
$body = pht(
|
||||
'This build is already stopped. You can not stop a build which '.
|
||||
'has already been stopped.');
|
||||
} else if ($build->isStopping()) {
|
||||
$body = pht(
|
||||
'This build is already stopping. You can not reissue a stop '.
|
||||
'command to a stopping build.');
|
||||
} else {
|
||||
$body = pht(
|
||||
'This build can not be stopped.');
|
||||
}
|
||||
}
|
||||
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->isStopped()) {
|
||||
$body = pht(
|
||||
'This build is not stopped. You can only resume a stopped '.
|
||||
'build.');
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$dialog = id(new AphrontDialogView())
|
||||
->setUser($viewer)
|
||||
->setTitle($title)
|
||||
->appendChild($body)
|
||||
->addCancelButton($build_uri);
|
||||
|
||||
if ($can_issue) {
|
||||
$dialog->addSubmitButton($submit);
|
||||
}
|
||||
|
||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterBuildCancelController
|
||||
extends HarbormasterController {
|
||||
|
||||
private $id;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->id = $data['id'];
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$id = $this->id;
|
||||
|
||||
$build = id(new HarbormasterBuildQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if ($build === null) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$build_uri = $this->getApplicationURI('/build/'.$build->getID());
|
||||
|
||||
if ($request->isDialogFormPost()) {
|
||||
$build->setCancelRequested(1);
|
||||
$build->save();
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($build_uri);
|
||||
}
|
||||
|
||||
$dialog = new AphrontDialogView();
|
||||
$dialog->setTitle(pht('Really cancel build?'))
|
||||
->setUser($viewer)
|
||||
->addSubmitButton(pht('Cancel'))
|
||||
->addCancelButton($build_uri, pht('Don\'t Cancel'));
|
||||
$dialog->appendChild(
|
||||
phutil_tag(
|
||||
'p',
|
||||
array(),
|
||||
pht(
|
||||
'Really cancel this build?')));
|
||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||
}
|
||||
|
||||
}
|
|
@ -214,25 +214,33 @@ final class HarbormasterBuildViewController
|
|||
->setObject($build)
|
||||
->setObjectURI("/build/{$id}");
|
||||
|
||||
$action =
|
||||
$can_restart = $build->canRestartBuild();
|
||||
$can_stop = $build->canStopBuild();
|
||||
$can_resume = $build->canResumeBuild();
|
||||
|
||||
$list->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Cancel Build'))
|
||||
->setIcon('delete');
|
||||
switch ($build->getBuildStatus()) {
|
||||
case HarbormasterBuild::STATUS_PENDING:
|
||||
case HarbormasterBuild::STATUS_WAITING:
|
||||
case HarbormasterBuild::STATUS_BUILDING:
|
||||
$cancel_uri = $this->getApplicationURI('/build/cancel/'.$id.'/');
|
||||
$action
|
||||
->setHref($cancel_uri)
|
||||
->setWorkflow(true);
|
||||
break;
|
||||
default:
|
||||
$action
|
||||
->setDisabled(true);
|
||||
break;
|
||||
}
|
||||
$list->addAction($action);
|
||||
->setName(pht('Restart Build'))
|
||||
->setIcon('backward')
|
||||
->setHref($this->getApplicationURI('/build/restart/'.$id.'/'))
|
||||
->setDisabled(!$can_restart)
|
||||
->setWorkflow(true));
|
||||
|
||||
$list->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Stop Build'))
|
||||
->setIcon('stop')
|
||||
->setHref($this->getApplicationURI('/build/stop/'.$id.'/'))
|
||||
->setDisabled(!$can_stop)
|
||||
->setWorkflow(true));
|
||||
|
||||
$list->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Resume Build'))
|
||||
->setIcon('play')
|
||||
->setHref($this->getApplicationURI('/build/resume/'.$id.'/'))
|
||||
->setDisabled(!$can_resume)
|
||||
->setWorkflow(true));
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
@ -272,8 +280,8 @@ final class HarbormasterBuildViewController
|
|||
}
|
||||
|
||||
private function getStatus(HarbormasterBuild $build) {
|
||||
if ($build->getCancelRequested()) {
|
||||
return pht('Cancelling');
|
||||
if ($build->isStopping()) {
|
||||
return pht('Stopping');
|
||||
}
|
||||
switch ($build->getBuildStatus()) {
|
||||
case HarbormasterBuild::STATUS_INACTIVE:
|
||||
|
@ -290,8 +298,8 @@ final class HarbormasterBuildViewController
|
|||
return pht('Failed');
|
||||
case HarbormasterBuild::STATUS_ERROR:
|
||||
return pht('Unexpected Error');
|
||||
case HarbormasterBuild::STATUS_CANCELLED:
|
||||
return pht('Cancelled');
|
||||
case HarbormasterBuild::STATUS_STOPPED:
|
||||
return pht('Stopped');
|
||||
default:
|
||||
return pht('Unknown');
|
||||
}
|
||||
|
|
|
@ -88,10 +88,6 @@ final class HarbormasterBuildableListController
|
|||
->setViewer($user)
|
||||
->addNavigationItems($nav->getMenu());
|
||||
|
||||
if ($for_app) {
|
||||
$nav->addFilter('new/', pht('New Build Plan'));
|
||||
}
|
||||
|
||||
$nav->addLabel(pht('Build Plans'));
|
||||
$nav->addFilter('plan/', pht('Manage Build Plans'));
|
||||
|
||||
|
|
|
@ -38,9 +38,9 @@ final class HarbormasterBuildableViewController
|
|||
->setObjectName(pht('Build %d', $build->getID()))
|
||||
->setHeader($build->getName())
|
||||
->setHref($view_uri);
|
||||
if ($build->getCancelRequested()) {
|
||||
if ($build->isStopping()) {
|
||||
$item->setBarColor('black');
|
||||
$item->addAttribute(pht('Cancelling'));
|
||||
$item->addAttribute(pht('Stopping'));
|
||||
} else {
|
||||
switch ($build->getBuildStatus()) {
|
||||
case HarbormasterBuild::STATUS_INACTIVE:
|
||||
|
@ -71,9 +71,9 @@ final class HarbormasterBuildableViewController
|
|||
$item->setBarColor('red');
|
||||
$item->addAttribute(pht('Unexpected Error'));
|
||||
break;
|
||||
case HarbormasterBuild::STATUS_CANCELLED:
|
||||
case HarbormasterBuild::STATUS_STOPPED:
|
||||
$item->setBarColor('black');
|
||||
$item->addAttribute(pht('Cancelled'));
|
||||
$item->addAttribute(pht('Stopped'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
abstract class HarbormasterPlanController extends PhabricatorController {
|
||||
abstract class HarbormasterPlanController extends HarbormasterController {
|
||||
|
||||
public function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
|
|
@ -57,6 +57,10 @@ final class HarbormasterPlanListController
|
|||
$nav = new AphrontSideNavFilterView();
|
||||
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
|
||||
|
||||
if ($for_app) {
|
||||
$nav->addFilter('new/', pht('New Build Plan'));
|
||||
}
|
||||
|
||||
id(new HarbormasterBuildPlanSearchEngine())
|
||||
->setViewer($user)
|
||||
->addNavigationItems($nav->getMenu());
|
||||
|
|
|
@ -72,16 +72,52 @@ final class HarbormasterBuildEngine extends Phobject {
|
|||
}
|
||||
|
||||
private function updateBuild(HarbormasterBuild $build) {
|
||||
// TODO: Handle cancellation and restarts.
|
||||
|
||||
if ($build->getBuildStatus() == HarbormasterBuild::STATUS_PENDING) {
|
||||
$should_stop = false;
|
||||
$should_resume = false;
|
||||
$should_restart = false;
|
||||
foreach ($build->getUnprocessedCommands() as $command) {
|
||||
switch ($command->getCommand()) {
|
||||
case HarbormasterBuildCommand::COMMAND_STOP:
|
||||
$should_stop = true;
|
||||
$should_resume = false;
|
||||
break;
|
||||
case HarbormasterBuildCommand::COMMAND_RESUME:
|
||||
$should_resume = true;
|
||||
$should_stop = false;
|
||||
break;
|
||||
case HarbormasterBuildCommand::COMMAND_RESTART:
|
||||
$should_restart = true;
|
||||
$should_resume = true;
|
||||
$should_stop = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (($build->getBuildStatus() == HarbormasterBuild::STATUS_PENDING) ||
|
||||
($should_restart)) {
|
||||
$this->destroyBuildTargets($build);
|
||||
$build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING);
|
||||
$build->save();
|
||||
}
|
||||
|
||||
if ($should_resume) {
|
||||
$build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING);
|
||||
$build->save();
|
||||
}
|
||||
|
||||
if ($should_stop && !$build->isComplete()) {
|
||||
$build->setBuildStatus(HarbormasterBuild::STATUS_STOPPED);
|
||||
$build->save();
|
||||
}
|
||||
|
||||
foreach ($build->getUnprocessedCommands() as $command) {
|
||||
$command->delete();
|
||||
}
|
||||
$build->attachUnprocessedCommands(array());
|
||||
|
||||
if ($build->getBuildStatus() == HarbormasterBuild::STATUS_BUILDING) {
|
||||
return $this->updateBuildSteps($build);
|
||||
$this->updateBuildSteps($build);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,8 +154,7 @@ final class HarbormasterBuildEngine extends Phobject {
|
|||
if ($step_targets) {
|
||||
$is_complete = true;
|
||||
foreach ($step_targets as $target) {
|
||||
// TODO: Move this to a top-level "status" field on BuildTarget.
|
||||
if (!$target->getDetail('__done__')) {
|
||||
if (!$target->isComplete()) {
|
||||
$is_complete = false;
|
||||
break;
|
||||
}
|
||||
|
@ -127,8 +162,7 @@ final class HarbormasterBuildEngine extends Phobject {
|
|||
|
||||
$is_failed = false;
|
||||
foreach ($step_targets as $target) {
|
||||
// TODO: Move this to a top-level "status" field on BuildTarget.
|
||||
if ($target->getDetail('__failed__')) {
|
||||
if ($target->isFailed()) {
|
||||
$is_failed = true;
|
||||
break;
|
||||
}
|
||||
|
@ -212,7 +246,7 @@ final class HarbormasterBuildEngine extends Phobject {
|
|||
foreach ($runnable as $runnable_step) {
|
||||
$target = HarbormasterBuildTarget::initializeNewBuildTarget(
|
||||
$build,
|
||||
$step,
|
||||
$runnable_step,
|
||||
$build->retrieveVariablesFromBuild());
|
||||
$target->save();
|
||||
|
||||
|
|
|
@ -94,8 +94,8 @@ final class HarbormasterUIEventListener
|
|||
case HarbormasterBuild::STATUS_ERROR:
|
||||
$item->setIcon('minus-red', pht('Unexpected Error'));
|
||||
break;
|
||||
case HarbormasterBuild::STATUS_CANCELLED:
|
||||
$item->setIcon('minus-dark', pht('Cancelled'));
|
||||
case HarbormasterBuild::STATUS_STOPPED:
|
||||
$item->setIcon('minus-dark', pht('Stopped'));
|
||||
break;
|
||||
default:
|
||||
$item->setIcon('question', pht('Unknown'));
|
||||
|
|
|
@ -92,6 +92,16 @@ final class HarbormasterBuildQuery
|
|||
$build->attachBuildPlan(idx($plans, $plan_phid));
|
||||
}
|
||||
|
||||
$build_phids = mpull($page, 'getPHID');
|
||||
$commands = id(new HarbormasterBuildCommand())->loadAllWhere(
|
||||
'targetPHID IN (%Ls) ORDER BY id ASC',
|
||||
$build_phids);
|
||||
$commands = mgroup($commands, 'getTargetPHID');
|
||||
foreach ($page as $build) {
|
||||
$unprocessed_commands = idx($commands, $build->getPHID(), array());
|
||||
$build->attachUnprocessedCommands($unprocessed_commands);
|
||||
}
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ final class WaitForPreviousBuildStepImplementation
|
|||
->execute();
|
||||
|
||||
foreach ($builds as $build) {
|
||||
if ($build->isBuilding()) {
|
||||
if (!$build->isComplete()) {
|
||||
$blockers[] = pht('Build %d', $build->getID());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
final class HarbormasterBuildCommand extends HarbormasterDAO {
|
||||
|
||||
const COMMAND_STOP = 'stop';
|
||||
const COMMAND_RESUME = 'resume';
|
||||
const COMMAND_RESTART = 'restart';
|
||||
|
||||
protected $authorPHID;
|
||||
protected $targetPHID;
|
||||
protected $command;
|
||||
|
||||
}
|
|
@ -6,10 +6,10 @@ final class HarbormasterBuild extends HarbormasterDAO
|
|||
protected $buildablePHID;
|
||||
protected $buildPlanPHID;
|
||||
protected $buildStatus;
|
||||
protected $cancelRequested;
|
||||
|
||||
private $buildable = self::ATTACHABLE;
|
||||
private $buildPlan = self::ATTACHABLE;
|
||||
private $unprocessedCommands = self::ATTACHABLE;
|
||||
|
||||
/**
|
||||
* Not currently being built.
|
||||
|
@ -47,14 +47,13 @@ final class HarbormasterBuild extends HarbormasterDAO
|
|||
const STATUS_ERROR = 'error';
|
||||
|
||||
/**
|
||||
* The build has been cancelled.
|
||||
* The build has been stopped.
|
||||
*/
|
||||
const STATUS_CANCELLED = 'cancelled';
|
||||
const STATUS_STOPPED = 'stopped';
|
||||
|
||||
public static function initializeNewBuild(PhabricatorUser $actor) {
|
||||
return id(new HarbormasterBuild())
|
||||
->setBuildStatus(self::STATUS_INACTIVE)
|
||||
->setCancelRequested(0);
|
||||
->setBuildStatus(self::STATUS_INACTIVE);
|
||||
}
|
||||
|
||||
public function getConfiguration() {
|
||||
|
@ -97,8 +96,7 @@ final class HarbormasterBuild extends HarbormasterDAO
|
|||
public function isBuilding() {
|
||||
return $this->getBuildStatus() === self::STATUS_PENDING ||
|
||||
$this->getBuildStatus() === self::STATUS_WAITING ||
|
||||
$this->getBuildStatus() === self::STATUS_BUILDING ||
|
||||
$this->getCancelRequested();
|
||||
$this->getBuildStatus() === self::STATUS_BUILDING;
|
||||
}
|
||||
|
||||
public function createLog(
|
||||
|
@ -106,10 +104,11 @@ final class HarbormasterBuild extends HarbormasterDAO
|
|||
$log_source,
|
||||
$log_type) {
|
||||
|
||||
$log = HarbormasterBuildLog::initializeNewBuildLog($build_target);
|
||||
$log->setLogSource($log_source);
|
||||
$log->setLogType($log_type);
|
||||
$log->save();
|
||||
$log = HarbormasterBuildLog::initializeNewBuildLog($build_target)
|
||||
->setLogSource($log_source)
|
||||
->setLogType($log_type)
|
||||
->save();
|
||||
|
||||
return $log;
|
||||
}
|
||||
|
||||
|
@ -139,25 +138,6 @@ final class HarbormasterBuild extends HarbormasterDAO
|
|||
return $artifact;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for and handles build cancellation. If this method returns
|
||||
* true, the caller should stop any current operations and return control
|
||||
* as quickly as possible.
|
||||
*/
|
||||
public function checkForCancellation() {
|
||||
// Here we load a copy of the current build and check whether
|
||||
// the user requested cancellation. We can't do `reload()` here
|
||||
// in case there are changes that have not yet been saved.
|
||||
$copy = id(new HarbormasterBuild())->load($this->getID());
|
||||
if ($copy->getCancelRequested()) {
|
||||
$this->setBuildStatus(HarbormasterBuild::STATUS_CANCELLED);
|
||||
$this->setCancelRequested(0);
|
||||
$this->save();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function retrieveVariablesFromBuild() {
|
||||
$results = array(
|
||||
'buildable.diff' => null,
|
||||
|
@ -212,6 +192,71 @@ final class HarbormasterBuild extends HarbormasterDAO
|
|||
'build.id' => pht('The ID of the current build.'));
|
||||
}
|
||||
|
||||
public function isComplete() {
|
||||
switch ($this->getBuildStatus()) {
|
||||
case self::STATUS_PASSED:
|
||||
case self::STATUS_FAILED:
|
||||
case self::STATUS_ERROR:
|
||||
case self::STATUS_STOPPED:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isStopped() {
|
||||
return ($this->getBuildStatus() == self::STATUS_STOPPED);
|
||||
}
|
||||
|
||||
|
||||
/* -( Build Commands )----------------------------------------------------- */
|
||||
|
||||
|
||||
public function getUnprocessedCommands() {
|
||||
return $this->assertAttached($this->unprocessedCommands);
|
||||
}
|
||||
|
||||
public function attachUnprocessedCommands(array $commands) {
|
||||
$this->unprocessedCommands = $commands;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasWaitingCommand($command_name) {
|
||||
foreach ($this->getUnprocessedCommands() as $command_object) {
|
||||
if ($command_object->getCommand() == $command_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function canRestartBuild() {
|
||||
return !$this->isRestarting();
|
||||
}
|
||||
|
||||
public function canStopBuild() {
|
||||
return !$this->isComplete() &&
|
||||
!$this->isStopped() &&
|
||||
!$this->isStopping();
|
||||
}
|
||||
|
||||
public function canResumeBuild() {
|
||||
return $this->isStopped() &&
|
||||
!$this->isResuming();
|
||||
}
|
||||
|
||||
public function isStopping() {
|
||||
return $this->hasWaitingCommand(HarbormasterBuildCommand::COMMAND_STOP);
|
||||
}
|
||||
|
||||
public function isResuming() {
|
||||
return $this->hasWaitingCommand(HarbormasterBuildCommand::COMMAND_RESUME);
|
||||
}
|
||||
|
||||
public function isRestarting() {
|
||||
return $this->hasWaitingCommand(HarbormasterBuildCommand::COMMAND_RESTART);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
|
|
@ -8,6 +8,11 @@ final class HarbormasterBuildTarget extends HarbormasterDAO
|
|||
protected $className;
|
||||
protected $details;
|
||||
protected $variables;
|
||||
protected $targetStatus;
|
||||
|
||||
const STATUS_PENDING = 'target/pending';
|
||||
const STATUS_PASSED = 'target/passed';
|
||||
const STATUS_FAILED = 'target/failed';
|
||||
|
||||
private $build = self::ATTACHABLE;
|
||||
private $buildStep = self::ATTACHABLE;
|
||||
|
@ -21,6 +26,7 @@ final class HarbormasterBuildTarget extends HarbormasterDAO
|
|||
->setBuildStepPHID($build_step->getPHID())
|
||||
->setClassName($build_step->getClassName())
|
||||
->setDetails($build_step->getDetails())
|
||||
->setTargetStatus(self::STATUS_PENDING)
|
||||
->setVariables($variables);
|
||||
}
|
||||
|
||||
|
@ -96,6 +102,30 @@ final class HarbormasterBuildTarget extends HarbormasterDAO
|
|||
}
|
||||
|
||||
|
||||
/* -( Status )------------------------------------------------------------- */
|
||||
|
||||
|
||||
public function isComplete() {
|
||||
switch ($this->getTargetStatus()) {
|
||||
case self::STATUS_PASSED:
|
||||
case self::STATUS_FAILED:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function isFailed() {
|
||||
switch ($this->getTargetStatus()) {
|
||||
case self::STATUS_FAILED:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
||||
|
|
|
@ -38,15 +38,15 @@ final class HarbormasterTargetWorker extends HarbormasterWorker {
|
|||
try {
|
||||
$implementation = $target->getImplementation();
|
||||
if (!$implementation->validateSettings()) {
|
||||
$target->setDetail('__failed__', true);
|
||||
$target->setTargetStatus(HarbormasterBuildTarget::STATUS_FAILED);
|
||||
$target->save();
|
||||
} else {
|
||||
$implementation->execute($build, $target);
|
||||
$target->setDetail('__done__', true);
|
||||
$target->setTargetStatus(HarbormasterBuildTarget::STATUS_PASSED);
|
||||
$target->save();
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
$target->setDetail('__failed__', true);
|
||||
$target->setTargetStatus(HarbormasterBuildTarget::STATUS_FAILED);
|
||||
$target->save();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue