From 2c55a4ad723797e349090a869c5699e00f22b4d4 Mon Sep 17 00:00:00 2001 From: Mike Riley Date: Sun, 31 Jul 2016 13:35:31 +0000 Subject: [PATCH] Provide a basic search engine for builds Summary: This supports a few basic use cases that aren't served by the buildable search engine: - I'm trying to discover when the last time that this particular build plan failed was. - I want to know if any builds have deadlocked. - At a glance, I'm more interested in what build plans are running, not which buildables are being built. This is more often than not the case. Test Plan: {F1744003} Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin, epriestley Differential Revision: https://secure.phabricator.com/D16347 --- src/__phutil_library_map__.php | 6 + .../PhabricatorHarbormasterApplication.php | 1 + .../HarbormasterBuildListController.php | 15 +++ .../HarbormasterBuildableListController.php | 9 ++ .../query/HarbormasterBuildSearchEngine.php | 126 ++++++++++++++++++ .../storage/build/HarbormasterBuild.php | 38 +++--- .../HarbormasterBuildStatusDatasource.php | 45 +++++++ 7 files changed, 218 insertions(+), 22 deletions(-) create mode 100644 src/applications/harbormaster/controller/HarbormasterBuildListController.php create mode 100644 src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php create mode 100644 src/applications/harbormaster/typeahead/HarbormasterBuildStatusDatasource.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 1ac5528904..c59605602e 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1107,6 +1107,7 @@ phutil_register_library_map(array( 'HarbormasterBuildFailureException' => 'applications/harbormaster/exception/HarbormasterBuildFailureException.php', 'HarbormasterBuildGraph' => 'applications/harbormaster/engine/HarbormasterBuildGraph.php', 'HarbormasterBuildLintMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildLintMessage.php', + 'HarbormasterBuildListController' => 'applications/harbormaster/controller/HarbormasterBuildListController.php', 'HarbormasterBuildLog' => 'applications/harbormaster/storage/build/HarbormasterBuildLog.php', 'HarbormasterBuildLogChunk' => 'applications/harbormaster/storage/build/HarbormasterBuildLogChunk.php', 'HarbormasterBuildLogChunkIterator' => 'applications/harbormaster/storage/build/HarbormasterBuildLogChunkIterator.php', @@ -1129,6 +1130,8 @@ phutil_register_library_map(array( 'HarbormasterBuildPlanTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanTransactionQuery.php', 'HarbormasterBuildQuery' => 'applications/harbormaster/query/HarbormasterBuildQuery.php', 'HarbormasterBuildRequest' => 'applications/harbormaster/engine/HarbormasterBuildRequest.php', + 'HarbormasterBuildSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildSearchEngine.php', + 'HarbormasterBuildStatusDatasource' => 'applications/harbormaster/typeahead/HarbormasterBuildStatusDatasource.php', 'HarbormasterBuildStep' => 'applications/harbormaster/storage/configuration/HarbormasterBuildStep.php', 'HarbormasterBuildStepCoreCustomField' => 'applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php', 'HarbormasterBuildStepCustomField' => 'applications/harbormaster/customfield/HarbormasterBuildStepCustomField.php', @@ -5647,6 +5650,7 @@ phutil_register_library_map(array( 'HarbormasterBuildFailureException' => 'Exception', 'HarbormasterBuildGraph' => 'AbstractDirectedGraph', 'HarbormasterBuildLintMessage' => 'HarbormasterDAO', + 'HarbormasterBuildListController' => 'HarbormasterController', 'HarbormasterBuildLog' => array( 'HarbormasterDAO', 'PhabricatorPolicyInterface', @@ -5682,6 +5686,8 @@ phutil_register_library_map(array( 'HarbormasterBuildPlanTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'HarbormasterBuildQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'HarbormasterBuildRequest' => 'Phobject', + 'HarbormasterBuildSearchEngine' => 'PhabricatorApplicationSearchEngine', + 'HarbormasterBuildStatusDatasource' => 'PhabricatorTypeaheadDatasource', 'HarbormasterBuildStep' => array( 'HarbormasterDAO', 'PhabricatorApplicationTransactionInterface', diff --git a/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php b/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php index 00fb570e03..ed7104965a 100644 --- a/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php +++ b/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php @@ -70,6 +70,7 @@ final class PhabricatorHarbormasterApplication extends PhabricatorApplication { => 'HarbormasterBuildableActionController', ), 'build/' => array( + $this->getQueryRoutePattern() => 'HarbormasterBuildListController', '(?P\d+)/' => 'HarbormasterBuildViewController', '(?Ppause|resume|restart|abort)/'. '(?P\d+)/(?:(?P[^/]+)/)?' diff --git a/src/applications/harbormaster/controller/HarbormasterBuildListController.php b/src/applications/harbormaster/controller/HarbormasterBuildListController.php new file mode 100644 index 0000000000..0da9f57d6f --- /dev/null +++ b/src/applications/harbormaster/controller/HarbormasterBuildListController.php @@ -0,0 +1,15 @@ +setController($this) + ->buildResponse(); + } + +} diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableListController.php b/src/applications/harbormaster/controller/HarbormasterBuildableListController.php index 99595335be..1c08b1950f 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildableListController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildableListController.php @@ -9,6 +9,15 @@ final class HarbormasterBuildableListController extends HarbormasterController { public function handleRequest(AphrontRequest $request) { $items = array(); + $items[] = id(new PHUIListItemView()) + ->setType(PHUIListItemView::TYPE_LABEL) + ->setName(pht('Builds')); + + $items[] = id(new PHUIListItemView()) + ->setType(PHUIListItemView::TYPE_LINK) + ->setName(pht('Browse Builds')) + ->setHref($this->getApplicationURI('build/')); + $items[] = id(new PHUIListItemView()) ->setType(PHUIListItemView::TYPE_LABEL) ->setName(pht('Build Plans')); diff --git a/src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php b/src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php new file mode 100644 index 0000000000..7a15305b20 --- /dev/null +++ b/src/applications/harbormaster/query/HarbormasterBuildSearchEngine.php @@ -0,0 +1,126 @@ +setLabel(pht('Build Plans')) + ->setKey('plans') + ->setAliases(array('plan')) + ->setDescription( + pht('Search for builds running a given build plan.')) + ->setDatasource(new HarbormasterBuildPlanDatasource()), + id(new PhabricatorSearchDatasourceField()) + ->setLabel(pht('Statuses')) + ->setKey('statuses') + ->setAliases(array('status')) + ->setDescription( + pht('Search for builds with given statuses.')) + ->setDatasource(new HarbormasterBuildStatusDatasource()), + ); + } + + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + + if ($map['plans']) { + $query->withBuildPlanPHIDs($map['plans']); + } + + if ($map['statuses']) { + $query->withBuildStatuses($map['statuses']); + } + + return $query; + } + + protected function getURI($path) { + return '/harbormaster/build/'.$path; + } + + protected function getBuiltinQueryNames() { + return array( + 'all' => pht('All Builds'), + ); + } + + public function buildSavedQueryFromBuiltin($query_key) { + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'all': + return $query; + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + + protected function renderResultList( + array $builds, + PhabricatorSavedQuery $query, + array $handles) { + assert_instances_of($builds, 'HarbormasterBuild'); + + $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 = HarbormasterBuild::getBuildStatusIcon($status); + $status_color = HarbormasterBuild::getBuildStatusColor($status); + $status_label = HarbormasterBuild::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; + } + +} diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuild.php b/src/applications/harbormaster/storage/build/HarbormasterBuild.php index 4d9278c7bf..385b9133a8 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php @@ -71,28 +71,22 @@ final class HarbormasterBuild extends HarbormasterDAO * @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_ABORTED: - return pht('Aborted'); - case self::STATUS_ERROR: - return pht('Unexpected Error'); - case self::STATUS_PAUSED: - return pht('Paused'); - case self::STATUS_DEADLOCKED: - return pht('Deadlocked'); - default: - return pht('Unknown'); - } + $map = self::getBuildStatusMap(); + return idx($map, $status, pht('Unknown ("%s")', $status)); + } + + public static function getBuildStatusMap() { + return array( + self::STATUS_INACTIVE => pht('Inactive'), + self::STATUS_PENDING => pht('Pending'), + self::STATUS_BUILDING => pht('Building'), + self::STATUS_PASSED => pht('Passed'), + self::STATUS_FAILED => pht('Failed'), + self::STATUS_ABORTED => pht('Aborted'), + self::STATUS_ERROR => pht('Unexpected Error'), + self::STATUS_PAUSED => pht('Paused'), + self::STATUS_DEADLOCKED => pht('Deadlocked'), + ); } public static function getBuildStatusIcon($status) { diff --git a/src/applications/harbormaster/typeahead/HarbormasterBuildStatusDatasource.php b/src/applications/harbormaster/typeahead/HarbormasterBuildStatusDatasource.php new file mode 100644 index 0000000000..3e757b4348 --- /dev/null +++ b/src/applications/harbormaster/typeahead/HarbormasterBuildStatusDatasource.php @@ -0,0 +1,45 @@ +buildResults(); + return $this->filterResultsAgainstTokens($results); + } + + public function renderTokens(array $values) { + return $this->renderTokensFromResults($this->buildResults(), $values); + } + + private function buildResults() { + $results = array(); + + $status_map = HarbormasterBuild::getBuildStatusMap(); + foreach ($status_map as $value => $name) { + $result = id(new PhabricatorTypeaheadResult()) + ->setIcon(HarbormasterBuild::getBuildStatusIcon($value)) + ->setColor(HarbormasterBuild::getBuildStatusColor($value)) + ->setPHID($value) + ->setName($name) + ->addAttribute(pht('Status')); + + $results[$value] = $result; + } + + return $results; + } + +}