diff --git a/resources/sql/autopatches/20140731.harbormasterstepdesc.sql b/resources/sql/autopatches/20140731.harbormasterstepdesc.sql new file mode 100644 index 0000000000..8824727afb --- /dev/null +++ b/resources/sql/autopatches/20140731.harbormasterstepdesc.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildstep + ADD description LONGTEXT NOT NULL COLLATE utf8_bin; diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php index 6be70a133c..d3baf9ef2e 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php @@ -1,377 +1,414 @@ 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(); } $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')); } 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(pht( - 'Build Target %d (%s)', - $build_target->getID(), - $build_target->getName())) + ->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->addSectionHeader(pht('Configuration 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->addSectionHeader(pht('Variables')); + $properties = new PHUIPropertyListView(); foreach ($variables as $key => $value) { $properties->addProperty($key, $value); } + $target_box->addPropertyList($properties, pht('Variables')); } - $targets[] = id(new PHUIObjectBoxView()) - ->setHeader($header) - ->addPropertyList($properties); + $properties = new PHUIPropertyListView(); + $properties->addProperty('Build Target ID', $build_target->getID()); + $target_box->addPropertyList($properties, pht('Metadata')); + + $targets[] = $target_box; $build_messages = idx($messages, $build_target->getPHID(), array()); if ($build_messages) { $targets[] = $this->buildMessages($build_messages); } $targets[] = $this->buildArtifacts($build_target); $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 = new PHUIObjectItemListView(); foreach ($artifacts as $artifact) { $list->addItem($artifact->getObjectItemView($viewer)); } $header = id(new PHUIHeaderView()) ->setHeader(pht('Build Artifacts')) ->setUser($viewer); $box = id(new PHUIObjectBoxView()) ->setHeader($header); return array($box, $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(); $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; } } $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_boxes[] = id(new PHUIObjectBoxView()) ->setHeader($header) ->setForm($log_view); } 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') ->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)); 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); $properties->addProperty( pht('Status'), $this->getStatus($build)); $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()); } private function getStatus(HarbormasterBuild $build) { if ($build->isStopping()) { return pht('Stopping'); } return HarbormasterBuild::getBuildStatusName($build->getBuildStatus()); } 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', )); $box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Build Target Messages')) ->appendChild($table); return $box; } } diff --git a/src/applications/harbormaster/controller/HarbormasterStepEditController.php b/src/applications/harbormaster/controller/HarbormasterStepEditController.php index 33aa8dd4b3..62a8ac0e68 100644 --- a/src/applications/harbormaster/controller/HarbormasterStepEditController.php +++ b/src/applications/harbormaster/controller/HarbormasterStepEditController.php @@ -1,225 +1,243 @@ id = idx($data, 'id'); $this->planID = idx($data, 'plan'); $this->className = idx($data, 'class'); } public function processRequest() { $request = $this->getRequest(); $viewer = $request->getUser(); $this->requireApplicationCapability( HarbormasterManagePlansCapability::CAPABILITY); if ($this->id) { $step = id(new HarbormasterBuildStepQuery()) ->setViewer($viewer) ->withIDs(array($this->id)) ->executeOne(); if (!$step) { return new Aphront404Response(); } $plan = $step->getBuildPlan(); $is_new = false; } else { $plan = id(new HarbormasterBuildPlanQuery()) ->setViewer($viewer) ->withIDs(array($this->planID)) ->executeOne(); if (!$plan) { return new Aphront404Response(); } $impl = HarbormasterBuildStepImplementation::getImplementation( $this->className); if (!$impl) { return new Aphront404Response(); } $step = HarbormasterBuildStep::initializeNewStep($viewer) ->setBuildPlanPHID($plan->getPHID()) ->setClassName($this->className); $is_new = true; } $plan_uri = $this->getApplicationURI('plan/'.$plan->getID().'/'); $implementation = $step->getStepImplementation(); $field_list = PhabricatorCustomField::getObjectFields( $step, PhabricatorCustomField::ROLE_EDIT); $field_list ->setViewer($viewer) ->readFieldsFromStorage($step); $e_name = true; $v_name = $step->getName(); + $e_description = true; + $v_description = $step->getDescription(); $e_depends_on = true; $raw_depends_on = $step->getDetail('dependsOn', array()); $v_depends_on = id(new PhabricatorHandleQuery()) ->setViewer($viewer) ->withPHIDs($raw_depends_on) ->execute(); $errors = array(); $validation_exception = null; if ($request->isFormPost()) { $e_name = null; $v_name = $request->getStr('name'); + $e_description = null; + $v_description = $request->getStr('description'); $e_depends_on = null; $v_depends_on = $request->getArr('dependsOn'); $xactions = $field_list->buildFieldTransactionsFromRequest( new HarbormasterBuildStepTransaction(), $request); $editor = id(new HarbormasterBuildStepEditor()) ->setActor($viewer) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request); $name_xaction = id(new HarbormasterBuildStepTransaction()) ->setTransactionType(HarbormasterBuildStepTransaction::TYPE_NAME) ->setNewValue($v_name); array_unshift($xactions, $name_xaction); $depends_on_xaction = id(new HarbormasterBuildStepTransaction()) ->setTransactionType( HarbormasterBuildStepTransaction::TYPE_DEPENDS_ON) ->setNewValue($v_depends_on); array_unshift($xactions, $depends_on_xaction); + $description_xaction = id(new HarbormasterBuildStepTransaction()) + ->setTransactionType( + HarbormasterBuildStepTransaction::TYPE_DESCRIPTION) + ->setNewValue($v_description); + array_unshift($xactions, $description_xaction); + if ($is_new) { // When creating a new step, make sure we have a create transaction // so we'll apply the transactions even if the step has no // configurable options. $create_xaction = id(new HarbormasterBuildStepTransaction()) ->setTransactionType(HarbormasterBuildStepTransaction::TYPE_CREATE); array_unshift($xactions, $create_xaction); } try { $editor->applyTransactions($step, $xactions); return id(new AphrontRedirectResponse())->setURI($plan_uri); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; } } $form = id(new AphrontFormView()) ->setUser($viewer) ->appendChild( id(new AphrontFormTextControl()) ->setName('name') ->setLabel(pht('Name')) ->setError($e_name) ->setValue($v_name)); $form ->appendChild( id(new AphrontFormTokenizerControl()) ->setDatasource(id(new HarbormasterBuildDependencyDatasource()) ->setParameters(array( 'planPHID' => $plan->getPHID(), 'stepPHID' => $is_new ? null : $step->getPHID(), ))) ->setName('dependsOn') ->setLabel(pht('Depends On')) ->setError($e_depends_on) ->setValue($v_depends_on)); $field_list->appendFieldsToForm($form); + $form + ->appendChild( + id(new PhabricatorRemarkupControl()) + ->setName('description') + ->setLabel(pht('Description')) + ->setError($e_description) + ->setValue($v_description)); + if ($is_new) { $submit = pht('Create Build Step'); $header = pht('New Step: %s', $implementation->getName()); $crumb = pht('Add Step'); } else { $submit = pht('Save Build Step'); $header = pht('Edit Step: %s', $implementation->getName()); $crumb = pht('Edit Step'); } $form->appendChild( id(new AphrontFormSubmitControl()) ->setValue($submit) ->addCancelButton($plan_uri)); $box = id(new PHUIObjectBoxView()) ->setHeaderText($header) ->setValidationException($validation_exception) ->setForm($form); $crumbs = $this->buildApplicationCrumbs(); $id = $plan->getID(); $crumbs->addTextCrumb(pht('Plan %d', $id), $plan_uri); $crumbs->addTextCrumb($crumb); $variables = $this->renderBuildVariablesTable(); if ($is_new) { $xaction_view = null; } else { $xactions = id(new HarbormasterBuildStepTransactionQuery()) ->setViewer($viewer) ->withObjectPHIDs(array($step->getPHID())) ->execute(); $xaction_view = id(new PhabricatorApplicationTransactionView()) ->setUser($viewer) ->setObjectPHID($step->getPHID()) ->setTransactions($xactions) ->setShouldTerminate(true); } return $this->buildApplicationPage( array( $crumbs, $box, $variables, $xaction_view, ), array( 'title' => $implementation->getName(), )); } private function renderBuildVariablesTable() { $viewer = $this->getRequest()->getUser(); $variables = HarbormasterBuild::getAvailableBuildVariables(); ksort($variables); $rows = array(); $rows[] = pht( 'The following variables can be used in most fields. To reference '. 'a variable, use `${name}` in a field.'); $rows[] = pht('| Variable | Description |'); $rows[] = '|---|---|'; foreach ($variables as $name => $description) { $rows[] = '| `'.$name.'` | '.$description.' |'; } $rows = implode("\n", $rows); $form = id(new AphrontFormView()) ->setUser($viewer) ->appendRemarkupInstructions($rows); return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Build Variables')) ->appendChild($form); } } diff --git a/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php index 14a3f40246..a486ff4db6 100644 --- a/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php +++ b/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php @@ -1,83 +1,93 @@ getTransactionType()) { case HarbormasterBuildStepTransaction::TYPE_CREATE: return null; case HarbormasterBuildStepTransaction::TYPE_NAME: if ($this->getIsNewObject()) { return null; } return $object->getName(); case HarbormasterBuildStepTransaction::TYPE_DEPENDS_ON: if ($this->getIsNewObject()) { return null; } return $object->getDetail('dependsOn', array()); + case HarbormasterBuildStepTransaction::TYPE_DESCRIPTION: + if ($this->getIsNewObject()) { + return null; + } + return $object->getDescription(); } return parent::getCustomTransactionOldValue($object, $xaction); } protected function getCustomTransactionNewValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case HarbormasterBuildStepTransaction::TYPE_CREATE: return true; case HarbormasterBuildStepTransaction::TYPE_NAME: case HarbormasterBuildStepTransaction::TYPE_DEPENDS_ON: + case HarbormasterBuildStepTransaction::TYPE_DESCRIPTION: return $xaction->getNewValue(); } return parent::getCustomTransactionNewValue($object, $xaction); } protected function applyCustomInternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case HarbormasterBuildStepTransaction::TYPE_CREATE: return; case HarbormasterBuildStepTransaction::TYPE_NAME: return $object->setName($xaction->getNewValue()); case HarbormasterBuildStepTransaction::TYPE_DEPENDS_ON: return $object->setDetail('dependsOn', $xaction->getNewValue()); + case HarbormasterBuildStepTransaction::TYPE_DESCRIPTION: + return $object->setDescription($xaction->getNewValue()); } return parent::applyCustomInternalTransaction($object, $xaction); } protected function applyCustomExternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case HarbormasterBuildStepTransaction::TYPE_CREATE: case HarbormasterBuildStepTransaction::TYPE_NAME: case HarbormasterBuildStepTransaction::TYPE_DEPENDS_ON: + case HarbormasterBuildStepTransaction::TYPE_DESCRIPTION: return; } return parent::applyCustomExternalTransaction($object, $xaction); } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php b/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php index 652ef56282..88c51f5e36 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildTargetQuery.php @@ -1,98 +1,129 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withBuildPHIDs(array $build_phids) { $this->buildPHIDs = $build_phids; return $this; } + public function needBuildSteps($need_build_steps) { + $this->needBuildSteps = $need_build_steps; + return $this; + } + protected function loadPage() { $table = new HarbormasterBuildTarget(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } private function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid in (%Ls)', $this->phids); } if ($this->buildPHIDs) { $where[] = qsprintf( $conn_r, 'buildPHID in (%Ls)', $this->buildPHIDs); } $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); } + protected function didFilterPage(array $page) { + if ($this->needBuildSteps) { + $step_phids = array(); + + foreach ($page as $target) { + $step_phids[] = $target->getBuildStepPHID(); + } + + $steps = id(new HarbormasterBuildStepQuery()) + ->setViewer($this->getViewer()) + ->setParentQuery($this) + ->withPHIDs($step_phids) + ->execute(); + + $steps = mpull($steps, null, 'getPHID'); + + foreach ($page as $target) { + $target->attachBuildStep( + $steps[$target->getBuildStepPHID()]); + } + } + + return $page; + } + protected function willFilterPage(array $page) { $builds = array(); $build_phids = array_filter(mpull($page, 'getBuildPHID')); if ($build_phids) { $builds = id(new PhabricatorObjectQuery()) ->setViewer($this->getViewer()) ->withPHIDs($build_phids) ->setParentQuery($this) ->execute(); $builds = mpull($builds, null, 'getPHID'); } foreach ($page as $key => $build_target) { $build_phid = $build_target->getBuildPHID(); if (empty($builds[$build_phid])) { unset($page[$key]); continue; } $build_target->attachBuild($builds[$build_phid]); } return $page; } public function getQueryApplicationClass() { return 'PhabricatorHarbormasterApplication'; } } diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php index 624219285f..3dc15f3843 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php @@ -1,190 +1,238 @@ setName($build_step->getName()) ->setBuildPHID($build->getPHID()) ->setBuildStepPHID($build_step->getPHID()) ->setClassName($build_step->getClassName()) ->setDetails($build_step->getDetails()) ->setTargetStatus(self::STATUS_PENDING) ->setVariables($variables); } public function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, self::CONFIG_SERIALIZATION => array( 'details' => self::SERIALIZATION_JSON, 'variables' => self::SERIALIZATION_JSON, ) ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( HarbormasterBuildTargetPHIDType::TYPECONST); } public function attachBuild(HarbormasterBuild $build) { $this->build = $build; return $this; } public function getBuild() { return $this->assertAttached($this->build); } public function attachBuildStep(HarbormasterBuildStep $step) { $this->buildStep = $step; return $this; } public function getBuildStep() { return $this->assertAttached($this->buildStep); } public function getDetail($key, $default = null) { return idx($this->details, $key, $default); } public function setDetail($key, $value) { $this->details[$key] = $value; return $this; } public function getVariables() { return parent::getVariables() + $this->getBuildTargetVariables(); } public function getVariable($key, $default = null) { return idx($this->variables, $key, $default); } public function setVariable($key, $value) { $this->variables[$key] = $value; return $this; } public function getImplementation() { if ($this->implementation === null) { $obj = HarbormasterBuildStepImplementation::requireImplementation( $this->className); $obj->loadSettings($this); $this->implementation = $obj; } return $this->implementation; } public function getName() { if (strlen($this->name)) { return $this->name; } try { return $this->getImplementation()->getName(); } catch (Exception $e) { return $this->getClassName(); } } private function getBuildTargetVariables() { return array( 'target.phid' => $this->getPHID(), ); } /* -( 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; } public function isWaiting() { switch ($this->getTargetStatus()) { case self::STATUS_WAITING: return true; } return false; } public function isUnderway() { switch ($this->getTargetStatus()) { case self::STATUS_PENDING: case self::STATUS_BUILDING: return true; } return false; } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, ); } public function getPolicy($capability) { return $this->getBuild()->getPolicy($capability); } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { return $this->getBuild()->hasAutomaticCapability( $capability, $viewer); } public function describeAutomaticCapability($capability) { return pht('Users must be able to see a build to view its build targets.'); } } diff --git a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php index 1204265b8c..74d9382d4b 100644 --- a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php +++ b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php @@ -1,117 +1,118 @@ true, self::CONFIG_SERIALIZATION => array( 'details' => self::SERIALIZATION_JSON, ) ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( HarbormasterBuildStepPHIDType::TYPECONST); } public function attachBuildPlan(HarbormasterBuildPlan $plan) { $this->buildPlan = $plan; return $this; } public function getBuildPlan() { return $this->assertAttached($this->buildPlan); } public function getDetail($key, $default = null) { return idx($this->details, $key, $default); } public function setDetail($key, $value) { $this->details[$key] = $value; return $this; } public function getName() { if (strlen($this->name)) { return $this->name; } return $this->getStepImplementation()->getName(); } public function getStepImplementation() { if ($this->implementation === null) { $obj = HarbormasterBuildStepImplementation::requireImplementation( $this->className); $obj->loadSettings($this); $this->implementation = $obj; } return $this->implementation; } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, ); } public function getPolicy($capability) { return $this->getBuildPlan()->getPolicy($capability); } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { return $this->getBuildPlan()->hasAutomaticCapability($capability, $viewer); } public function describeAutomaticCapability($capability) { return pht('A build step has the same policies as its build plan.'); } /* -( PhabricatorCustomFieldInterface )------------------------------------ */ public function getCustomFieldSpecificationForRole($role) { return array(); } public function getCustomFieldBaseClass() { return 'HarbormasterBuildStepCustomField'; } public function getCustomFields() { return $this->assertAttached($this->customFields); } public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) { $this->customFields = $fields; return $this; } } diff --git a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStepTransaction.php b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStepTransaction.php index 89dab2e51b..0ac25ee12f 100644 --- a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStepTransaction.php +++ b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStepTransaction.php @@ -1,62 +1,63 @@ getAuthorPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_CREATE: return pht( '%s created this build step.', $this->renderHandleLink($author_phid)); } return parent::getTitle(); } public function getIcon() { $author_phid = $this->getAuthorPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_CREATE: return 'fa-plus'; } return parent::getIcon(); } public function getColor() { $author_phid = $this->getAuthorPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_CREATE: return 'green'; } return parent::getColor(); } }