@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Render lint results as Harbormaster lint messages

Summary:
Ref T8095. Render lint results in a future-ready way.

This makes the renderer accept `HarbormasterBuildLintMessage` objects. If we have legacy data instead, it converts it into `HarbormasterBuildLintMessage` objects.

Design is a bit rough but will be cleaned up later after T7739.

This moves away from "postponed linters", which are obsolete after Harbormaster (and were only ever used by Facebook).

Test Plan: {F523429}

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T8095

Differential Revision: https://secure.phabricator.com/D13377

+157 -189
+2
src/__phutil_library_map__.php
··· 891 891 'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php', 892 892 'HarbormasterHTTPRequestBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php', 893 893 'HarbormasterLeaseHostBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php', 894 + 'HarbormasterLintPropertyView' => 'applications/harbormaster/view/HarbormasterLintPropertyView.php', 894 895 'HarbormasterManagePlansCapability' => 'applications/harbormaster/capability/HarbormasterManagePlansCapability.php', 895 896 'HarbormasterManagementBuildWorkflow' => 'applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php', 896 897 'HarbormasterManagementUpdateWorkflow' => 'applications/harbormaster/management/HarbormasterManagementUpdateWorkflow.php', ··· 4351 4352 'HarbormasterDAO' => 'PhabricatorLiskDAO', 4352 4353 'HarbormasterHTTPRequestBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 4353 4354 'HarbormasterLeaseHostBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 4355 + 'HarbormasterLintPropertyView' => 'AphrontView', 4354 4356 'HarbormasterManagePlansCapability' => 'PhabricatorPolicyCapability', 4355 4357 'HarbormasterManagementBuildWorkflow' => 'HarbormasterManagementWorkflow', 4356 4358 'HarbormasterManagementUpdateWorkflow' => 'HarbormasterManagementWorkflow',
+76 -189
src/applications/differential/customfield/DifferentialLintField.php
··· 38 38 $keys = array( 39 39 'arc:lint', 40 40 'arc:lint-excuse', 41 - 'arc:lint-postponed', 42 41 ); 43 42 44 43 $properties = id(new DifferentialDiffProperty())->loadAllWhere( ··· 51 50 $diff->attachProperty($key, idx($properties, $key)); 52 51 } 53 52 54 - $path_changesets = mpull($diff->loadChangesets(), 'getID', 'getFilename'); 53 + $status = $this->renderLintStatus($diff); 55 54 56 - $lstar = DifferentialRevisionUpdateHistoryView::renderDiffLintStar($diff); 57 - $lmsg = DifferentialRevisionUpdateHistoryView::getDiffLintMessage($diff); 58 - $ldata = $diff->getProperty('arc:lint'); 59 - $ltail = null; 55 + $lint = array(); 60 56 61 - $rows = array(); 57 + // TODO: Look for Harbormaster messages here. 62 58 63 - $rows[] = array( 64 - 'style' => 'star', 65 - 'name' => $lstar, 66 - 'value' => $lmsg, 67 - 'show' => true, 68 - ); 69 59 70 - $excuse = $diff->getProperty('arc:lint-excuse'); 71 - if ($excuse) { 72 - $rows[] = array( 73 - 'style' => 'excuse', 74 - 'name' => 'Excuse', 75 - 'value' => phutil_escape_html_newlines($excuse), 76 - 'show' => true, 77 - ); 78 - } 60 + if (!$lint) { 61 + // No Harbormaster messages, so look for legacy messages and make them 62 + // look like modern messages. 63 + $legacy_lint = $diff->getProperty('arc:lint'); 64 + if ($legacy_lint) { 65 + // Show the top 100 legacy lint messages. Previously, we showed some 66 + // by default and let the user toggle the rest. With modern messages, 67 + // we can send the user to the Harbormaster detail page. Just show 68 + // "a lot" of messages in legacy cases to try to strike a balance 69 + // between implementation simplicitly and compatibility. 70 + $legacy_lint = array_slice($legacy_lint, 0, 100); 79 71 80 - $show_limit = 10; 81 - $hidden = array(); 82 - 83 - if ($ldata) { 84 - $ldata = igroup($ldata, 'path'); 85 - foreach ($ldata as $path => $messages) { 86 - 87 - $rows[] = array( 88 - 'style' => 'section', 89 - 'name' => $path, 90 - 'show' => $show_limit, 91 - ); 92 - 93 - foreach ($messages as $message) { 94 - $path = idx($message, 'path'); 95 - $line = idx($message, 'line'); 96 - 97 - $code = idx($message, 'code'); 98 - $severity = idx($message, 'severity'); 99 - 100 - $name = idx($message, 'name'); 101 - $description = idx($message, 'description'); 102 - 103 - $line_link = pht('line %d', intval($line)); 104 - if (isset($path_changesets[$path])) { 105 - $href = '#C'.$path_changesets[$path].'NL'.max(1, $line); 106 - 107 - // TODO: We are always showing the active diff 108 - // if ($diff->getID() != $this->getDiff()->getID()) { 109 - // $href = '/D'.$diff->getRevisionID().'?id='.$diff->getID().$href; 110 - // } 111 - 112 - $line_link = phutil_tag( 113 - 'a', 114 - array( 115 - 'href' => $href, 116 - ), 117 - $line_link); 118 - } 119 - 120 - if ($show_limit) { 121 - --$show_limit; 122 - $show = true; 123 - } else { 124 - $show = false; 125 - if (empty($hidden[$severity])) { 126 - $hidden[$severity] = 0; 127 - } 128 - $hidden[$severity]++; 129 - } 130 - 131 - $rows[] = array( 132 - 'style' => $this->getSeverityStyle($severity), 133 - 'name' => ucwords($severity), 134 - 'value' => hsprintf( 135 - '(%s) %s at %s', 136 - $code, 137 - $name, 138 - $line_link), 139 - 'show' => $show, 140 - ); 141 - 142 - if (!empty($message['locations'])) { 143 - $locations = array(); 144 - foreach ($message['locations'] as $location) { 145 - $other_line = idx($location, 'line'); 146 - $locations[] = 147 - idx($location, 'path', $path). 148 - ($other_line ? ":{$other_line}" : ''); 149 - } 150 - $description .= "\n".pht( 151 - 'Other locations: %s', 152 - implode(', ', $locations)); 153 - } 154 - 155 - if (strlen($description)) { 156 - $rows[] = array( 157 - 'style' => 'details', 158 - 'value' => phutil_escape_html_newlines($description), 159 - 'show' => false, 160 - ); 161 - if (empty($hidden['details'])) { 162 - $hidden['details'] = 0; 163 - } 164 - $hidden['details']++; 165 - } 72 + $target = new HarbormasterBuildTarget(); 73 + foreach ($legacy_lint as $message) { 74 + $modern = HarbormasterBuildLintMessage::newFromDictionary( 75 + $target, 76 + $this->getModernLintMessageDictionary($message)); 77 + $lint[] = $modern; 166 78 } 167 79 } 168 80 } 169 81 170 - $postponed = $diff->getProperty('arc:lint-postponed'); 171 - if ($postponed) { 172 - foreach ($postponed as $linter) { 173 - $rows[] = array( 174 - 'style' => $this->getPostponedStyle(), 175 - 'name' => 'Postponed', 176 - 'value' => $linter, 177 - 'show' => false, 178 - ); 179 - if (empty($hidden['postponed'])) { 180 - $hidden['postponed'] = 0; 181 - } 182 - $hidden['postponed']++; 183 - } 184 - } 185 - 186 - $show_string = $this->renderShowString($hidden); 187 - 188 - $view = new DifferentialResultsTableView(); 189 - $view->setRows($rows); 190 - $view->setShowMoreString($show_string); 191 - 192 - return $view->render(); 193 - } 194 - 195 - private function getSeverityStyle($severity) { 196 - $map = array( 197 - ArcanistLintSeverity::SEVERITY_ERROR => 'red', 198 - ArcanistLintSeverity::SEVERITY_WARNING => 'yellow', 199 - ArcanistLintSeverity::SEVERITY_AUTOFIX => 'yellow', 200 - ArcanistLintSeverity::SEVERITY_ADVICE => 'yellow', 201 - ); 202 - return idx($map, $severity); 203 - } 82 + if ($lint) { 83 + $path_map = mpull($diff->loadChangesets(), 'getID', 'getFilename'); 84 + foreach ($path_map as $path => $id) { 85 + $href = '#C'.$id.'NL'; 204 86 205 - private function getPostponedStyle() { 206 - return 'blue'; 207 - } 87 + // TODO: When the diff is not the right-hand-size diff, we should 88 + // ideally adjust this URI to be absolute. 208 89 209 - private function renderShowString(array $hidden) { 210 - if (!$hidden) { 211 - return null; 212 - } 213 - 214 - // Reorder hidden things by severity. 215 - $hidden = array_select_keys( 216 - $hidden, 217 - array( 218 - ArcanistLintSeverity::SEVERITY_ERROR, 219 - ArcanistLintSeverity::SEVERITY_WARNING, 220 - ArcanistLintSeverity::SEVERITY_AUTOFIX, 221 - ArcanistLintSeverity::SEVERITY_ADVICE, 222 - 'details', 223 - 'postponed', 224 - )) + $hidden; 225 - 226 - $show = array(); 227 - foreach ($hidden as $key => $value) { 228 - switch ($key) { 229 - case ArcanistLintSeverity::SEVERITY_ERROR: 230 - $show[] = pht('%d Error(s)', $value); 231 - break; 232 - case ArcanistLintSeverity::SEVERITY_WARNING: 233 - $show[] = pht('%d Warning(s)', $value); 234 - break; 235 - case ArcanistLintSeverity::SEVERITY_AUTOFIX: 236 - $show[] = pht('%d Auto-Fix(es)', $value); 237 - break; 238 - case ArcanistLintSeverity::SEVERITY_ADVICE: 239 - $show[] = pht('%d Advice(s)', $value); 240 - break; 241 - case 'details': 242 - $show[] = pht('%d Detail(s)', $value); 243 - break; 244 - case 'postponed': 245 - $show[] = pht('%d Postponed', $value); 246 - break; 247 - default: 248 - $show[] = $value; 249 - break; 90 + $path_map[$path] = $href; 250 91 } 92 + 93 + $view = id(new HarbormasterLintPropertyView()) 94 + ->setPathURIMap($path_map) 95 + ->setLintMessages($lint); 96 + } else { 97 + $view = null; 251 98 } 252 99 253 - return pht( 254 - 'Show Full Lint Results (%s)', 255 - implode(', ', $show)); 100 + return array( 101 + $status, 102 + $view, 103 + ); 256 104 } 257 105 258 106 public function getWarningsForDetailView() { ··· 277 125 278 126 return $warnings; 279 127 } 128 + 129 + private function renderLintStatus(DifferentialDiff $diff) { 130 + $colors = array( 131 + DifferentialLintStatus::LINT_NONE => 'grey', 132 + DifferentialLintStatus::LINT_OKAY => 'green', 133 + DifferentialLintStatus::LINT_WARN => 'yellow', 134 + DifferentialLintStatus::LINT_FAIL => 'red', 135 + DifferentialLintStatus::LINT_SKIP => 'blue', 136 + DifferentialLintStatus::LINT_AUTO_SKIP => 'blue', 137 + DifferentialLintStatus::LINT_POSTPONED => 'blue', 138 + ); 139 + $icon_color = idx($colors, $diff->getLintStatus(), 'grey'); 140 + 141 + $message = DifferentialRevisionUpdateHistoryView::getDiffLintMessage($diff); 142 + 143 + $excuse = $diff->getProperty('arc:lint-excuse'); 144 + if (strlen($excuse)) { 145 + $excuse = array( 146 + phutil_tag('strong', array(), pht('Excuse:')), 147 + ' ', 148 + phutil_escape_html_newlines($excuse), 149 + ); 150 + } 151 + 152 + $status = id(new PHUIStatusListView()) 153 + ->addItem( 154 + id(new PHUIStatusItemView()) 155 + ->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color) 156 + ->setTarget($message) 157 + ->setNote($excuse)); 158 + 159 + return $status; 160 + } 161 + 162 + private function getModernLintMessageDictionary(array $map) { 163 + // TODO: We might need to remap some stuff here? 164 + return $map; 165 + } 166 + 280 167 281 168 }
+78
src/applications/harbormaster/view/HarbormasterLintPropertyView.php
··· 1 + <?php 2 + 3 + final class HarbormasterLintPropertyView extends AphrontView { 4 + 5 + private $pathURIMap; 6 + private $lintMessages = array(); 7 + 8 + public function setPathURIMap(array $map) { 9 + $this->pathURIMap = $map; 10 + return $this; 11 + } 12 + 13 + public function setLintMessages(array $messages) { 14 + assert_instances_of($messages, 'HarbormasterBuildLintMessage'); 15 + $this->lintMessages = $messages; 16 + return $this; 17 + } 18 + 19 + public function render() { 20 + $rows = array(); 21 + foreach ($this->lintMessages as $message) { 22 + $path = $message->getPath(); 23 + $line = $message->getLine(); 24 + 25 + $href = null; 26 + if (strlen(idx($this->pathURIMap, $path))) { 27 + $href = $this->pathURIMap[$path].max($line, 1); 28 + } 29 + 30 + $severity = $this->renderSeverity($message->getSeverity()); 31 + 32 + $location = $path.':'.$line; 33 + if (strlen($href)) { 34 + $location = phutil_tag( 35 + 'a', 36 + array( 37 + 'href' => $href, 38 + ), 39 + $location); 40 + } 41 + 42 + $rows[] = array( 43 + $location, 44 + $severity, 45 + $message->getCode(), 46 + $message->getName(), 47 + ); 48 + } 49 + 50 + $table = id(new AphrontTableView($rows)) 51 + ->setHeaders( 52 + array( 53 + pht('Location'), 54 + pht('Severity'), 55 + pht('Code'), 56 + pht('Message'), 57 + )) 58 + ->setColumnClasses( 59 + array( 60 + 'pri', 61 + null, 62 + null, 63 + 'wide', 64 + )); 65 + 66 + return $table; 67 + } 68 + 69 + private function renderSeverity($severity) { 70 + $names = ArcanistLintSeverity::getLintSeverities(); 71 + $name = idx($names, $severity, $severity); 72 + 73 + // TODO: Add some color here? 74 + 75 + return $name; 76 + } 77 + 78 + }
+1
src/view/phui/PHUIStatusItemView.php
··· 22 22 const ICON_MINUS = 'fa-minus-circle'; 23 23 const ICON_OPEN = 'fa-circle-o'; 24 24 const ICON_CLOCK = 'fa-clock-o'; 25 + const ICON_STAR = 'fa-star'; 25 26 26 27 /* render_textarea */ 27 28 public function setIcon($icon, $color = null, $label = null) {