[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/harbormaster/controller/ -> HarbormasterBuildViewController.php (source)

   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  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1