diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php index f080961efc..461425ba3d 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php @@ -1,254 +1,311 @@ getViewer(); $buildable = id(new HarbormasterBuildableQuery()) ->setViewer($viewer) ->withIDs(array($request->getURIData('id'))) ->needBuildableHandles(true) ->needContainerHandles(true) ->executeOne(); if (!$buildable) { return new Aphront404Response(); } $id = $buildable->getID(); // Pull builds and build targets. $builds = id(new HarbormasterBuildQuery()) ->setViewer($viewer) ->withBuildablePHIDs(array($buildable->getPHID())) ->needBuildTargets(true) ->execute(); + list($lint, $unit) = $this->renderLintAndUnit($builds); + $buildable->attachBuilds($builds); $object = $buildable->getBuildableObject(); $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); $timeline = $this->buildTransactionTimeline( $buildable, new HarbormasterBuildableTransactionQuery()); $timeline->setShouldTerminate(true); $actions = $this->buildActionList($buildable); $this->buildPropertyLists($box, $buildable, $actions); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($buildable->getMonogram()); return $this->buildApplicationPage( array( $crumbs, $box, + $lint, + $unit, $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-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-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); if ($buildable->getContainerHandle() !== null) { $properties->addProperty( pht('Container'), $buildable->getContainerHandle()->renderLink()); } $properties->addProperty( pht('Buildable'), $buildable->getBuildableHandle()->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-repeat', pht('Restarting')); } else if ($build->isStopping()) { $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-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($stop_uri)) ->setWorkflow(true) ->setDisabled(!$build->canStopBuild())); } $targets = $build->getBuildTargets(); if ($targets) { $target_list = id(new PHUIStatusListView()); foreach ($targets as $target) { $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); } $build_list->setFlush(true); $box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Builds')) ->appendChild($build_list); return $box; } + private function renderLintAndUnit(array $builds) { + $viewer = $this->getViewer(); + + $targets = array(); + foreach ($builds as $build) { + foreach ($build->getBuildTargets() as $target) { + $targets[] = $target; + } + } + + if (!$targets) { + return; + } + + $target_phids = mpull($targets, 'getPHID'); + + $lint_data = id(new HarbormasterBuildLintMessage())->loadAllWhere( + 'buildTargetPHID IN (%Ls) LIMIT 25', + $target_phids); + + $unit_data = id(new HarbormasterBuildUnitMessage())->loadAllWhere( + 'buildTargetPHID IN (%Ls) LIMIT 25', + $target_phids); + + if ($lint_data) { + $lint_table = id(new HarbormasterLintPropertyView()) + ->setUser($viewer) + ->setLintMessages($lint_data); + + $lint = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Lint Messages')) + ->appendChild($lint_table); + } else { + $lint = null; + } + + if ($unit_data) { + $unit_table = id(new HarbormasterUnitPropertyView()) + ->setUser($viewer) + ->setUnitMessages($unit_data); + + $unit = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Unit Tests')) + ->appendChild($unit_table); + } else { + $unit = null; + } + + return array($lint, $unit); + } + + + } diff --git a/src/applications/harbormaster/view/HarbormasterLintPropertyView.php b/src/applications/harbormaster/view/HarbormasterLintPropertyView.php index 8cf86124fe..f2591daae5 100644 --- a/src/applications/harbormaster/view/HarbormasterLintPropertyView.php +++ b/src/applications/harbormaster/view/HarbormasterLintPropertyView.php @@ -1,78 +1,78 @@ pathURIMap = $map; return $this; } public function setLintMessages(array $messages) { assert_instances_of($messages, 'HarbormasterBuildLintMessage'); $this->lintMessages = $messages; return $this; } public function render() { $rows = array(); foreach ($this->lintMessages as $message) { $path = $message->getPath(); $line = $message->getLine(); $href = null; if (strlen(idx($this->pathURIMap, $path))) { $href = $this->pathURIMap[$path].max($line, 1); } $severity = $this->renderSeverity($message->getSeverity()); $location = $path.':'.$line; if (strlen($href)) { $location = phutil_tag( 'a', array( 'href' => $href, ), $location); } $rows[] = array( $location, $severity, $message->getCode(), $message->getName(), ); } $table = id(new AphrontTableView($rows)) ->setHeaders( array( pht('Location'), pht('Severity'), pht('Code'), pht('Message'), )) ->setColumnClasses( array( 'pri', null, null, 'wide', )); return $table; } private function renderSeverity($severity) { $names = ArcanistLintSeverity::getLintSeverities(); $name = idx($names, $severity, $severity); // TODO: Add some color here? return $name; } } diff --git a/src/applications/harbormaster/view/HarbormasterUnitPropertyView.php b/src/applications/harbormaster/view/HarbormasterUnitPropertyView.php index e49749636c..8bff9744f7 100644 --- a/src/applications/harbormaster/view/HarbormasterUnitPropertyView.php +++ b/src/applications/harbormaster/view/HarbormasterUnitPropertyView.php @@ -1,90 +1,90 @@ pathURIMap = $map; return $this; } public function setUnitMessages(array $messages) { assert_instances_of($messages, 'HarbormasterBuildUnitMessage'); $this->unitMessages = $messages; return $this; } public function render() { $rows = array(); $any_duration = false; foreach ($this->unitMessages as $message) { $result = $this->renderResult($message->getResult()); $duration = $message->getDuration(); if ($duration !== null) { $any_duration = true; $duration = pht('%s ms', new PhutilNumber((int)(1000 * $duration))); } $name = $message->getName(); $namespace = $message->getNamespace(); if (strlen($namespace)) { $name = $namespace.'::'.$name; } $engine = $message->getEngine(); if (strlen($engine)) { $name = $engine.' > '.$name; } $rows[] = array( $result, $duration, $name, ); } $table = id(new AphrontTableView($rows)) ->setHeaders( array( pht('Result'), pht('Time'), pht('Test'), )) ->setColumnClasses( array( null, null, 'pri wide', )) ->setColumnVisibility( array( true, $any_duration, )); return $table; } private function renderResult($result) { $names = array( ArcanistUnitTestResult::RESULT_BROKEN => pht('Broken'), ArcanistUnitTestResult::RESULT_FAIL => pht('Failed'), ArcanistUnitTestResult::RESULT_UNSOUND => pht('Unsound'), ArcanistUnitTestResult::RESULT_SKIP => pht('Skipped'), ArcanistUnitTestResult::RESULT_POSTPONED => pht('Postponed'), ArcanistUnitTestResult::RESULT_PASS => pht('Passed'), ); $result = idx($names, $result, $result); // TODO: Add some color. return $result; } }