diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1370,6 +1370,7 @@ 'HarbormasterBuildTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildTransactionQuery.php', 'HarbormasterBuildUnitMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php', 'HarbormasterBuildUnitMessageQuery' => 'applications/harbormaster/query/HarbormasterBuildUnitMessageQuery.php', + 'HarbormasterBuildView' => 'applications/harbormaster/view/HarbormasterBuildView.php', 'HarbormasterBuildViewController' => 'applications/harbormaster/controller/HarbormasterBuildViewController.php', 'HarbormasterBuildWorker' => 'applications/harbormaster/worker/HarbormasterBuildWorker.php', 'HarbormasterBuildable' => 'applications/harbormaster/storage/HarbormasterBuildable.php', @@ -6999,6 +7000,7 @@ 'PhabricatorPolicyInterface', ), 'HarbormasterBuildUnitMessageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'HarbormasterBuildView' => 'AphrontView', 'HarbormasterBuildViewController' => 'HarbormasterController', 'HarbormasterBuildWorker' => 'HarbormasterWorker', 'HarbormasterBuildable' => array( diff --git a/src/applications/harbormaster/controller/HarbormasterPlanViewController.php b/src/applications/harbormaster/controller/HarbormasterPlanViewController.php --- a/src/applications/harbormaster/controller/HarbormasterPlanViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterPlanViewController.php @@ -18,11 +18,6 @@ return new Aphront404Response(); } - $timeline = $this->buildTransactionTimeline( - $plan, - new HarbormasterBuildPlanTransactionQuery()); - $timeline->setShouldTerminate(true); - $title = $plan->getName(); $header = id(new PHUIHeaderView()) @@ -33,24 +28,30 @@ $curtain = $this->buildCurtainView($plan); - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb(pht('Plan %d', $id)); - $crumbs->setBorder(true); + $crumbs = $this->buildApplicationCrumbs() + ->addTextCrumb($plan->getObjectName()) + ->setBorder(true); - list($step_list, $has_any_conflicts, $would_deadlock) = + list($step_list, $has_any_conflicts, $would_deadlock, $steps) = $this->buildStepList($plan); $error = null; - if ($would_deadlock) { - $error = pht('This build plan will deadlock when executed, due to '. - 'circular dependencies present in the build plan. '. - 'Examine the step list and resolve the deadlock.'); + if (!$steps) { + $error = pht( + 'This build plan does not have any build steps yet, so it will '. + 'not do anything when run.'); + } else if ($would_deadlock) { + $error = pht( + 'This build plan will deadlock when executed, due to circular '. + 'dependencies present in the build plan. Examine the step list '. + 'and resolve the deadlock.'); } else if ($has_any_conflicts) { // A deadlocking build will also cause all the artifacts to be // invalid, so we just skip showing this message if that's the // case. - $error = pht('This build plan has conflicts in one or more build steps. '. - 'Examine the step list and resolve the listed errors.'); + $error = pht( + 'This build plan has conflicts in one or more build steps. '. + 'Examine the step list and resolve the listed errors.'); } if ($error) { @@ -59,18 +60,28 @@ ->appendChild($error); } + $builds_view = $this->newBuildsView($plan); + + $timeline = $this->buildTransactionTimeline( + $plan, + new HarbormasterBuildPlanTransactionQuery()); + $timeline->setShouldTerminate(true); + $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) - ->setMainColumn(array( - $error, - $step_list, - $timeline, - )); + ->setMainColumn( + array( + $error, + $step_list, + $builds_view, + $timeline, + )); return $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) + ->setPageObjectPHIDs(array($plan->getPHID())) ->appendChild($view); } @@ -213,7 +224,7 @@ ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($step_list); - return array($step_box, $has_any_conflicts, $is_deadlocking); + return array($step_box, $has_any_conflicts, $is_deadlocking, $steps); } private function buildCurtainView(HarbormasterBuildPlan $plan) { @@ -376,7 +387,7 @@ array $steps) { $has_conflicts = false; - if (count($step_phids) === 0) { + if (!$step_phids) { return null; } @@ -436,4 +447,41 @@ return array($ui, $has_conflicts); } + + private function newBuildsView(HarbormasterBuildPlan $plan) { + $viewer = $this->getViewer(); + + $builds = id(new HarbormasterBuildQuery()) + ->setViewer($viewer) + ->withBuildPlanPHIDs(array($plan->getPHID())) + ->setLimit(10) + ->execute(); + + $list = id(new HarbormasterBuildView()) + ->setViewer($viewer) + ->setBuilds($builds) + ->newObjectList(); + + $list->setNoDataString(pht('No recent builds.')); + + $more_href = new PhutilURI( + $this->getApplicationURI('/build/'), + array('plan' => $plan->getPHID())); + + $more_link = id(new PHUIButtonView()) + ->setTag('a') + ->setIcon('fa-list-ul') + ->setText(pht('View All Builds')) + ->setHref($more_href); + + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Recent Builds')) + ->addActionLink($more_link); + + return id(new PHUIObjectBoxView()) + ->setHeader($header) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->appendChild($list); + } + } diff --git a/src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php b/src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php --- a/src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php +++ b/src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php @@ -128,49 +128,14 @@ $viewer = $this->requireViewer(); - $buildables = mpull($builds, 'getBuildable'); - $object_phids = mpull($buildables, 'getBuildablePHID'); - $initiator_phids = mpull($builds, 'getInitiatorPHID'); - $phids = array_mergev(array($initiator_phids, $object_phids)); - $phids = array_unique(array_filter($phids)); - - $handles = $viewer->loadHandles($phids); - - $list = new PHUIObjectItemListView(); - foreach ($builds as $build) { - $id = $build->getID(); - $initiator = $handles[$build->getInitiatorPHID()]; - $buildable_object = $handles[$build->getBuildable()->getBuildablePHID()]; - - $item = id(new PHUIObjectItemView()) - ->setViewer($viewer) - ->setObject($build) - ->setObjectName(pht('Build %d', $build->getID())) - ->setHeader($build->getName()) - ->setHref($build->getURI()) - ->setEpoch($build->getDateCreated()) - ->addAttribute($buildable_object->getName()); - - if ($initiator) { - $item->addHandleIcon($initiator, $initiator->getName()); - } - - $status = $build->getBuildStatus(); - - $status_icon = HarbormasterBuildStatus::getBuildStatusIcon($status); - $status_color = HarbormasterBuildStatus::getBuildStatusColor($status); - $status_label = HarbormasterBuildStatus::getBuildStatusName($status); - - $item->setStatusIcon("{$status_icon} {$status_color}", $status_label); - - $list->addItem($item); - } - - $result = new PhabricatorApplicationSearchResultView(); - $result->setObjectList($list); - $result->setNoDataString(pht('No builds found.')); - - return $result; + $list = id(new HarbormasterBuildView()) + ->setViewer($viewer) + ->setBuilds($builds) + ->newObjectList(); + + return id(new PhabricatorApplicationSearchResultView()) + ->setObjectList($list) + ->setNoDataString(pht('No builds found.')); } } diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuild.php b/src/applications/harbormaster/storage/build/HarbormasterBuild.php --- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php @@ -193,6 +193,10 @@ return HarbormasterBuildStatus::newBuildStatusObject($status_key); } + public function getObjectName() { + return pht('Build %d', $this->getID()); + } + /* -( Build Commands )----------------------------------------------------- */ diff --git a/src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php b/src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php --- a/src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php +++ b/src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php @@ -91,7 +91,7 @@ } public function getObjectName() { - return pht('Build Plan %d', $this->getID()); + return pht('Plan %d', $this->getID()); } diff --git a/src/applications/harbormaster/view/HarbormasterBuildView.php b/src/applications/harbormaster/view/HarbormasterBuildView.php new file mode 100644 --- /dev/null +++ b/src/applications/harbormaster/view/HarbormasterBuildView.php @@ -0,0 +1,67 @@ +builds = $builds; + return $this; + } + + public function getBuilds() { + return $this->builds; + } + + public function render() { + return $this->newObjectList(); + } + + public function newObjectList() { + $viewer = $this->getViewer(); + $builds = $this->getBuilds(); + + $buildables = mpull($builds, 'getBuildable'); + $object_phids = mpull($buildables, 'getBuildablePHID'); + $initiator_phids = mpull($builds, 'getInitiatorPHID'); + $phids = array_mergev(array($initiator_phids, $object_phids)); + $phids = array_unique(array_filter($phids)); + + $handles = $viewer->loadHandles($phids); + + $list = new PHUIObjectItemListView(); + foreach ($builds as $build) { + $id = $build->getID(); + $initiator = $handles[$build->getInitiatorPHID()]; + $buildable_object = $handles[$build->getBuildable()->getBuildablePHID()]; + + $item = id(new PHUIObjectItemView()) + ->setViewer($viewer) + ->setObject($build) + ->setObjectName($build->getObjectName()) + ->setHeader($build->getName()) + ->setHref($build->getURI()) + ->setEpoch($build->getDateCreated()) + ->addAttribute($buildable_object->getName()); + + if ($initiator) { + $item->addByline($initiator->renderLink()); + } + + $status = $build->getBuildStatus(); + + $status_icon = HarbormasterBuildStatus::getBuildStatusIcon($status); + $status_color = HarbormasterBuildStatus::getBuildStatusColor($status); + $status_label = HarbormasterBuildStatus::getBuildStatusName($status); + + $item->setStatusIcon("{$status_icon} {$status_color}", $status_label); + + $list->addItem($item); + } + + return $list; + } + +}