diff --git a/resources/celerity/map.php b/resources/celerity/map.php index b41d205d4f..e50b64255d 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -10,7 +10,7 @@ return array( 'core.pkg.css' => '7f0d6232', 'core.pkg.js' => 'a590b451', 'darkconsole.pkg.js' => 'e7393ebb', - 'differential.pkg.css' => 'fe951924', + 'differential.pkg.css' => '27498de3', 'differential.pkg.js' => 'ebef29b1', 'diffusion.pkg.css' => '385e85b3', 'diffusion.pkg.js' => '0115b37c', @@ -61,7 +61,6 @@ return array( 'rsrc/css/application/differential/changeset-view.css' => '9b8e8bb7', 'rsrc/css/application/differential/core.css' => '7ac3cabc', 'rsrc/css/application/differential/phui-inline-comment.css' => 'fa5b8d1f', - 'rsrc/css/application/differential/results-table.css' => '181aa9d9', 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', 'rsrc/css/application/differential/revision-history.css' => '0e8eb855', 'rsrc/css/application/differential/revision-list.css' => 'f3c47d33', @@ -357,7 +356,6 @@ return array( 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '037b59eb', 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '2c426492', 'rsrc/js/application/differential/behavior-populate.js' => '8694b1df', - 'rsrc/js/application/differential/behavior-show-field-details.js' => 'bba9eedf', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', 'rsrc/js/application/diffusion/DiffusionLocateFileSource.js' => 'b42eddc7', @@ -511,7 +509,6 @@ return array( 'differential-changeset-view-css' => '9b8e8bb7', 'differential-core-view-css' => '7ac3cabc', 'differential-inline-comment-editor' => 'd4c87bf4', - 'differential-results-table-css' => '181aa9d9', 'differential-revision-add-comment-css' => 'c47f8c40', 'differential-revision-comment-css' => '14b8565a', 'differential-revision-history-css' => '0e8eb855', @@ -565,7 +562,6 @@ return array( 'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-keyboard-navigation' => '2c426492', 'javelin-behavior-differential-populate' => '8694b1df', - 'javelin-behavior-differential-show-field-details' => 'bba9eedf', 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', 'javelin-behavior-differential-user-select' => 'a8d8459d', 'javelin-behavior-diffusion-commit-branches' => 'bdaf4d04', @@ -1711,11 +1707,6 @@ return array( 'javelin-workflow', 'phabricator-draggable-list', ), - 'bba9eedf' => array( - 'javelin-behavior', - 'javelin-stratcom', - 'javelin-dom', - ), 'bd4c8dca' => array( 'javelin-install', 'javelin-util', @@ -2192,7 +2183,6 @@ return array( 'differential.pkg.css' => array( 'differential-core-view-css', 'differential-changeset-view-css', - 'differential-results-table-css', 'differential-revision-history-css', 'differential-revision-list-css', 'differential-table-of-contents-css', diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php index bf1ea73c79..adc001d0ec 100644 --- a/resources/celerity/packages.php +++ b/resources/celerity/packages.php @@ -141,7 +141,6 @@ return array( 'differential.pkg.css' => array( 'differential-core-view-css', 'differential-changeset-view-css', - 'differential-results-table-css', 'differential-revision-history-css', 'differential-revision-list-css', 'differential-table-of-contents-css', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index f1a5d85bfa..fd5a95fc99 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -419,7 +419,6 @@ phutil_register_library_map(array( 'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php', 'DifferentialRepositoryLookup' => 'applications/differential/query/DifferentialRepositoryLookup.php', 'DifferentialRequiredSignaturesField' => 'applications/differential/customfield/DifferentialRequiredSignaturesField.php', - 'DifferentialResultsTableView' => 'applications/differential/view/DifferentialResultsTableView.php', 'DifferentialRevertPlanField' => 'applications/differential/customfield/DifferentialRevertPlanField.php', 'DifferentialReviewedByField' => 'applications/differential/customfield/DifferentialReviewedByField.php', 'DifferentialReviewer' => 'applications/differential/storage/DifferentialReviewer.php', @@ -894,6 +893,7 @@ phutil_register_library_map(array( 'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php', 'HarbormasterHTTPRequestBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php', 'HarbormasterLeaseHostBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php', + 'HarbormasterLintPropertyView' => 'applications/harbormaster/view/HarbormasterLintPropertyView.php', 'HarbormasterManagePlansCapability' => 'applications/harbormaster/capability/HarbormasterManagePlansCapability.php', 'HarbormasterManagementBuildWorkflow' => 'applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php', 'HarbormasterManagementUpdateWorkflow' => 'applications/harbormaster/management/HarbormasterManagementUpdateWorkflow.php', @@ -921,6 +921,7 @@ phutil_register_library_map(array( 'HarbormasterTargetWorker' => 'applications/harbormaster/worker/HarbormasterTargetWorker.php', 'HarbormasterThrowExceptionBuildStep' => 'applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php', 'HarbormasterUIEventListener' => 'applications/harbormaster/event/HarbormasterUIEventListener.php', + 'HarbormasterUnitPropertyView' => 'applications/harbormaster/view/HarbormasterUnitPropertyView.php', 'HarbormasterUploadArtifactBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php', 'HarbormasterWaitForPreviousBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php', 'HarbormasterWorker' => 'applications/harbormaster/worker/HarbormasterWorker.php', @@ -3789,7 +3790,6 @@ phutil_register_library_map(array( 'DifferentialRepositoryField' => 'DifferentialCoreCustomField', 'DifferentialRepositoryLookup' => 'Phobject', 'DifferentialRequiredSignaturesField' => 'DifferentialCoreCustomField', - 'DifferentialResultsTableView' => 'AphrontView', 'DifferentialRevertPlanField' => 'DifferentialStoredCustomField', 'DifferentialReviewedByField' => 'DifferentialCoreCustomField', 'DifferentialReviewer' => 'Phobject', @@ -4356,6 +4356,7 @@ phutil_register_library_map(array( 'HarbormasterDAO' => 'PhabricatorLiskDAO', 'HarbormasterHTTPRequestBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 'HarbormasterLeaseHostBuildStepImplementation' => 'HarbormasterBuildStepImplementation', + 'HarbormasterLintPropertyView' => 'AphrontView', 'HarbormasterManagePlansCapability' => 'PhabricatorPolicyCapability', 'HarbormasterManagementBuildWorkflow' => 'HarbormasterManagementWorkflow', 'HarbormasterManagementUpdateWorkflow' => 'HarbormasterManagementWorkflow', @@ -4383,6 +4384,7 @@ phutil_register_library_map(array( 'HarbormasterTargetWorker' => 'HarbormasterWorker', 'HarbormasterThrowExceptionBuildStep' => 'HarbormasterBuildStepImplementation', 'HarbormasterUIEventListener' => 'PhabricatorEventListener', + 'HarbormasterUnitPropertyView' => 'AphrontView', 'HarbormasterUploadArtifactBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 'HarbormasterWaitForPreviousBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 'HarbormasterWorker' => 'PhabricatorWorker', @@ -6730,6 +6732,7 @@ phutil_register_library_map(array( 'PhabricatorProjectInterface', 'PhabricatorDestructibleInterface', 'PhabricatorSpacesInterface', + 'PhabricatorMentionableInterface', ), 'PholioMockCommentController' => 'PholioController', 'PholioMockEditController' => 'PholioController', diff --git a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php index b557acb4f3..4028a6c25b 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php @@ -264,11 +264,11 @@ final class PhabricatorCalendarEventSearchEngine $list = new PHUIObjectItemListView(); foreach ($events as $event) { - $from = phabricator_datetime($event->getDateFrom(), $viewer); $duration = ''; + $event_date_info = $this->getEventDateLabel($event); $creator_handle = $handles[$event->getUserPHID()]; - $attendees = array(); + foreach ($event->getInvitees() as $invitee) { $attendees[] = $invitee->getInviteePHID(); } @@ -287,8 +287,8 @@ final class PhabricatorCalendarEventSearchEngine $item = id(new PHUIObjectItemView()) ->setHeader($viewer->renderHandle($event->getPHID())->render()) + ->addAttribute($event_date_info) ->addAttribute($attendees) - ->addIcon('none', $from) ->addIcon('none', $duration); $list->addItem($item); @@ -520,4 +520,41 @@ final class PhabricatorCalendarEventSearchEngine return false; } + + private function getEventDateLabel($event) { + $viewer = $this->requireViewer(); + + $from_datetime = PhabricatorTime::getDateTimeFromEpoch( + $event->getDateFrom(), + $viewer); + $to_datetime = PhabricatorTime::getDateTimeFromEpoch( + $event->getDateTo(), + $viewer); + + $from_date_formatted = $from_datetime->format('Y m d'); + $to_date_formatted = $to_datetime->format('Y m d'); + + if ($event->getIsAllDay()) { + if ($from_date_formatted == $to_date_formatted) { + return pht( + '%s, All Day', + phabricator_date($event->getDateFrom(), $viewer)); + } else { + return pht( + '%s - %s, All Day', + phabricator_date($event->getDateFrom(), $viewer), + phabricator_date($event->getDateTo(), $viewer)); + } + } else if ($from_date_formatted == $to_date_formatted) { + return pht( + '%s - %s', + phabricator_datetime($event->getDateFrom(), $viewer), + phabricator_time($event->getDateTo(), $viewer)); + } else { + return pht( + '%s - %s', + phabricator_datetime($event->getDateFrom(), $viewer), + phabricator_datetime($event->getDateTo(), $viewer)); + } + } } diff --git a/src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php b/src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php index 91b5ca8653..686f7cba83 100644 --- a/src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php +++ b/src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php @@ -160,7 +160,8 @@ final class DifferentialCreateDiffConduitAPIMethod return array( 'diffid' => $diff->getID(), - 'uri' => $uri, + 'phid' => $diff->getPHID(), + 'uri' => $uri, ); } diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index b8e040c50a..94d9db0716 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -265,8 +265,25 @@ final class DifferentialRevisionViewController extends DifferentialController { $revision_detail_box->setInfoView($revision_warnings); } + $detail_diffs = array_select_keys( + $diffs, + array($diff_vs, $target->getID())); + $detail_diffs = mpull($detail_diffs, null, 'getPHID'); + + $buildables = id(new HarbormasterBuildableQuery()) + ->setViewer($user) + ->withBuildablePHIDs(array_keys($detail_diffs)) + ->withManualBuildables(false) + ->needBuilds(true) + ->needTargets(true) + ->execute(); + $buildables = mpull($buildables, null, 'getBuildablePHID'); + foreach ($detail_diffs as $diff_phid => $detail_diff) { + $detail_diff->attachBuildable(idx($buildables, $diff_phid)); + } + $diff_detail_box = $this->buildDiffDetailView( - array_select_keys($diffs, array($diff_vs, $target->getID())), + $detail_diffs, $revision, $field_list); diff --git a/src/applications/differential/customfield/DifferentialLintField.php b/src/applications/differential/customfield/DifferentialLintField.php index 94df1b7e07..6681dd035d 100644 --- a/src/applications/differential/customfield/DifferentialLintField.php +++ b/src/applications/differential/customfield/DifferentialLintField.php @@ -38,7 +38,6 @@ final class DifferentialLintField $keys = array( 'arc:lint', 'arc:lint-excuse', - 'arc:lint-postponed', ); $properties = id(new DifferentialDiffProperty())->loadAllWhere( @@ -51,208 +50,72 @@ final class DifferentialLintField $diff->attachProperty($key, idx($properties, $key)); } - $path_changesets = mpull($diff->loadChangesets(), 'getID', 'getFilename'); + $status = $this->renderLintStatus($diff); - $lstar = DifferentialRevisionUpdateHistoryView::renderDiffLintStar($diff); - $lmsg = DifferentialRevisionUpdateHistoryView::getDiffLintMessage($diff); - $ldata = $diff->getProperty('arc:lint'); - $ltail = null; + $lint = array(); - $rows = array(); + $buildable = $diff->getBuildable(); + if ($buildable) { + $target_phids = array(); + foreach ($buildable->getBuilds() as $build) { + foreach ($build->getBuildTargets() as $target) { + $target_phids[] = $target->getPHID(); + } + } - $rows[] = array( - 'style' => 'star', - 'name' => $lstar, - 'value' => $lmsg, - 'show' => true, - ); - - $excuse = $diff->getProperty('arc:lint-excuse'); - if ($excuse) { - $rows[] = array( - 'style' => 'excuse', - 'name' => 'Excuse', - 'value' => phutil_escape_html_newlines($excuse), - 'show' => true, - ); + $lint = id(new HarbormasterBuildLintMessage())->loadAllWhere( + 'buildTargetPHID IN (%Ls) LIMIT 25', + $target_phids); } - $show_limit = 10; - $hidden = array(); + if (!$lint) { + // No Harbormaster messages, so look for legacy messages and make them + // look like modern messages. + $legacy_lint = $diff->getProperty('arc:lint'); + if ($legacy_lint) { + // Show the top 100 legacy lint messages. Previously, we showed some + // by default and let the user toggle the rest. With modern messages, + // we can send the user to the Harbormaster detail page. Just show + // "a lot" of messages in legacy cases to try to strike a balance + // between implementation simplicitly and compatibility. + $legacy_lint = array_slice($legacy_lint, 0, 100); - if ($ldata) { - $ldata = igroup($ldata, 'path'); - foreach ($ldata as $path => $messages) { - - $rows[] = array( - 'style' => 'section', - 'name' => $path, - 'show' => $show_limit, - ); - - foreach ($messages as $message) { - $path = idx($message, 'path'); - $line = idx($message, 'line'); - - $code = idx($message, 'code'); - $severity = idx($message, 'severity'); - - $name = idx($message, 'name'); - $description = idx($message, 'description'); - - $line_link = pht('line %d', intval($line)); - if (isset($path_changesets[$path])) { - $href = '#C'.$path_changesets[$path].'NL'.max(1, $line); - - // TODO: We are always showing the active diff - // if ($diff->getID() != $this->getDiff()->getID()) { - // $href = '/D'.$diff->getRevisionID().'?id='.$diff->getID().$href; - // } - - $line_link = phutil_tag( - 'a', - array( - 'href' => $href, - ), - $line_link); - } - - if ($show_limit) { - --$show_limit; - $show = true; - } else { - $show = false; - if (empty($hidden[$severity])) { - $hidden[$severity] = 0; - } - $hidden[$severity]++; - } - - $rows[] = array( - 'style' => $this->getSeverityStyle($severity), - 'name' => ucwords($severity), - 'value' => hsprintf( - '(%s) %s at %s', - $code, - $name, - $line_link), - 'show' => $show, - ); - - if (!empty($message['locations'])) { - $locations = array(); - foreach ($message['locations'] as $location) { - $other_line = idx($location, 'line'); - $locations[] = - idx($location, 'path', $path). - ($other_line ? ":{$other_line}" : ''); - } - $description .= "\n".pht( - 'Other locations: %s', - implode(', ', $locations)); - } - - if (strlen($description)) { - $rows[] = array( - 'style' => 'details', - 'value' => phutil_escape_html_newlines($description), - 'show' => false, - ); - if (empty($hidden['details'])) { - $hidden['details'] = 0; - } - $hidden['details']++; + $target = new HarbormasterBuildTarget(); + foreach ($legacy_lint as $message) { + try { + $modern = HarbormasterBuildLintMessage::newFromDictionary( + $target, + $this->getModernLintMessageDictionary($message)); + $lint[] = $modern; + } catch (Exception $ex) { + // Ignore any poorly formatted messages. } } } } - $postponed = $diff->getProperty('arc:lint-postponed'); - if ($postponed) { - foreach ($postponed as $linter) { - $rows[] = array( - 'style' => $this->getPostponedStyle(), - 'name' => 'Postponed', - 'value' => $linter, - 'show' => false, - ); - if (empty($hidden['postponed'])) { - $hidden['postponed'] = 0; - } - $hidden['postponed']++; + if ($lint) { + $path_map = mpull($diff->loadChangesets(), 'getID', 'getFilename'); + foreach ($path_map as $path => $id) { + $href = '#C'.$id.'NL'; + + // TODO: When the diff is not the right-hand-size diff, we should + // ideally adjust this URI to be absolute. + + $path_map[$path] = $href; } + + $view = id(new HarbormasterLintPropertyView()) + ->setPathURIMap($path_map) + ->setLintMessages($lint); + } else { + $view = null; } - $show_string = $this->renderShowString($hidden); - - $view = new DifferentialResultsTableView(); - $view->setRows($rows); - $view->setShowMoreString($show_string); - - return $view->render(); - } - - private function getSeverityStyle($severity) { - $map = array( - ArcanistLintSeverity::SEVERITY_ERROR => 'red', - ArcanistLintSeverity::SEVERITY_WARNING => 'yellow', - ArcanistLintSeverity::SEVERITY_AUTOFIX => 'yellow', - ArcanistLintSeverity::SEVERITY_ADVICE => 'yellow', + return array( + $status, + $view, ); - return idx($map, $severity); - } - - private function getPostponedStyle() { - return 'blue'; - } - - private function renderShowString(array $hidden) { - if (!$hidden) { - return null; - } - - // Reorder hidden things by severity. - $hidden = array_select_keys( - $hidden, - array( - ArcanistLintSeverity::SEVERITY_ERROR, - ArcanistLintSeverity::SEVERITY_WARNING, - ArcanistLintSeverity::SEVERITY_AUTOFIX, - ArcanistLintSeverity::SEVERITY_ADVICE, - 'details', - 'postponed', - )) + $hidden; - - $show = array(); - foreach ($hidden as $key => $value) { - switch ($key) { - case ArcanistLintSeverity::SEVERITY_ERROR: - $show[] = pht('%d Error(s)', $value); - break; - case ArcanistLintSeverity::SEVERITY_WARNING: - $show[] = pht('%d Warning(s)', $value); - break; - case ArcanistLintSeverity::SEVERITY_AUTOFIX: - $show[] = pht('%d Auto-Fix(es)', $value); - break; - case ArcanistLintSeverity::SEVERITY_ADVICE: - $show[] = pht('%d Advice(s)', $value); - break; - case 'details': - $show[] = pht('%d Detail(s)', $value); - break; - case 'postponed': - $show[] = pht('%d Postponed', $value); - break; - default: - $show[] = $value; - break; - } - } - - return pht( - 'Show Full Lint Results (%s)', - implode(', ', $show)); } public function getWarningsForDetailView() { @@ -278,4 +141,50 @@ final class DifferentialLintField return $warnings; } + private function renderLintStatus(DifferentialDiff $diff) { + $colors = array( + DifferentialLintStatus::LINT_NONE => 'grey', + DifferentialLintStatus::LINT_OKAY => 'green', + DifferentialLintStatus::LINT_WARN => 'yellow', + DifferentialLintStatus::LINT_FAIL => 'red', + DifferentialLintStatus::LINT_SKIP => 'blue', + DifferentialLintStatus::LINT_AUTO_SKIP => 'blue', + DifferentialLintStatus::LINT_POSTPONED => 'blue', + ); + $icon_color = idx($colors, $diff->getLintStatus(), 'grey'); + + $message = DifferentialRevisionUpdateHistoryView::getDiffLintMessage($diff); + + $excuse = $diff->getProperty('arc:lint-excuse'); + if (strlen($excuse)) { + $excuse = array( + phutil_tag('strong', array(), pht('Excuse:')), + ' ', + phutil_escape_html_newlines($excuse), + ); + } + + $status = id(new PHUIStatusListView()) + ->addItem( + id(new PHUIStatusItemView()) + ->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color) + ->setTarget($message) + ->setNote($excuse)); + + return $status; + } + + private function getModernLintMessageDictionary(array $map) { + // Strip out `null` values to satisfy stricter typechecks. + foreach ($map as $key => $value) { + if ($value === null) { + unset($map[$key]); + } + } + + // TODO: We might need to remap some stuff here? + return $map; + } + + } diff --git a/src/applications/differential/customfield/DifferentialUnitField.php b/src/applications/differential/customfield/DifferentialUnitField.php index 56ea06d3ae..36798b7eb3 100644 --- a/src/applications/differential/customfield/DifferentialUnitField.php +++ b/src/applications/differential/customfield/DifferentialUnitField.php @@ -48,182 +48,67 @@ final class DifferentialUnitField $diff->attachProperty($key, idx($properties, $key)); } - $ustar = DifferentialRevisionUpdateHistoryView::renderDiffUnitStar($diff); - $umsg = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff); + $status = $this->renderUnitStatus($diff); - $rows = array(); + $unit = array(); - $rows[] = array( - 'style' => 'star', - 'name' => $ustar, - 'value' => $umsg, - 'show' => true, - ); + $buildable = $diff->getBuildable(); + if ($buildable) { + $target_phids = array(); + foreach ($buildable->getBuilds() as $build) { + foreach ($build->getBuildTargets() as $target) { + $target_phids[] = $target->getPHID(); + } + } - $excuse = $diff->getProperty('arc:unit-excuse'); - if ($excuse) { - $rows[] = array( - 'style' => 'excuse', - 'name' => pht('Excuse'), - 'value' => phutil_escape_html_newlines($excuse), - 'show' => true, - ); + $unit = id(new HarbormasterBuildUnitMessage())->loadAllWhere( + 'buildTargetPHID IN (%Ls) LIMIT 25', + $target_phids); } - $show_limit = 10; - $hidden = array(); + if (!$unit) { + $legacy_unit = $diff->getProperty('arc:unit'); + if ($legacy_unit) { + // Show the top 100 legacy unit messages. + $legacy_unit = array_slice($legacy_unit, 0, 100); - $udata = $diff->getProperty('arc:unit'); - if ($udata) { - $sort_map = array( - ArcanistUnitTestResult::RESULT_BROKEN => 0, - ArcanistUnitTestResult::RESULT_FAIL => 1, - ArcanistUnitTestResult::RESULT_UNSOUND => 2, - ArcanistUnitTestResult::RESULT_SKIP => 3, - ArcanistUnitTestResult::RESULT_POSTPONED => 4, - ArcanistUnitTestResult::RESULT_PASS => 5, - ); - - foreach ($udata as $key => $test) { - $udata[$key]['sort'] = idx($sort_map, idx($test, 'result')); - } - $udata = isort($udata, 'sort'); - $engine = new PhabricatorMarkupEngine(); - $engine->setViewer($this->getViewer()); - $markup_objects = array(); - foreach ($udata as $key => $test) { - $userdata = idx($test, 'userdata'); - if ($userdata) { - if ($userdata !== false) { - $userdata = str_replace("\000", '', $userdata); + $target = new HarbormasterBuildTarget(); + foreach ($legacy_unit as $message) { + try { + $modern = HarbormasterBuildUnitMessage::newFromDictionary( + $target, + $this->getModernUnitMessageDictionary($message)); + $unit[] = $modern; + } catch (Exception $ex) { + // Just ignore it if legacy messages aren't formatted like + // we expect. } - $markup_object = id(new PhabricatorMarkupOneOff()) - ->setContent($userdata) - ->setPreserveLinebreaks(true); - $engine->addObject($markup_object, 'default'); - $markup_objects[$key] = $markup_object; - } - } - $engine->process(); - foreach ($udata as $key => $test) { - $result = idx($test, 'result'); - - $default_hide = false; - switch ($result) { - case ArcanistUnitTestResult::RESULT_POSTPONED: - case ArcanistUnitTestResult::RESULT_PASS: - $default_hide = true; - break; - } - - if ($show_limit && !$default_hide) { - --$show_limit; - $show = true; - } else { - $show = false; - if (empty($hidden[$result])) { - $hidden[$result] = 0; - } - $hidden[$result]++; - } - - $value = idx($test, 'name'); - - $namespace = idx($test, 'namespace'); - if ($namespace) { - $value = $namespace.'::'.$value; - } - - if (!empty($test['link'])) { - $value = phutil_tag( - 'a', - array( - 'href' => $test['link'], - 'target' => '_blank', - ), - $value); - } - $rows[] = array( - 'style' => $this->getResultStyle($result), - 'name' => ucwords($result), - 'value' => $value, - 'show' => $show, - ); - - if (isset($markup_objects[$key])) { - $rows[] = array( - 'style' => 'details', - 'value' => $engine->getOutput($markup_objects[$key], 'default'), - 'show' => false, - ); - if (empty($hidden['details'])) { - $hidden['details'] = 0; - } - $hidden['details']++; } } } - $show_string = $this->renderShowString($hidden); + if ($unit) { + $path_map = mpull($diff->loadChangesets(), 'getID', 'getFilename'); + foreach ($path_map as $path => $id) { + $href = '#C'.$id.'NL'; - $view = new DifferentialResultsTableView(); - $view->setRows($rows); - $view->setShowMoreString($show_string); + // TODO: When the diff is not the right-hand-size diff, we should + // ideally adjust this URI to be absolute. - return $view->render(); - } + $path_map[$path] = $href; + } - private function getResultStyle($result) { - $map = array( - ArcanistUnitTestResult::RESULT_PASS => 'green', - ArcanistUnitTestResult::RESULT_FAIL => 'red', - ArcanistUnitTestResult::RESULT_SKIP => 'blue', - ArcanistUnitTestResult::RESULT_BROKEN => 'red', - ArcanistUnitTestResult::RESULT_UNSOUND => 'yellow', - ArcanistUnitTestResult::RESULT_POSTPONED => 'blue', + $view = id(new HarbormasterUnitPropertyView()) + ->setPathURIMap($path_map) + ->setUnitMessages($unit); + } else { + $view = null; + } + + return array( + $status, + $view, ); - return idx($map, $result); - } - - private function renderShowString(array $hidden) { - if (!$hidden) { - return null; - } - - // Reorder hidden things by severity. - $hidden = array_select_keys( - $hidden, - array( - ArcanistUnitTestResult::RESULT_BROKEN, - ArcanistUnitTestResult::RESULT_FAIL, - ArcanistUnitTestResult::RESULT_UNSOUND, - ArcanistUnitTestResult::RESULT_SKIP, - ArcanistUnitTestResult::RESULT_POSTPONED, - ArcanistUnitTestResult::RESULT_PASS, - 'details', - )) + $hidden; - - $noun = 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'), - ); - - $show = array(); - foreach ($hidden as $key => $value) { - if ($key == 'details') { - $show[] = pht('%d Detail(s)', $value); - } else { - $show[] = $value.' '.idx($noun, $key); - } - } - - return pht( - 'Show Full Unit Results (%s)', - implode(', ', $show)); } public function getWarningsForDetailView() { @@ -248,4 +133,51 @@ final class DifferentialUnitField } + private function renderUnitStatus(DifferentialDiff $diff) { + $colors = array( + DifferentialUnitStatus::UNIT_NONE => 'grey', + DifferentialUnitStatus::UNIT_OKAY => 'green', + DifferentialUnitStatus::UNIT_WARN => 'yellow', + DifferentialUnitStatus::UNIT_FAIL => 'red', + DifferentialUnitStatus::UNIT_SKIP => 'blue', + DifferentialUnitStatus::UNIT_AUTO_SKIP => 'blue', + DifferentialUnitStatus::UNIT_POSTPONED => 'blue', + ); + $icon_color = idx($colors, $diff->getUnitStatus(), 'grey'); + + $message = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff); + + $excuse = $diff->getProperty('arc:unit-excuse'); + if (strlen($excuse)) { + $excuse = array( + phutil_tag('strong', array(), pht('Excuse:')), + ' ', + phutil_escape_html_newlines($excuse), + ); + } + + $status = id(new PHUIStatusListView()) + ->addItem( + id(new PHUIStatusItemView()) + ->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color) + ->setTarget($message) + ->setNote($excuse)); + + return $status; + } + + private function getModernUnitMessageDictionary(array $map) { + // Strip out `null` values to satisfy stricter typechecks. + foreach ($map as $key => $value) { + if ($value === null) { + unset($map[$key]); + } + } + + // TODO: Remap more stuff here? + + return $map; + } + + } diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php index b9405faff9..fce8e423b4 100644 --- a/src/applications/differential/editor/DifferentialTransactionEditor.php +++ b/src/applications/differential/editor/DifferentialTransactionEditor.php @@ -587,6 +587,21 @@ final class DifferentialTransactionEditor $diff->setRevisionID($object->getID()); $diff->save(); + + // Update Harbormaster to set the containerPHID correctly for any + // existing buildables. We may otherwise have buildables stuck with + // the old (`null`) container. + + // TODO: This is a bit iffy, maybe we can find a cleaner approach? + $table = new HarbormasterBuildable(); + $conn_w = $table->establishConnection('w'); + queryfx( + $conn_w, + 'UPDATE %T SET containerPHID = %s WHERE buildablePHID = %s', + $table->getTableName(), + $object->getPHID(), + $diff->getPHID()); + return; } diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php index 869d0538c8..26379f9f33 100644 --- a/src/applications/differential/storage/DifferentialDiff.php +++ b/src/applications/differential/storage/DifferentialDiff.php @@ -39,6 +39,7 @@ final class DifferentialDiff private $changesets = self::ATTACHABLE; private $revision = self::ATTACHABLE; private $properties = array(); + private $buildable = self::ATTACHABLE; protected function getConfiguration() { return array( @@ -323,6 +324,15 @@ final class DifferentialDiff return $this->assertAttachedKey($this->properties, $key); } + public function attachBuildable(HarbormasterBuildable $buildable = null) { + $this->buildable = $buildable; + return $this; + } + + public function getBuildable() { + return $this->assertAttached($this->buildable); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/differential/view/DifferentialResultsTableView.php b/src/applications/differential/view/DifferentialResultsTableView.php deleted file mode 100644 index f05b24216b..0000000000 --- a/src/applications/differential/view/DifferentialResultsTableView.php +++ /dev/null @@ -1,115 +0,0 @@ -rows = $rows; - return $this; - } - - public function setShowMoreString($show_more_string) { - $this->showMoreString = $show_more_string; - return $this; - } - - public function render() { - - $rows = array(); - - $any_hidden = false; - foreach ($this->rows as $row) { - - $style = idx($row, 'style'); - switch ($style) { - case 'section': - $cells = phutil_tag( - 'th', - array( - 'colspan' => 2, - ), - idx($row, 'name')); - break; - default: - $name = phutil_tag( - 'th', - array( - ), - idx($row, 'name')); - $value = phutil_tag( - 'td', - array( - ), - idx($row, 'value')); - $cells = array($name, $value); - break; - } - - $show = idx($row, 'show'); - - $rows[] = javelin_tag( - 'tr', - array( - 'style' => $show ? null : 'display: none', - 'sigil' => $show ? null : 'differential-results-row-toggle', - 'class' => 'differential-results-row-'.$style, - ), - $cells); - - if (!$show) { - $any_hidden = true; - } - } - - if ($any_hidden) { - $show_more = javelin_tag( - 'a', - array( - 'href' => '#', - 'mustcapture' => true, - ), - $this->showMoreString); - - $hide_more = javelin_tag( - 'a', - array( - 'href' => '#', - 'mustcapture' => true, - ), - pht('Hide')); - - $rows[] = javelin_tag( - 'tr', - array( - 'class' => 'differential-results-row-show', - 'sigil' => 'differential-results-row-show', - ), - phutil_tag('th', array('colspan' => 2), $show_more)); - - $rows[] = javelin_tag( - 'tr', - array( - 'class' => 'differential-results-row-show', - 'sigil' => 'differential-results-row-hide', - 'style' => 'display: none', - ), - phutil_tag('th', array('colspan' => 2), $hide_more)); - - $this->initBehavior('differential-show-field-details'); - } - - $this->requireResource('differential-results-table-css'); - - return javelin_tag( - 'table', - array( - 'class' => 'differential-results-table', - 'sigil' => 'differential-results-table', - ), - $rows); - } - - -} diff --git a/src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php b/src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php index f6752f08fb..9590f90cec 100644 --- a/src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php +++ b/src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php @@ -18,7 +18,9 @@ final class HarbormasterSendMessageConduitAPIMethod return array( 'buildTargetPHID' => 'required phid', - 'type' => 'required '.$type_const, + 'lint' => 'optional list', + 'unit' => 'optional list', + 'type' => 'required '.$type_const, ); } @@ -40,10 +42,31 @@ final class HarbormasterSendMessageConduitAPIMethod throw new Exception(pht('No such build target!')); } - $message = HarbormasterBuildMessage::initializeNewMessage($viewer) + $save = array(); + + $lint_messages = $request->getValue('lint', array()); + foreach ($lint_messages as $lint) { + $save[] = HarbormasterBuildLintMessage::newFromDictionary( + $build_target, + $lint); + } + + $unit_messages = $request->getValue('unit', array()); + foreach ($unit_messages as $unit) { + $save[] = HarbormasterBuildUnitMessage::newFromDictionary( + $build_target, + $unit); + } + + $save[] = HarbormasterBuildMessage::initializeNewMessage($viewer) ->setBuildTargetPHID($build_target->getPHID()) - ->setType($message_type) - ->save(); + ->setType($message_type); + + $build_target->openTransaction(); + foreach ($save as $object) { + $object->save(); + } + $build_target->saveTransaction(); // If the build has completely paused because all steps are blocked on // waiting targets, this will resume it. diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php index 4af1f34276..2b5d2c7222 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php @@ -48,9 +48,7 @@ final class HarbormasterBuildViewController $this->buildPropertyLists($box, $build, $actions); $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb( - $build->getBuildable()->getMonogram(), - '/'.$build->getBuildable()->getMonogram()); + $this->addBuildableCrumb($crumbs, $build->getBuildable()); $crumbs->addTextCrumb($title); if ($generation === null || $generation > $build->getBuildGeneration() || @@ -99,29 +97,52 @@ final class HarbormasterBuildViewController $item->setIcon($icon, $color); $status_view->addItem($item); - $properties->addProperty(pht('Name'), $build_target->getName()); + $when = array(); + $started = $build_target->getDateStarted(); + $now = PhabricatorTime::getNow(); + if ($started) { + $ended = $build_target->getDateCompleted(); + if ($ended) { + $when[] = pht( + 'Completed at %s', + phabricator_datetime($started, $viewer)); - if ($build_target->getDateStarted() !== null) { - $properties->addProperty( - pht('Started'), - phabricator_datetime($build_target->getDateStarted(), $viewer)); - if ($build_target->isComplete()) { - $properties->addProperty( - pht('Completed'), - phabricator_datetime($build_target->getDateCompleted(), $viewer)); - $properties->addProperty( - pht('Duration'), - phutil_format_relative_time_detailed( - $build_target->getDateCompleted() - - $build_target->getDateStarted())); + $duration = ($ended - $started); + if ($duration) { + $when[] = pht( + 'Built for %s', + phutil_format_relative_time_detailed($duration)); + } else { + $when[] = pht('Built instantly'); + } } else { - $properties->addProperty( - pht('Elapsed'), - phutil_format_relative_time_detailed( - time() - $build_target->getDateStarted())); + $when[] = pht( + 'Started at %s', + phabricator_datetime($started, $viewer)); + $duration = ($now - $started); + if ($duration) { + $when[] = pht( + 'Running for %s', + phutil_format_relative_time_detailed($duration)); + } + } + } else { + $created = $build_target->getDateCreated(); + $when[] = pht( + 'Queued at %s', + phabricator_datetime($started, $viewer)); + $duration = ($now - $created); + if ($duration) { + $when[] = pht( + 'Waiting for %s', + phutil_format_relative_time_detailed($duration)); } } + $properties->addProperty( + pht('When'), + phutil_implode_html(" \xC2\xB7 ", $when)); + $properties->addProperty(pht('Status'), $status_view); $target_box->addPropertyList($properties, pht('Overview')); @@ -162,9 +183,7 @@ final class HarbormasterBuildViewController $variables = $build_target->getVariables(); if ($variables) { $properties = new PHUIPropertyListView(); - foreach ($variables as $key => $value) { - $properties->addProperty($key, $value); - } + $properties->addRawContent($this->buildProperties($variables)); $target_box->addPropertyList($properties, pht('Variables')); } @@ -183,7 +202,12 @@ final class HarbormasterBuildViewController } $properties = new PHUIPropertyListView(); - $properties->addProperty(pht('Build Target ID'), $build_target->getID()); + $properties->addProperty( + pht('Build Target ID'), + $build_target->getID()); + $properties->addProperty( + pht('Build Target PHID'), + $build_target->getPHID()); $target_box->addPropertyList($properties, pht('Metadata')); $targets[] = $target_box; @@ -528,4 +552,30 @@ final class HarbormasterBuildViewController return $table; } + private function buildProperties(array $properties) { + ksort($properties); + + $rows = array(); + foreach ($properties as $key => $value) { + $rows[] = array( + $key, + $value, + ); + } + + $table = id(new AphrontTableView($rows)) + ->setHeaders( + array( + pht('Key'), + pht('Value'), + )) + ->setColumnClasses( + array( + 'pri right', + 'wide', + )); + + return $table; + } + } diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php index c3a5817994..d5cd47d437 100644 --- a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php +++ b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php @@ -3,21 +3,12 @@ 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; + public function handleRequest(AphrontRequest $request) { + $viewer = $this->getViewer(); $buildable = id(new HarbormasterBuildableQuery()) ->setViewer($viewer) - ->withIDs(array($id)) + ->withIDs(array($request->getURIData('id'))) ->needBuildableHandles(true) ->needContainerHandles(true) ->executeOne(); @@ -25,6 +16,8 @@ final class HarbormasterBuildableViewController return new Aphront404Response(); } + $id = $buildable->getID(); + // Pull builds and build targets. $builds = id(new HarbormasterBuildQuery()) ->setViewer($viewer) @@ -32,7 +25,10 @@ final class HarbormasterBuildableViewController ->needBuildTargets(true) ->execute(); + list($lint, $unit) = $this->renderLintAndUnit($builds); + $buildable->attachBuilds($builds); + $object = $buildable->getBuildableObject(); $build_list = $this->buildBuildList($buildable); @@ -55,12 +51,14 @@ final class HarbormasterBuildableViewController $this->buildPropertyLists($box, $buildable, $actions); $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb("B{$id}"); + $crumbs->addTextCrumb($buildable->getMonogram()); return $this->buildApplicationPage( array( $crumbs, $box, + $lint, + $unit, $build_list, $timeline, ), @@ -144,16 +142,16 @@ final class HarbormasterBuildableViewController ->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('Buildable'), + $buildable->getBuildableHandle()->renderLink()); + $properties->addProperty( pht('Origin'), $buildable->getIsManualBuildable() @@ -250,7 +248,66 @@ final class HarbormasterBuildableViewController $build_list->addItem($item); } - return $build_list; + $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/controller/HarbormasterController.php b/src/applications/harbormaster/controller/HarbormasterController.php index c946cf7002..dea76b36d3 100644 --- a/src/applications/harbormaster/controller/HarbormasterController.php +++ b/src/applications/harbormaster/controller/HarbormasterController.php @@ -2,4 +2,14 @@ abstract class HarbormasterController extends PhabricatorController { + protected function addBuildableCrumb( + PHUICrumbsView $crumbs, + HarbormasterBuildable $buildable) { + + $monogram = $buildable->getMonogram(); + $uri = '/'.$monogram; + + $crumbs->addTextCrumb($monogram, $uri); + } + } diff --git a/src/applications/harbormaster/engine/HarbormasterBuildEngine.php b/src/applications/harbormaster/engine/HarbormasterBuildEngine.php index 98e67635f3..71c14414e7 100644 --- a/src/applications/harbormaster/engine/HarbormasterBuildEngine.php +++ b/src/applications/harbormaster/engine/HarbormasterBuildEngine.php @@ -332,6 +332,11 @@ final class HarbormasterBuildEngine extends Phobject { $message->save(); $target->setTargetStatus($new_status); + + if ($target->isComplete()) { + $target->setDateCompleted(PhabricatorTime::getNow()); + } + $target->save(); } } diff --git a/src/applications/harbormaster/query/HarbormasterBuildableQuery.php b/src/applications/harbormaster/query/HarbormasterBuildableQuery.php index 1b1956a0c7..2986bb45b7 100644 --- a/src/applications/harbormaster/query/HarbormasterBuildableQuery.php +++ b/src/applications/harbormaster/query/HarbormasterBuildableQuery.php @@ -13,6 +13,7 @@ final class HarbormasterBuildableQuery private $needContainerHandles; private $needBuildableHandles; private $needBuilds; + private $needTargets; public function withIDs(array $ids) { $this->ids = $ids; @@ -59,19 +60,17 @@ final class HarbormasterBuildableQuery return $this; } + public function needTargets($need) { + $this->needTargets = $need; + return $this; + } + + public function newResultObject() { + return new HarbormasterBuildable(); + } + protected function loadPage() { - $table = new HarbormasterBuildable(); - $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); + return $this->loadStandardPage($this->newResultObject()); } protected function willFilterPage(array $page) { @@ -157,11 +156,12 @@ final class HarbormasterBuildableQuery } } - if ($this->needBuilds) { + if ($this->needBuilds || $this->needTargets) { $builds = id(new HarbormasterBuildQuery()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withBuildablePHIDs(mpull($page, 'getPHID')) + ->needBuildTargets($this->needTargets) ->execute(); $builds = mgroup($builds, 'getBuildablePHID'); foreach ($page as $key => $buildable) { @@ -172,47 +172,45 @@ final class HarbormasterBuildableQuery return $page; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->buildablePHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'buildablePHID IN (%Ls)', $this->buildablePHIDs); } if ($this->containerPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'containerPHID in (%Ls)', $this->containerPHIDs); } if ($this->manualBuildables !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'isManualBuildable = %d', (int)$this->manualBuildables); } - $where[] = $this->buildPagingClause($conn_r); - - return $this->formatWhereClause($where); + return $where; } public function getQueryApplicationClass() { diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php b/src/applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php index d21fc2f718..3ee67e7280 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php @@ -15,7 +15,7 @@ final class HarbormasterBuildUnitMessage public static function initializeNewUnitMessage( HarbormasterBuildTarget $build_target) { - return id(new HarbormasterBuildLintMessage()) + return id(new HarbormasterBuildUnitMessage()) ->setBuildTargetPHID($build_target->getPHID()); } diff --git a/src/applications/harbormaster/view/HarbormasterLintPropertyView.php b/src/applications/harbormaster/view/HarbormasterLintPropertyView.php new file mode 100644 index 0000000000..f2591daae5 --- /dev/null +++ b/src/applications/harbormaster/view/HarbormasterLintPropertyView.php @@ -0,0 +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 new file mode 100644 index 0000000000..8bff9744f7 --- /dev/null +++ b/src/applications/harbormaster/view/HarbormasterUnitPropertyView.php @@ -0,0 +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; + } + +} diff --git a/src/applications/harbormaster/worker/HarbormasterTargetWorker.php b/src/applications/harbormaster/worker/HarbormasterTargetWorker.php index d0d6d168ab..a13a9de163 100644 --- a/src/applications/harbormaster/worker/HarbormasterTargetWorker.php +++ b/src/applications/harbormaster/worker/HarbormasterTargetWorker.php @@ -59,7 +59,7 @@ final class HarbormasterTargetWorker extends HarbormasterWorker { $target->setTargetStatus($next_status); if ($target->isComplete()) { - $target->setDateCompleted(time()); + $target->setDateCompleted(PhabricatorTime::getNow()); } $target->save(); @@ -70,12 +70,12 @@ final class HarbormasterTargetWorker extends HarbormasterWorker { } catch (HarbormasterBuildFailureException $ex) { // A build step wants to fail explicitly. $target->setTargetStatus(HarbormasterBuildTarget::STATUS_FAILED); - $target->setDateCompleted(time()); + $target->setDateCompleted(PhabricatorTime::getNow()); $target->save(); } catch (HarbormasterBuildAbortedException $ex) { // A build step is aborting because the build has been restarted. $target->setTargetStatus(HarbormasterBuildTarget::STATUS_ABORTED); - $target->setDateCompleted(time()); + $target->setDateCompleted(PhabricatorTime::getNow()); $target->save(); } catch (Exception $ex) { phlog($ex); diff --git a/src/applications/pholio/storage/PholioMock.php b/src/applications/pholio/storage/PholioMock.php index 22c1015704..71d1857383 100644 --- a/src/applications/pholio/storage/PholioMock.php +++ b/src/applications/pholio/storage/PholioMock.php @@ -10,7 +10,8 @@ final class PholioMock extends PholioDAO PhabricatorApplicationTransactionInterface, PhabricatorProjectInterface, PhabricatorDestructibleInterface, - PhabricatorSpacesInterface { + PhabricatorSpacesInterface, + PhabricatorMentionableInterface { const MARKUP_FIELD_DESCRIPTION = 'markup:description'; diff --git a/src/view/phui/PHUIStatusItemView.php b/src/view/phui/PHUIStatusItemView.php index e82799fc27..f4cfaf4462 100644 --- a/src/view/phui/PHUIStatusItemView.php +++ b/src/view/phui/PHUIStatusItemView.php @@ -22,6 +22,7 @@ final class PHUIStatusItemView extends AphrontTagView { const ICON_MINUS = 'fa-minus-circle'; const ICON_OPEN = 'fa-circle-o'; const ICON_CLOCK = 'fa-clock-o'; + const ICON_STAR = 'fa-star'; /* render_textarea */ public function setIcon($icon, $color = null, $label = null) { diff --git a/webroot/rsrc/css/application/differential/results-table.css b/webroot/rsrc/css/application/differential/results-table.css deleted file mode 100644 index 80d8ad1756..0000000000 --- a/webroot/rsrc/css/application/differential/results-table.css +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @provides differential-results-table-css - */ - -table.differential-results-table { - border-collapse: separate; - width: 96%; - font-size: 11px; -} - -.differential-results-table th { - text-align: center; - white-space: nowrap; - vertical-align: middle; - padding: 2px 4px; - width: 50px; - border-right: 1px solid #fff; - background: #f7f7f7; -} - -.device .differential-results-table th { - white-space: normal; -} - -.differential-results-table td { - padding: 0 8px; - margin: 0; - vertical-align: middle; - background: #f7f7f7; -} - -.differential-results-table tr.differential-results-row-star th, -.differential-results-table tr.differential-results-row-star td { - background: {$greybackground}; -} - -.differential-results-table tr.differential-results-row-section th { - padding-top: 4px; - text-align: left; -} - -.differential-results-table tr.differential-results-row-excuse th { - background: #3399ff; -} - -.differential-results-table tr.differential-results-row-excuse td { - padding-top: 8px; - padding-right: 8px; - padding-bottom: 8px; -} - -.differential-results-table tr.differential-results-row-red th { - background: #ff4422; -} - -.differential-results-table tr.differential-results-row-yellow th { - background: #ffdd66; -} - -.differential-results-table tr.differential-results-row-green th { - background: #22dd44; -} - -.differential-results-table tr.differential-results-row-blue th { - background: #88bbff; -} - -.differential-results-table tr.differential-results-row-details td { - color: {$lightgreytext}; -} - -.differential-results-table tr.differential-results-row-show th { - border-top: 1px solid #fff; - border-right: none; - padding: 2px; - color: {$bluetext}; - background: {$greybackground}; -} diff --git a/webroot/rsrc/js/application/differential/behavior-show-field-details.js b/webroot/rsrc/js/application/differential/behavior-show-field-details.js deleted file mode 100644 index a50d322595..0000000000 --- a/webroot/rsrc/js/application/differential/behavior-show-field-details.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @provides javelin-behavior-differential-show-field-details - * @requires javelin-behavior - * javelin-stratcom - * javelin-dom - */ - -JX.behavior('differential-show-field-details', function() { - - JX.Stratcom.listen( - 'click', - ['differential-results-row-show', 'tag:a'], - function(e) { - toggle(e, true); - }); - - JX.Stratcom.listen( - 'click', - ['differential-results-row-hide', 'tag:a'], - function(e) { - toggle(e, false); - }); - - function toggle(e, show) { - e.kill(); - - var f = show ? JX.DOM.show : JX.DOM.hide; - var g = show ? JX.DOM.hide : JX.DOM.show; - - var table = e.getNode('differential-results-table'); - var rows = JX.DOM.scry(table, 'tr', 'differential-results-row-toggle'); - for (var ii = 0; ii < rows.length; ii++) { - f(rows[ii]); - } - - g(JX.DOM.find(table, 'tr', 'differential-results-row-show')); - f(JX.DOM.find(table, 'tr', 'differential-results-row-hide')); - } - -});