diff --git a/src/applications/harbormaster/controller/HarbormasterBuildActionController.php b/src/applications/harbormaster/controller/HarbormasterBuildActionController.php index 569cef290..5b0c84e10 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildActionController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildActionController.php @@ -1,155 +1,155 @@ <?php final class HarbormasterBuildActionController extends HarbormasterController { private $id; private $action; private $via; public function willProcessRequest(array $data) { $this->id = $data['id']; $this->action = $data['action']; $this->via = idx($data, 'via'); } public function processRequest() { $request = $this->getRequest(); $viewer = $request->getUser(); $command = $this->action; $build = id(new HarbormasterBuildQuery()) ->setViewer($viewer) ->withIDs(array($this->id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->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(); } switch ($this->via) { case 'buildable': $return_uri = '/'.$build->getBuildable()->getMonogram(); break; default: $return_uri = $this->getApplicationURI('/build/'.$build->getID().'/'); break; } if ($request->isDialogFormPost() && $can_issue) { $editor = id(new HarbormasterBuildTransactionEditor()) ->setActor($viewer) ->setContentSourceFromRequest($request) ->setContinueOnNoEffect(true) ->setContinueOnMissingFields(true); $xaction = id(new HarbormasterBuildTransaction()) ->setTransactionType(HarbormasterBuildTransaction::TYPE_COMMAND) ->setNewValue($command); $editor->applyTransactions($build, array($xaction)); return id(new AphrontRedirectResponse())->setURI($return_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?'); + $title = pht('Really pause build?'); $body = pht( - 'If you stop this build, work will halt once the current steps '. + 'If you pause this build, work will halt once the current steps '. 'complete. You can resume the build later.'); - $submit = pht('Stop Build'); + $submit = pht('Pause Build'); } else { - $title = pht('Unable to Stop Build'); + $title = pht('Unable to Pause Build'); if ($build->isComplete()) { $body = pht( - 'This build is already complete. You can not stop a completed '. + 'This build is already complete. You can not pause 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.'); + 'This build is already paused. You can not pause a build which '. + 'has already been paused.'); } else if ($build->isStopping()) { $body = pht( - 'This build is already stopping. You can not reissue a stop '. - 'command to a stopping build.'); + 'This build is already pausing. You can not reissue a pause '. + 'command to a pausing build.'); } else { $body = pht( - 'This build can not be stopped.'); + '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->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($return_uri); if ($can_issue) { $dialog->addSubmitButton($submit); } return id(new AphrontDialogResponse())->setDialog($dialog); } } diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php index 681526b35..10f436efa 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php @@ -1,487 +1,489 @@ <?php final class HarbormasterBuildViewController 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) { return new Aphront404Response(); } require_celerity_resource('harbormaster-css'); $title = pht('Build %d', $id); $header = id(new PHUIHeaderView()) ->setHeader($title) ->setUser($viewer) ->setPolicyObject($build); if ($build->isRestarting()) { $header->setStatus('fa-exclamation-triangle', 'red', pht('Restarting')); } else if ($build->isStopping()) { - $header->setStatus('fa-exclamation-triangle', 'red', pht('Stopping')); + $header->setStatus('fa-exclamation-triangle', 'red', pht('Pausing')); } else if ($build->isResuming()) { $header->setStatus('fa-exclamation-triangle', 'red', pht('Resuming')); } $box = id(new PHUIObjectBoxView()) ->setHeader($header); $actions = $this->buildActionList($build); $this->buildPropertyLists($box, $build, $actions); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb( $build->getBuildable()->getMonogram(), '/'.$build->getBuildable()->getMonogram()); $crumbs->addTextCrumb($title); $build_targets = id(new HarbormasterBuildTargetQuery()) ->setViewer($viewer) ->needBuildSteps(true) ->withBuildPHIDs(array($build->getPHID())) ->execute(); if ($build_targets) { $messages = id(new HarbormasterBuildMessageQuery()) ->setViewer($viewer) ->withBuildTargetPHIDs(mpull($build_targets, 'getPHID')) ->execute(); $messages = mgroup($messages, 'getBuildTargetPHID'); } else { $messages = array(); } $targets = array(); foreach ($build_targets as $build_target) { $header = id(new PHUIHeaderView()) ->setHeader($build_target->getName()) ->setUser($viewer); $target_box = id(new PHUIObjectBoxView()) ->setHeader($header); $properties = new PHUIPropertyListView(); $status_view = new PHUIStatusListView(); $item = new PHUIStatusItemView(); $status = $build_target->getTargetStatus(); $status_name = HarbormasterBuildTarget::getBuildTargetStatusName($status); $icon = HarbormasterBuildTarget::getBuildTargetStatusIcon($status); $color = HarbormasterBuildTarget::getBuildTargetStatusColor($status); $item->setTarget($status_name); $item->setIcon($icon, $color); $status_view->addItem($item); $properties->addProperty(pht('Name'), $build_target->getName()); $properties->addProperty(pht('Status'), $status_view); $target_box->addPropertyList($properties, pht('Overview')); $description = $build_target->getBuildStep()->getDescription(); if ($description) { $rendered = PhabricatorMarkupEngine::renderOneObject( id(new PhabricatorMarkupOneOff()) ->setContent($description) ->setPreserveLinebreaks(true), 'default', $viewer); $properties->addSectionHeader(pht('Description')); $properties->addTextContent($rendered); } $details = $build_target->getDetails(); if ($details) { $properties = new PHUIPropertyListView(); foreach ($details as $key => $value) { $properties->addProperty($key, $value); } $target_box->addPropertyList($properties, pht('Configuration')); } $variables = $build_target->getVariables(); if ($variables) { $properties = new PHUIPropertyListView(); foreach ($variables as $key => $value) { $properties->addProperty($key, $value); } $target_box->addPropertyList($properties, pht('Variables')); } $artifacts = $this->buildArtifacts($build_target); if ($artifacts) { $properties = new PHUIPropertyListView(); $properties->addRawContent($artifacts); $target_box->addPropertyList($properties, pht('Artifacts')); } $build_messages = idx($messages, $build_target->getPHID(), array()); if ($build_messages) { $properties = new PHUIPropertyListView(); $properties->addRawContent($this->buildMessages($build_messages)); $target_box->addPropertyList($properties, pht('Messages')); } $properties = new PHUIPropertyListView(); $properties->addProperty('Build Target ID', $build_target->getID()); $target_box->addPropertyList($properties, pht('Metadata')); $targets[] = $target_box; $targets[] = $this->buildLog($build, $build_target); } $xactions = id(new HarbormasterBuildTransactionQuery()) ->setViewer($viewer) ->withObjectPHIDs(array($build->getPHID())) ->execute(); $timeline = id(new PhabricatorApplicationTransactionView()) ->setUser($viewer) ->setObjectPHID($build->getPHID()) ->setTransactions($xactions); return $this->buildApplicationPage( array( $crumbs, $box, $targets, $timeline, ), array( 'title' => $title, )); } private function buildArtifacts( HarbormasterBuildTarget $build_target) { $request = $this->getRequest(); $viewer = $request->getUser(); $artifacts = id(new HarbormasterBuildArtifactQuery()) ->setViewer($viewer) ->withBuildTargetPHIDs(array($build_target->getPHID())) ->execute(); if (count($artifacts) === 0) { return null; } $list = id(new PHUIObjectItemListView()) ->setFlush(true); foreach ($artifacts as $artifact) { $list->addItem($artifact->getObjectItemView($viewer)); } return $list; } private function buildLog( HarbormasterBuild $build, HarbormasterBuildTarget $build_target) { $request = $this->getRequest(); $viewer = $request->getUser(); $limit = $request->getInt('l', 25); $logs = id(new HarbormasterBuildLogQuery()) ->setViewer($viewer) ->withBuildTargetPHIDs(array($build_target->getPHID())) ->execute(); $empty_logs = array(); $log_boxes = array(); foreach ($logs as $log) { $start = 1; $lines = preg_split("/\r\n|\r|\n/", $log->getLogText()); if ($limit !== 0) { $start = count($lines) - $limit; if ($start >= 1) { $lines = array_slice($lines, -$limit, $limit); } else { $start = 1; } } $id = null; $is_empty = false; if (count($lines) === 1 && trim($lines[0]) === '') { // Prevent Harbormaster from showing empty build logs. $id = celerity_generate_unique_node_id(); $empty_logs[] = $id; $is_empty = true; } $log_view = new ShellLogView(); $log_view->setLines($lines); $log_view->setStart($start); $header = id(new PHUIHeaderView()) ->setHeader(pht( 'Build Log %d (%s - %s)', $log->getID(), $log->getLogSource(), $log->getLogType())) ->setSubheader($this->createLogHeader($build, $log)) ->setUser($viewer); $log_box = id(new PHUIObjectBoxView()) ->setHeader($header) ->setForm($log_view); if ($is_empty) { $log_box = phutil_tag( 'div', array( 'style' => 'display: none', 'id' => $id), $log_box); } $log_boxes[] = $log_box; } if ($empty_logs) { $hide_id = celerity_generate_unique_node_id(); Javelin::initBehavior('phabricator-reveal-content'); $expand = phutil_tag( 'div', array( 'id' => $hide_id, 'class' => 'harbormaster-empty-logs-are-hidden mlr mlt mll', ), array( pht( '%s empty logs are hidden.', new PhutilNumber(count($empty_logs))), ' ', javelin_tag( 'a', array( 'href' => '#', 'sigil' => 'reveal-content', 'meta' => array( 'showIDs' => $empty_logs, 'hideIDs' => array($hide_id), ), ), pht('Show all logs.')), )); array_unshift($log_boxes, $expand); } return $log_boxes; } private function createLogHeader($build, $log) { $request = $this->getRequest(); $limit = $request->getInt('l', 25); $lines_25 = $this->getApplicationURI('/build/'.$build->getID().'/?l=25'); $lines_50 = $this->getApplicationURI('/build/'.$build->getID().'/?l=50'); $lines_100 = $this->getApplicationURI('/build/'.$build->getID().'/?l=100'); $lines_0 = $this->getApplicationURI('/build/'.$build->getID().'/?l=0'); $link_25 = phutil_tag('a', array('href' => $lines_25), pht('25')); $link_50 = phutil_tag('a', array('href' => $lines_50), pht('50')); $link_100 = phutil_tag('a', array('href' => $lines_100), pht('100')); $link_0 = phutil_tag('a', array('href' => $lines_0), pht('Unlimited')); if ($limit === 25) { $link_25 = phutil_tag('strong', array(), $link_25); } else if ($limit === 50) { $link_50 = phutil_tag('strong', array(), $link_50); } else if ($limit === 100) { $link_100 = phutil_tag('strong', array(), $link_100); } else if ($limit === 0) { $link_0 = phutil_tag('strong', array(), $link_0); } return phutil_tag( 'span', array(), array( $link_25, ' - ', $link_50, ' - ', $link_100, ' - ', $link_0, ' Lines')); } private function buildActionList(HarbormasterBuild $build) { $request = $this->getRequest(); $viewer = $request->getUser(); $id = $build->getID(); $list = id(new PhabricatorActionListView()) ->setUser($viewer) ->setObject($build) ->setObjectURI("/build/{$id}"); $can_restart = $build->canRestartBuild(); $can_stop = $build->canStopBuild(); $can_resume = $build->canResumeBuild(); $list->addAction( id(new PhabricatorActionView()) ->setName(pht('Restart Build')) - ->setIcon('fa-backward') + ->setIcon('fa-repeat') ->setHref($this->getApplicationURI('/build/restart/'.$id.'/')) ->setDisabled(!$can_restart) ->setWorkflow(true)); - $list->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Stop Build')) - ->setIcon('fa-stop') - ->setHref($this->getApplicationURI('/build/stop/'.$id.'/')) - ->setDisabled(!$can_stop) - ->setWorkflow(true)); - - $list->addAction( - id(new PhabricatorActionView()) - ->setName(pht('Resume Build')) - ->setIcon('fa-play') - ->setHref($this->getApplicationURI('/build/resume/'.$id.'/')) - ->setDisabled(!$can_resume) - ->setWorkflow(true)); + if ($build->canResumeBuild()) { + $list->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Resume Build')) + ->setIcon('fa-play') + ->setHref($this->getApplicationURI('/build/resume/'.$id.'/')) + ->setDisabled(!$can_resume) + ->setWorkflow(true)); + } else { + $list->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Pause Build')) + ->setIcon('fa-pause') + ->setHref($this->getApplicationURI('/build/stop/'.$id.'/')) + ->setDisabled(!$can_stop) + ->setWorkflow(true)); + } return $list; } private function buildPropertyLists( PHUIObjectBoxView $box, HarbormasterBuild $build, PhabricatorActionListView $actions) { $request = $this->getRequest(); $viewer = $request->getUser(); $properties = id(new PHUIPropertyListView()) ->setUser($viewer) ->setObject($build) ->setActionList($actions); $box->addPropertyList($properties); $handles = id(new PhabricatorHandleQuery()) ->setViewer($viewer) ->withPHIDs(array( $build->getBuildablePHID(), $build->getBuildPlanPHID())) ->execute(); $properties->addProperty( pht('Buildable'), $handles[$build->getBuildablePHID()]->renderLink()); $properties->addProperty( pht('Build Plan'), $handles[$build->getBuildPlanPHID()]->renderLink()); $properties->addProperty( pht('Status'), $this->getStatus($build)); } private function getStatus(HarbormasterBuild $build) { $status_view = new PHUIStatusListView(); $item = new PHUIStatusItemView(); if ($build->isStopping()) { - $status_name = pht('Stopping'); + $status_name = pht('Pausing'); $icon = PHUIStatusItemView::ICON_RIGHT; $color = 'dark'; } else { $status = $build->getBuildStatus(); $status_name = HarbormasterBuild::getBuildStatusName($status); $icon = HarbormasterBuild::getBuildStatusIcon($status); $color = HarbormasterBuild::getBuildStatusColor($status); } $item->setTarget($status_name); $item->setIcon($icon, $color); $status_view->addItem($item); return $status_view; } private function buildMessages(array $messages) { $viewer = $this->getRequest()->getUser(); if ($messages) { $handles = id(new PhabricatorHandleQuery()) ->setViewer($viewer) ->withPHIDs(mpull($messages, 'getAuthorPHID')) ->execute(); } else { $handles = array(); } $rows = array(); foreach ($messages as $message) { $rows[] = array( $message->getID(), $handles[$message->getAuthorPHID()]->renderLink(), $message->getType(), $message->getIsConsumed() ? pht('Consumed') : null, phabricator_datetime($message->getDateCreated(), $viewer), ); } $table = new AphrontTableView($rows); $table->setNoDataString(pht('No messages for this build target.')); $table->setHeaders( array( pht('ID'), pht('From'), pht('Type'), pht('Consumed'), pht('Received'), )); $table->setColumnClasses( array( '', '', 'wide', '', 'date', )); return $table; } } diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php index 3c8292df2..e75022914 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php @@ -1,285 +1,258 @@ <?php final class HarbormasterBuildableViewController 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; $buildable = id(new HarbormasterBuildableQuery()) ->setViewer($viewer) ->withIDs(array($id)) ->needBuildableHandles(true) ->needContainerHandles(true) ->executeOne(); if (!$buildable) { return new Aphront404Response(); } // Pull builds and build targets. $builds = id(new HarbormasterBuildQuery()) ->setViewer($viewer) ->withBuildablePHIDs(array($buildable->getPHID())) ->needBuildTargets(true) ->execute(); $buildable->attachBuilds($builds); $build_list = $this->buildBuildList($buildable); $title = pht('Buildable %d', $id); $header = id(new PHUIHeaderView()) ->setHeader($title) ->setUser($viewer) ->setPolicyObject($buildable); $box = id(new PHUIObjectBoxView()) ->setHeader($header); $xactions = id(new HarbormasterBuildableTransactionQuery()) ->setViewer($viewer) ->withObjectPHIDs(array($buildable->getPHID())) ->execute(); $timeline = id(new PhabricatorApplicationTransactionView()) ->setUser($viewer) ->setObjectPHID($buildable->getPHID()) ->setTransactions($xactions); $actions = $this->buildActionList($buildable); $this->buildPropertyLists($box, $buildable, $actions); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb("B{$id}"); return $this->buildApplicationPage( array( $crumbs, $box, $build_list, $timeline, ), array( 'title' => $title, )); } private function buildActionList(HarbormasterBuildable $buildable) { $request = $this->getRequest(); $viewer = $request->getUser(); $id = $buildable->getID(); $list = id(new PhabricatorActionListView()) ->setUser($viewer) ->setObject($buildable) ->setObjectURI($buildable->getMonogram()); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $buildable, PhabricatorPolicyCapability::CAN_EDIT); $can_restart = false; $can_resume = false; $can_stop = false; foreach ($buildable->getBuilds() as $build) { if ($build->canRestartBuild()) { $can_restart = true; } if ($build->canResumeBuild()) { $can_resume = true; } if ($build->canStopBuild()) { $can_stop = true; } } $restart_uri = "buildable/{$id}/restart/"; $stop_uri = "buildable/{$id}/stop/"; $resume_uri = "buildable/{$id}/resume/"; $list->addAction( id(new PhabricatorActionView()) - ->setIcon('fa-backward') + ->setIcon('fa-repeat') ->setName(pht('Restart All Builds')) ->setHref($this->getApplicationURI($restart_uri)) ->setWorkflow(true) ->setDisabled(!$can_restart || !$can_edit)); $list->addAction( id(new PhabricatorActionView()) - ->setIcon('fa-stop') - ->setName(pht('Stop All Builds')) + ->setIcon('fa-pause') + ->setName(pht('Pause All Builds')) ->setHref($this->getApplicationURI($stop_uri)) ->setWorkflow(true) ->setDisabled(!$can_stop || !$can_edit)); $list->addAction( id(new PhabricatorActionView()) ->setIcon('fa-play') ->setName(pht('Resume All Builds')) ->setHref($this->getApplicationURI($resume_uri)) ->setWorkflow(true) ->setDisabled(!$can_resume || !$can_edit)); return $list; } private function buildPropertyLists( PHUIObjectBoxView $box, HarbormasterBuildable $buildable, PhabricatorActionListView $actions) { $request = $this->getRequest(); $viewer = $request->getUser(); $properties = id(new PHUIPropertyListView()) ->setUser($viewer) ->setObject($buildable) ->setActionList($actions); $box->addPropertyList($properties); $properties->addProperty( pht('Buildable'), $buildable->getBuildableHandle()->renderLink()); if ($buildable->getContainerHandle() !== null) { $properties->addProperty( pht('Container'), $buildable->getContainerHandle()->renderLink()); } $properties->addProperty( pht('Origin'), $buildable->getIsManualBuildable() ? pht('Manual Buildable') : pht('Automatic Buildable')); } private function buildBuildList(HarbormasterBuildable $buildable) { $viewer = $this->getRequest()->getUser(); $build_list = id(new PHUIObjectItemListView()) ->setUser($viewer); foreach ($buildable->getBuilds() as $build) { $view_uri = $this->getApplicationURI('/build/'.$build->getID().'/'); $item = id(new PHUIObjectItemView()) ->setObjectName(pht('Build %d', $build->getID())) ->setHeader($build->getName()) ->setHref($view_uri); $status = $build->getBuildStatus(); $item->setBarColor(HarbormasterBuild::getBuildStatusColor($status)); $item->addAttribute(HarbormasterBuild::getBuildStatusName($status)); if ($build->isRestarting()) { - $item->addIcon('fa-backward', pht('Restarting')); + $item->addIcon('fa-repeat', pht('Restarting')); } else if ($build->isStopping()) { - $item->addIcon('fa-stop', pht('Stopping')); + $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/"; $stop_uri = "build/stop/{$build_id}/buildable/"; $item->addAction( id(new PHUIListItemView()) - ->setIcon('fa-backward') + ->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-stop') - ->setName(pht('Stop')) + ->setIcon('fa-pause') + ->setName(pht('Pause')) ->setHref($this->getApplicationURI($stop_uri)) ->setWorkflow(true) ->setDisabled(!$build->canStopBuild())); } $targets = $build->getBuildTargets(); if ($targets) { $target_list = id(new PHUIStatusListView()); foreach ($targets as $target) { - switch ($target->getTargetStatus()) { - case HarbormasterBuildTarget::STATUS_PENDING: - $icon = PHUIStatusItemView::ICON_CLOCK; - $color = 'green'; - $status_name = pht('Pending'); - break; - case HarbormasterBuildTarget::STATUS_BUILDING: - $icon = PHUIStatusItemView::ICON_RIGHT; - $color = 'green'; - $status_name = pht('Building'); - break; - case HarbormasterBuildTarget::STATUS_WAITING: - $icon = PHUIStatusItemView::ICON_CLOCK; - $color = 'orange'; - $status_name = pht('Waiting'); - break; - case HarbormasterBuildTarget::STATUS_PASSED: - $icon = PHUIStatusItemView::ICON_ACCEPT; - $color = 'green'; - $status_name = pht('Passed'); - break; - case HarbormasterBuildTarget::STATUS_FAILED: - $icon = PHUIStatusItemView::ICON_REJECT; - $color = 'red'; - $status_name = pht('Failed'); - break; - default: - $icon = PHUIStatusItemView::ICON_QUESTION; - $color = 'bluegrey'; - $status_name = pht('Unknown'); - break; - } + $status = $target->getTargetStatus(); + $icon = HarbormasterBuildTarget::getBuildTargetStatusIcon($status); + $color = HarbormasterBuildTarget::getBuildTargetStatusColor($status); + $status_name = + HarbormasterBuildTarget::getBuildTargetStatusName($status); $name = $target->getName(); $target_list->addItem( id(new PHUIStatusItemView()) ->setIcon($icon, $color, $status_name) ->setTarget(pht('Target %d', $target->getID())) ->setNote($name)); } $target_box = id(new PHUIBoxView()) ->addPadding(PHUI::PADDING_SMALL) ->appendChild($target_list); $item->appendChild($target_box); } $build_list->addItem($item); } return $build_list; } } diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuild.php b/src/applications/harbormaster/storage/build/HarbormasterBuild.php index 2b7c06507..3395052ef 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php @@ -1,406 +1,406 @@ <?php final class HarbormasterBuild extends HarbormasterDAO implements PhabricatorPolicyInterface { protected $buildablePHID; protected $buildPlanPHID; protected $buildStatus; private $buildable = self::ATTACHABLE; private $buildPlan = self::ATTACHABLE; private $buildTargets = self::ATTACHABLE; private $unprocessedCommands = self::ATTACHABLE; /** * Not currently being built. */ const STATUS_INACTIVE = 'inactive'; /** * Pending pick up by the Harbormaster daemon. */ const STATUS_PENDING = 'pending'; /** * Current building the buildable. */ const STATUS_BUILDING = 'building'; /** * The build has passed. */ const STATUS_PASSED = 'passed'; /** * The build has failed. */ const STATUS_FAILED = 'failed'; /** * The build encountered an unexpected error. */ const STATUS_ERROR = 'error'; /** * The build has been stopped. */ const STATUS_STOPPED = 'stopped'; /** * The build has been deadlocked. */ const STATUS_DEADLOCKED = 'deadlocked'; /** * Get a human readable name for a build status constant. * * @param const Build status constant. * @return string Human-readable name. */ public static function getBuildStatusName($status) { switch ($status) { case self::STATUS_INACTIVE: return pht('Inactive'); case self::STATUS_PENDING: return pht('Pending'); case self::STATUS_BUILDING: return pht('Building'); case self::STATUS_PASSED: return pht('Passed'); case self::STATUS_FAILED: return pht('Failed'); case self::STATUS_ERROR: return pht('Unexpected Error'); case self::STATUS_STOPPED: - return pht('Stopped'); + return pht('Paused'); case self::STATUS_DEADLOCKED: return pht('Deadlocked'); default: return pht('Unknown'); } } public static function getBuildStatusIcon($status) { switch ($status) { case self::STATUS_INACTIVE: case self::STATUS_PENDING: return PHUIStatusItemView::ICON_OPEN; case self::STATUS_BUILDING: return PHUIStatusItemView::ICON_RIGHT; case self::STATUS_PASSED: return PHUIStatusItemView::ICON_ACCEPT; case self::STATUS_FAILED: return PHUIStatusItemView::ICON_REJECT; case self::STATUS_ERROR: return PHUIStatusItemView::ICON_MINUS; case self::STATUS_STOPPED: return PHUIStatusItemView::ICON_MINUS; case self::STATUS_DEADLOCKED: return PHUIStatusItemView::ICON_WARNING; default: return PHUIStatusItemView::ICON_QUESTION; } } public static function getBuildStatusColor($status) { switch ($status) { case self::STATUS_INACTIVE: return 'dark'; case self::STATUS_PENDING: case self::STATUS_BUILDING: return 'blue'; case self::STATUS_PASSED: return 'green'; case self::STATUS_FAILED: case self::STATUS_ERROR: case self::STATUS_DEADLOCKED: return 'red'; case self::STATUS_STOPPED: return 'dark'; default: return 'bluegrey'; } } public static function initializeNewBuild(PhabricatorUser $actor) { return id(new HarbormasterBuild()) ->setBuildStatus(self::STATUS_INACTIVE); } public function delete() { $this->openTransaction(); $this->deleteUnprocessedCommands(); $result = parent::delete(); $this->saveTransaction(); return $result; } public function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( HarbormasterBuildPHIDType::TYPECONST); } public function attachBuildable(HarbormasterBuildable $buildable) { $this->buildable = $buildable; return $this; } public function getBuildable() { return $this->assertAttached($this->buildable); } public function getName() { if ($this->getBuildPlan()) { return $this->getBuildPlan()->getName(); } return pht('Build'); } public function attachBuildPlan( HarbormasterBuildPlan $build_plan = null) { $this->buildPlan = $build_plan; return $this; } public function getBuildPlan() { return $this->assertAttached($this->buildPlan); } public function getBuildTargets() { return $this->assertAttached($this->buildTargets); } public function attachBuildTargets(array $targets) { $this->buildTargets = $targets; return $this; } public function isBuilding() { return $this->getBuildStatus() === self::STATUS_PENDING || $this->getBuildStatus() === self::STATUS_BUILDING; } public function createLog( HarbormasterBuildTarget $build_target, $log_source, $log_type) { $log_source = phutil_utf8_shorten($log_source, 250); $log = HarbormasterBuildLog::initializeNewBuildLog($build_target) ->setLogSource($log_source) ->setLogType($log_type) ->save(); return $log; } public function createArtifact( HarbormasterBuildTarget $build_target, $artifact_key, $artifact_type) { $artifact = HarbormasterBuildArtifact::initializeNewBuildArtifact($build_target); $artifact->setArtifactKey($this->getPHID(), $artifact_key); $artifact->setArtifactType($artifact_type); $artifact->save(); return $artifact; } public function loadArtifact($name) { $artifact = id(new HarbormasterBuildArtifactQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withArtifactKeys( $this->getPHID(), array($name)) ->executeOne(); if ($artifact === null) { throw new Exception('Artifact not found!'); } return $artifact; } public function retrieveVariablesFromBuild() { $results = array( 'buildable.diff' => null, 'buildable.revision' => null, 'buildable.commit' => null, 'repository.callsign' => null, 'repository.vcs' => null, 'repository.uri' => null, 'step.timestamp' => null, 'build.id' => null, ); $buildable = $this->getBuildable(); $object = $buildable->getBuildableObject(); $object_variables = $object->getBuildVariables(); $results = $object_variables + $results; $results['step.timestamp'] = time(); $results['build.id'] = $this->getID(); return $results; } public static function getAvailableBuildVariables() { $objects = id(new PhutilSymbolLoader()) ->setAncestorClass('HarbormasterBuildableInterface') ->loadObjects(); $variables = array(); $variables[] = array( 'step.timestamp' => pht('The current UNIX timestamp.'), 'build.id' => pht('The ID of the current build.'), 'target.phid' => pht('The PHID of the current build target.'), ); foreach ($objects as $object) { $variables[] = $object->getAvailableBuildVariables(); } $variables = array_mergev($variables); return $variables; } 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 )----------------------------------------------------- */ private function getUnprocessedCommands() { return $this->assertAttached($this->unprocessedCommands); } public function attachUnprocessedCommands(array $commands) { $this->unprocessedCommands = $commands; return $this; } 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() { $is_stopping = false; foreach ($this->getUnprocessedCommands() as $command_object) { $command = $command_object->getCommand(); switch ($command) { case HarbormasterBuildCommand::COMMAND_STOP: $is_stopping = true; break; case HarbormasterBuildCommand::COMMAND_RESUME: case HarbormasterBuildCommand::COMMAND_RESTART: $is_stopping = false; break; } } return $is_stopping; } public function isResuming() { $is_resuming = false; foreach ($this->getUnprocessedCommands() as $command_object) { $command = $command_object->getCommand(); switch ($command) { case HarbormasterBuildCommand::COMMAND_RESTART: case HarbormasterBuildCommand::COMMAND_RESUME: $is_resuming = true; break; case HarbormasterBuildCommand::COMMAND_STOP: $is_resuming = false; break; } } return $is_resuming; } public function isRestarting() { $is_restarting = false; foreach ($this->getUnprocessedCommands() as $command_object) { $command = $command_object->getCommand(); switch ($command) { case HarbormasterBuildCommand::COMMAND_RESTART: $is_restarting = true; break; } } return $is_restarting; } public function deleteUnprocessedCommands() { foreach ($this->getUnprocessedCommands() as $key => $command_object) { $command_object->delete(); unset($this->unprocessedCommands[$key]); } return $this; } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { return $this->getBuildable()->getPolicy($capability); } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { return $this->getBuildable()->hasAutomaticCapability( $capability, $viewer); } public function describeAutomaticCapability($capability) { return pht('A build inherits policies from its buildable.'); } }