[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class HarbormasterBuildViewController 4 extends HarbormasterController { 5 6 private $id; 7 8 public function willProcessRequest(array $data) { 9 $this->id = $data['id']; 10 } 11 12 public function processRequest() { 13 $request = $this->getRequest(); 14 $viewer = $request->getUser(); 15 16 $id = $this->id; 17 $generation = $request->getInt('g'); 18 19 $build = id(new HarbormasterBuildQuery()) 20 ->setViewer($viewer) 21 ->withIDs(array($id)) 22 ->executeOne(); 23 if (!$build) { 24 return new Aphront404Response(); 25 } 26 27 require_celerity_resource('harbormaster-css'); 28 29 $title = pht('Build %d', $id); 30 31 $header = id(new PHUIHeaderView()) 32 ->setHeader($title) 33 ->setUser($viewer) 34 ->setPolicyObject($build); 35 36 if ($build->isRestarting()) { 37 $header->setStatus('fa-exclamation-triangle', 'red', pht('Restarting')); 38 } else if ($build->isStopping()) { 39 $header->setStatus('fa-exclamation-triangle', 'red', pht('Pausing')); 40 } else if ($build->isResuming()) { 41 $header->setStatus('fa-exclamation-triangle', 'red', pht('Resuming')); 42 } 43 44 $box = id(new PHUIObjectBoxView()) 45 ->setHeader($header); 46 47 $actions = $this->buildActionList($build); 48 $this->buildPropertyLists($box, $build, $actions); 49 50 $crumbs = $this->buildApplicationCrumbs(); 51 $crumbs->addTextCrumb( 52 $build->getBuildable()->getMonogram(), 53 '/'.$build->getBuildable()->getMonogram()); 54 $crumbs->addTextCrumb($title); 55 56 if ($generation === null || $generation > $build->getBuildGeneration() || 57 $generation < 0) { 58 $generation = $build->getBuildGeneration(); 59 } 60 61 $build_targets = id(new HarbormasterBuildTargetQuery()) 62 ->setViewer($viewer) 63 ->needBuildSteps(true) 64 ->withBuildPHIDs(array($build->getPHID())) 65 ->withBuildGenerations(array($generation)) 66 ->execute(); 67 68 if ($build_targets) { 69 $messages = id(new HarbormasterBuildMessageQuery()) 70 ->setViewer($viewer) 71 ->withBuildTargetPHIDs(mpull($build_targets, 'getPHID')) 72 ->execute(); 73 $messages = mgroup($messages, 'getBuildTargetPHID'); 74 } else { 75 $messages = array(); 76 } 77 78 $targets = array(); 79 foreach ($build_targets as $build_target) { 80 $header = id(new PHUIHeaderView()) 81 ->setHeader($build_target->getName()) 82 ->setUser($viewer); 83 84 $target_box = id(new PHUIObjectBoxView()) 85 ->setHeader($header); 86 87 $properties = new PHUIPropertyListView(); 88 $status_view = new PHUIStatusListView(); 89 90 $item = new PHUIStatusItemView(); 91 92 $status = $build_target->getTargetStatus(); 93 $status_name = 94 HarbormasterBuildTarget::getBuildTargetStatusName($status); 95 $icon = HarbormasterBuildTarget::getBuildTargetStatusIcon($status); 96 $color = HarbormasterBuildTarget::getBuildTargetStatusColor($status); 97 98 $item->setTarget($status_name); 99 $item->setIcon($icon, $color); 100 $status_view->addItem($item); 101 102 $properties->addProperty(pht('Name'), $build_target->getName()); 103 104 if ($build_target->getDateStarted() !== null) { 105 $properties->addProperty( 106 pht('Started'), 107 phabricator_datetime($build_target->getDateStarted(), $viewer)); 108 if ($build_target->isComplete()) { 109 $properties->addProperty( 110 pht('Completed'), 111 phabricator_datetime($build_target->getDateCompleted(), $viewer)); 112 $properties->addProperty( 113 pht('Duration'), 114 phutil_format_relative_time_detailed( 115 $build_target->getDateCompleted() - 116 $build_target->getDateStarted())); 117 } else { 118 $properties->addProperty( 119 pht('Elapsed'), 120 phutil_format_relative_time_detailed( 121 time() - $build_target->getDateStarted())); 122 } 123 } 124 125 $properties->addProperty(pht('Status'), $status_view); 126 127 $target_box->addPropertyList($properties, pht('Overview')); 128 129 $step = $build_target->getBuildStep(); 130 131 if ($step) { 132 $description = $step->getDescription(); 133 if ($description) { 134 $rendered = PhabricatorMarkupEngine::renderOneObject( 135 id(new PhabricatorMarkupOneOff()) 136 ->setContent($description) 137 ->setPreserveLinebreaks(true), 138 'default', 139 $viewer); 140 141 $properties->addSectionHeader(pht('Description')); 142 $properties->addTextContent($rendered); 143 } 144 } else { 145 $target_box->setFormErrors( 146 array( 147 pht( 148 'This build step has since been deleted on the build plan. '. 149 'Some information may be omitted.'), 150 )); 151 } 152 153 $details = $build_target->getDetails(); 154 if ($details) { 155 $properties = new PHUIPropertyListView(); 156 foreach ($details as $key => $value) { 157 $properties->addProperty($key, $value); 158 } 159 $target_box->addPropertyList($properties, pht('Configuration')); 160 } 161 162 $variables = $build_target->getVariables(); 163 if ($variables) { 164 $properties = new PHUIPropertyListView(); 165 foreach ($variables as $key => $value) { 166 $properties->addProperty($key, $value); 167 } 168 $target_box->addPropertyList($properties, pht('Variables')); 169 } 170 171 $artifacts = $this->buildArtifacts($build_target); 172 if ($artifacts) { 173 $properties = new PHUIPropertyListView(); 174 $properties->addRawContent($artifacts); 175 $target_box->addPropertyList($properties, pht('Artifacts')); 176 } 177 178 $build_messages = idx($messages, $build_target->getPHID(), array()); 179 if ($build_messages) { 180 $properties = new PHUIPropertyListView(); 181 $properties->addRawContent($this->buildMessages($build_messages)); 182 $target_box->addPropertyList($properties, pht('Messages')); 183 } 184 185 $properties = new PHUIPropertyListView(); 186 $properties->addProperty('Build Target ID', $build_target->getID()); 187 $target_box->addPropertyList($properties, pht('Metadata')); 188 189 $targets[] = $target_box; 190 191 $targets[] = $this->buildLog($build, $build_target); 192 } 193 194 $xactions = id(new HarbormasterBuildTransactionQuery()) 195 ->setViewer($viewer) 196 ->withObjectPHIDs(array($build->getPHID())) 197 ->execute(); 198 $timeline = id(new PhabricatorApplicationTransactionView()) 199 ->setUser($viewer) 200 ->setObjectPHID($build->getPHID()) 201 ->setTransactions($xactions); 202 203 return $this->buildApplicationPage( 204 array( 205 $crumbs, 206 $box, 207 $targets, 208 $timeline, 209 ), 210 array( 211 'title' => $title, 212 )); 213 } 214 215 private function buildArtifacts( 216 HarbormasterBuildTarget $build_target) { 217 218 $request = $this->getRequest(); 219 $viewer = $request->getUser(); 220 221 $artifacts = id(new HarbormasterBuildArtifactQuery()) 222 ->setViewer($viewer) 223 ->withBuildTargetPHIDs(array($build_target->getPHID())) 224 ->execute(); 225 226 if (count($artifacts) === 0) { 227 return null; 228 } 229 230 $list = id(new PHUIObjectItemListView()) 231 ->setFlush(true); 232 233 foreach ($artifacts as $artifact) { 234 $item = $artifact->getObjectItemView($viewer); 235 if ($item !== null) { 236 $list->addItem($item); 237 } 238 } 239 240 return $list; 241 } 242 243 private function buildLog( 244 HarbormasterBuild $build, 245 HarbormasterBuildTarget $build_target) { 246 247 $request = $this->getRequest(); 248 $viewer = $request->getUser(); 249 $limit = $request->getInt('l', 25); 250 251 $logs = id(new HarbormasterBuildLogQuery()) 252 ->setViewer($viewer) 253 ->withBuildTargetPHIDs(array($build_target->getPHID())) 254 ->execute(); 255 256 $empty_logs = array(); 257 258 $log_boxes = array(); 259 foreach ($logs as $log) { 260 $start = 1; 261 $lines = preg_split("/\r\n|\r|\n/", $log->getLogText()); 262 if ($limit !== 0) { 263 $start = count($lines) - $limit; 264 if ($start >= 1) { 265 $lines = array_slice($lines, -$limit, $limit); 266 } else { 267 $start = 1; 268 } 269 } 270 271 $id = null; 272 $is_empty = false; 273 if (count($lines) === 1 && trim($lines[0]) === '') { 274 // Prevent Harbormaster from showing empty build logs. 275 $id = celerity_generate_unique_node_id(); 276 $empty_logs[] = $id; 277 $is_empty = true; 278 } 279 280 $log_view = new ShellLogView(); 281 $log_view->setLines($lines); 282 $log_view->setStart($start); 283 284 $header = id(new PHUIHeaderView()) 285 ->setHeader(pht( 286 'Build Log %d (%s - %s)', 287 $log->getID(), 288 $log->getLogSource(), 289 $log->getLogType())) 290 ->setSubheader($this->createLogHeader($build, $log)) 291 ->setUser($viewer); 292 293 $log_box = id(new PHUIObjectBoxView()) 294 ->setHeader($header) 295 ->setForm($log_view); 296 297 if ($is_empty) { 298 $log_box = phutil_tag( 299 'div', 300 array( 301 'style' => 'display: none', 302 'id' => $id, 303 ), 304 $log_box); 305 } 306 307 $log_boxes[] = $log_box; 308 } 309 310 if ($empty_logs) { 311 $hide_id = celerity_generate_unique_node_id(); 312 313 Javelin::initBehavior('phabricator-reveal-content'); 314 315 $expand = phutil_tag( 316 'div', 317 array( 318 'id' => $hide_id, 319 'class' => 'harbormaster-empty-logs-are-hidden mlr mlt mll', 320 ), 321 array( 322 pht( 323 '%s empty logs are hidden.', 324 new PhutilNumber(count($empty_logs))), 325 ' ', 326 javelin_tag( 327 'a', 328 array( 329 'href' => '#', 330 'sigil' => 'reveal-content', 331 'meta' => array( 332 'showIDs' => $empty_logs, 333 'hideIDs' => array($hide_id), 334 ), 335 ), 336 pht('Show all logs.')), 337 )); 338 339 array_unshift($log_boxes, $expand); 340 } 341 342 return $log_boxes; 343 } 344 345 private function createLogHeader($build, $log) { 346 $request = $this->getRequest(); 347 $limit = $request->getInt('l', 25); 348 349 $lines_25 = $this->getApplicationURI('/build/'.$build->getID().'/?l=25'); 350 $lines_50 = $this->getApplicationURI('/build/'.$build->getID().'/?l=50'); 351 $lines_100 = 352 $this->getApplicationURI('/build/'.$build->getID().'/?l=100'); 353 $lines_0 = $this->getApplicationURI('/build/'.$build->getID().'/?l=0'); 354 355 $link_25 = phutil_tag('a', array('href' => $lines_25), pht('25')); 356 $link_50 = phutil_tag('a', array('href' => $lines_50), pht('50')); 357 $link_100 = phutil_tag('a', array('href' => $lines_100), pht('100')); 358 $link_0 = phutil_tag('a', array('href' => $lines_0), pht('Unlimited')); 359 360 if ($limit === 25) { 361 $link_25 = phutil_tag('strong', array(), $link_25); 362 } else if ($limit === 50) { 363 $link_50 = phutil_tag('strong', array(), $link_50); 364 } else if ($limit === 100) { 365 $link_100 = phutil_tag('strong', array(), $link_100); 366 } else if ($limit === 0) { 367 $link_0 = phutil_tag('strong', array(), $link_0); 368 } 369 370 return phutil_tag( 371 'span', 372 array(), 373 array( 374 $link_25, 375 ' - ', 376 $link_50, 377 ' - ', 378 $link_100, 379 ' - ', 380 $link_0, 381 ' Lines', 382 )); 383 } 384 385 private function buildActionList(HarbormasterBuild $build) { 386 $request = $this->getRequest(); 387 $viewer = $request->getUser(); 388 $id = $build->getID(); 389 390 $list = id(new PhabricatorActionListView()) 391 ->setUser($viewer) 392 ->setObject($build) 393 ->setObjectURI("/build/{$id}"); 394 395 $can_restart = $build->canRestartBuild(); 396 $can_stop = $build->canStopBuild(); 397 $can_resume = $build->canResumeBuild(); 398 399 $list->addAction( 400 id(new PhabricatorActionView()) 401 ->setName(pht('Restart Build')) 402 ->setIcon('fa-repeat') 403 ->setHref($this->getApplicationURI('/build/restart/'.$id.'/')) 404 ->setDisabled(!$can_restart) 405 ->setWorkflow(true)); 406 407 if ($build->canResumeBuild()) { 408 $list->addAction( 409 id(new PhabricatorActionView()) 410 ->setName(pht('Resume Build')) 411 ->setIcon('fa-play') 412 ->setHref($this->getApplicationURI('/build/resume/'.$id.'/')) 413 ->setDisabled(!$can_resume) 414 ->setWorkflow(true)); 415 } else { 416 $list->addAction( 417 id(new PhabricatorActionView()) 418 ->setName(pht('Pause Build')) 419 ->setIcon('fa-pause') 420 ->setHref($this->getApplicationURI('/build/stop/'.$id.'/')) 421 ->setDisabled(!$can_stop) 422 ->setWorkflow(true)); 423 } 424 425 return $list; 426 } 427 428 private function buildPropertyLists( 429 PHUIObjectBoxView $box, 430 HarbormasterBuild $build, 431 PhabricatorActionListView $actions) { 432 $request = $this->getRequest(); 433 $viewer = $request->getUser(); 434 435 $properties = id(new PHUIPropertyListView()) 436 ->setUser($viewer) 437 ->setObject($build) 438 ->setActionList($actions); 439 $box->addPropertyList($properties); 440 441 $handles = id(new PhabricatorHandleQuery()) 442 ->setViewer($viewer) 443 ->withPHIDs(array( 444 $build->getBuildablePHID(), 445 $build->getBuildPlanPHID(), 446 )) 447 ->execute(); 448 449 $properties->addProperty( 450 pht('Buildable'), 451 $handles[$build->getBuildablePHID()]->renderLink()); 452 453 $properties->addProperty( 454 pht('Build Plan'), 455 $handles[$build->getBuildPlanPHID()]->renderLink()); 456 457 $properties->addProperty( 458 pht('Restarts'), 459 $build->getBuildGeneration()); 460 461 $properties->addProperty( 462 pht('Status'), 463 $this->getStatus($build)); 464 } 465 466 private function getStatus(HarbormasterBuild $build) { 467 $status_view = new PHUIStatusListView(); 468 469 $item = new PHUIStatusItemView(); 470 471 if ($build->isStopping()) { 472 $status_name = pht('Pausing'); 473 $icon = PHUIStatusItemView::ICON_RIGHT; 474 $color = 'dark'; 475 } else { 476 $status = $build->getBuildStatus(); 477 $status_name = 478 HarbormasterBuild::getBuildStatusName($status); 479 $icon = HarbormasterBuild::getBuildStatusIcon($status); 480 $color = HarbormasterBuild::getBuildStatusColor($status); 481 } 482 483 $item->setTarget($status_name); 484 $item->setIcon($icon, $color); 485 $status_view->addItem($item); 486 487 return $status_view; 488 } 489 490 private function buildMessages(array $messages) { 491 $viewer = $this->getRequest()->getUser(); 492 493 if ($messages) { 494 $handles = id(new PhabricatorHandleQuery()) 495 ->setViewer($viewer) 496 ->withPHIDs(mpull($messages, 'getAuthorPHID')) 497 ->execute(); 498 } else { 499 $handles = array(); 500 } 501 502 $rows = array(); 503 foreach ($messages as $message) { 504 $rows[] = array( 505 $message->getID(), 506 $handles[$message->getAuthorPHID()]->renderLink(), 507 $message->getType(), 508 $message->getIsConsumed() ? pht('Consumed') : null, 509 phabricator_datetime($message->getDateCreated(), $viewer), 510 ); 511 } 512 513 $table = new AphrontTableView($rows); 514 $table->setNoDataString(pht('No messages for this build target.')); 515 $table->setHeaders( 516 array( 517 pht('ID'), 518 pht('From'), 519 pht('Type'), 520 pht('Consumed'), 521 pht('Received'), 522 )); 523 $table->setColumnClasses( 524 array( 525 '', 526 '', 527 'wide', 528 '', 529 'date', 530 )); 531 532 return $table; 533 } 534 535 536 537 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |