[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

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

   1  <?php
   2  
   3  final class HarbormasterPlanViewController extends HarbormasterPlanController {
   4  
   5    private $id;
   6  
   7    public function willProcessRequest(array $data) {
   8      $this->id = $data['id'];
   9    }
  10  
  11    public function processRequest() {
  12      $request = $this->getRequest();
  13      $viewer = $request->getUser();
  14  
  15      $id = $this->id;
  16  
  17      $plan = id(new HarbormasterBuildPlanQuery())
  18        ->setViewer($viewer)
  19        ->withIDs(array($id))
  20        ->executeOne();
  21      if (!$plan) {
  22        return new Aphront404Response();
  23      }
  24  
  25      $xactions = id(new HarbormasterBuildPlanTransactionQuery())
  26        ->setViewer($viewer)
  27        ->withObjectPHIDs(array($plan->getPHID()))
  28        ->execute();
  29  
  30      $xaction_view = id(new PhabricatorApplicationTransactionView())
  31        ->setUser($viewer)
  32        ->setObjectPHID($plan->getPHID())
  33        ->setTransactions($xactions)
  34        ->setShouldTerminate(true);
  35  
  36      $title = pht('Plan %d', $id);
  37  
  38      $header = id(new PHUIHeaderView())
  39        ->setHeader($title)
  40        ->setUser($viewer)
  41        ->setPolicyObject($plan);
  42  
  43      $box = id(new PHUIObjectBoxView())
  44        ->setHeader($header);
  45  
  46      $actions = $this->buildActionList($plan);
  47      $this->buildPropertyLists($box, $plan, $actions);
  48  
  49      $crumbs = $this->buildApplicationCrumbs();
  50      $crumbs->addTextCrumb(pht('Plan %d', $id));
  51  
  52      list($step_list, $has_any_conflicts, $would_deadlock) =
  53        $this->buildStepList($plan);
  54  
  55      if ($would_deadlock) {
  56        $box->setFormErrors(
  57          array(
  58            pht(
  59              'This build plan will deadlock when executed, due to '.
  60              'circular dependencies present in the build plan. '.
  61              'Examine the step list and resolve the deadlock.'),
  62          ));
  63      } else if ($has_any_conflicts) {
  64        // A deadlocking build will also cause all the artifacts to be
  65        // invalid, so we just skip showing this message if that's the
  66        // case.
  67        $box->setFormErrors(
  68          array(
  69            pht(
  70              'This build plan has conflicts in one or more build steps. '.
  71              'Examine the step list and resolve the listed errors.'),
  72          ));
  73      }
  74  
  75      return $this->buildApplicationPage(
  76        array(
  77          $crumbs,
  78          $box,
  79          $step_list,
  80          $xaction_view,
  81        ),
  82        array(
  83          'title' => $title,
  84        ));
  85    }
  86  
  87    private function buildStepList(HarbormasterBuildPlan $plan) {
  88      $request = $this->getRequest();
  89      $viewer = $request->getUser();
  90  
  91      $run_order =
  92        HarbormasterBuildGraph::determineDependencyExecution($plan);
  93  
  94      $steps = id(new HarbormasterBuildStepQuery())
  95        ->setViewer($viewer)
  96        ->withBuildPlanPHIDs(array($plan->getPHID()))
  97        ->execute();
  98      $steps = mpull($steps, null, 'getPHID');
  99  
 100      $can_edit = $this->hasApplicationCapability(
 101        HarbormasterManagePlansCapability::CAPABILITY);
 102  
 103      $step_list = id(new PHUIObjectItemListView())
 104        ->setUser($viewer)
 105        ->setNoDataString(
 106          pht('This build plan does not have any build steps yet.'));
 107  
 108      $i = 1;
 109      $last_depth = 0;
 110      $has_any_conflicts = false;
 111      $is_deadlocking = false;
 112      foreach ($run_order as $run_ref) {
 113        $step = $steps[$run_ref['node']->getPHID()];
 114        $depth = $run_ref['depth'] + 1;
 115        if ($last_depth !== $depth) {
 116          $last_depth = $depth;
 117          $i = 1;
 118        } else {
 119          $i++;
 120        }
 121  
 122        $implementation = null;
 123        try {
 124          $implementation = $step->getStepImplementation();
 125        } catch (Exception $ex) {
 126          // We can't initialize the implementation. This might be because
 127          // it's been renamed or no longer exists.
 128          $item = id(new PHUIObjectItemView())
 129            ->setObjectName(pht('Step %d.%d', $depth, $i))
 130            ->setHeader(pht('Unknown Implementation'))
 131            ->setBarColor('red')
 132            ->addAttribute(pht(
 133              'This step has an invalid implementation (%s).',
 134              $step->getClassName()))
 135            ->addAction(
 136              id(new PHUIListItemView())
 137                ->setIcon('fa-times')
 138                ->addSigil('harbormaster-build-step-delete')
 139                ->setWorkflow(true)
 140                ->setRenderNameAsTooltip(true)
 141                ->setName(pht('Delete'))
 142                ->setHref(
 143                  $this->getApplicationURI('step/delete/'.$step->getID().'/')));
 144          $step_list->addItem($item);
 145          continue;
 146        }
 147        $item = id(new PHUIObjectItemView())
 148          ->setObjectName(pht('Step %d.%d', $depth, $i))
 149          ->setHeader($step->getName());
 150  
 151        $item->addAttribute($implementation->getDescription());
 152  
 153        $step_id = $step->getID();
 154        $edit_uri = $this->getApplicationURI("step/edit/{$step_id}/");
 155        $delete_uri = $this->getApplicationURI("step/delete/{$step_id}/");
 156  
 157        if ($can_edit) {
 158          $item->setHref($edit_uri);
 159        }
 160  
 161        $item
 162          ->setHref($edit_uri)
 163          ->addAction(
 164            id(new PHUIListItemView())
 165              ->setIcon('fa-times')
 166              ->addSigil('harbormaster-build-step-delete')
 167              ->setWorkflow(true)
 168              ->setDisabled(!$can_edit)
 169              ->setHref(
 170                $this->getApplicationURI('step/delete/'.$step->getID().'/')));
 171  
 172        $depends = $step->getStepImplementation()->getDependencies($step);
 173        $inputs = $step->getStepImplementation()->getArtifactInputs();
 174        $outputs = $step->getStepImplementation()->getArtifactOutputs();
 175  
 176        $has_conflicts = false;
 177        if ($depends || $inputs || $outputs) {
 178          $available_artifacts =
 179            HarbormasterBuildStepImplementation::getAvailableArtifacts(
 180              $plan,
 181              $step,
 182              null);
 183          $available_artifacts = ipull($available_artifacts, 'type');
 184  
 185          list($depends_ui, $has_conflicts) = $this->buildDependsOnList(
 186              $depends,
 187              pht('Depends On'),
 188              $steps);
 189  
 190          list($inputs_ui, $has_conflicts) = $this->buildArtifactList(
 191              $inputs,
 192              'in',
 193              pht('Input Artifacts'),
 194              $available_artifacts);
 195  
 196          list($outputs_ui) = $this->buildArtifactList(
 197              $outputs,
 198              'out',
 199              pht('Output Artifacts'),
 200              array());
 201  
 202          $item->appendChild(
 203            phutil_tag(
 204              'div',
 205              array(
 206                'class' => 'harbormaster-artifact-io',
 207              ),
 208              array(
 209                $depends_ui,
 210                $inputs_ui,
 211                $outputs_ui,
 212              )));
 213        }
 214  
 215        if ($has_conflicts) {
 216          $has_any_conflicts = true;
 217          $item->setBarColor('red');
 218        }
 219  
 220        if ($run_ref['cycle']) {
 221          $is_deadlocking = true;
 222        }
 223  
 224        if ($is_deadlocking) {
 225          $item->setBarColor('red');
 226        }
 227  
 228        $step_list->addItem($item);
 229      }
 230  
 231      return array($step_list, $has_any_conflicts, $is_deadlocking);
 232    }
 233  
 234    private function buildActionList(HarbormasterBuildPlan $plan) {
 235      $request = $this->getRequest();
 236      $viewer = $request->getUser();
 237      $id = $plan->getID();
 238  
 239      $list = id(new PhabricatorActionListView())
 240        ->setUser($viewer)
 241        ->setObject($plan)
 242        ->setObjectURI($this->getApplicationURI("plan/{$id}/"));
 243  
 244      $can_edit = $this->hasApplicationCapability(
 245        HarbormasterManagePlansCapability::CAPABILITY);
 246  
 247      $list->addAction(
 248        id(new PhabricatorActionView())
 249          ->setName(pht('Edit Plan'))
 250          ->setHref($this->getApplicationURI("plan/edit/{$id}/"))
 251          ->setWorkflow(!$can_edit)
 252          ->setDisabled(!$can_edit)
 253          ->setIcon('fa-pencil'));
 254  
 255      if ($plan->isDisabled()) {
 256        $list->addAction(
 257          id(new PhabricatorActionView())
 258            ->setName(pht('Enable Plan'))
 259            ->setHref($this->getApplicationURI("plan/disable/{$id}/"))
 260            ->setWorkflow(true)
 261            ->setDisabled(!$can_edit)
 262            ->setIcon('fa-check'));
 263      } else {
 264        $list->addAction(
 265          id(new PhabricatorActionView())
 266            ->setName(pht('Disable Plan'))
 267            ->setHref($this->getApplicationURI("plan/disable/{$id}/"))
 268            ->setWorkflow(true)
 269            ->setDisabled(!$can_edit)
 270            ->setIcon('fa-ban'));
 271      }
 272  
 273      $list->addAction(
 274        id(new PhabricatorActionView())
 275          ->setName(pht('Add Build Step'))
 276          ->setHref($this->getApplicationURI("step/add/{$id}/"))
 277          ->setWorkflow(true)
 278          ->setDisabled(!$can_edit)
 279          ->setIcon('fa-plus'));
 280  
 281      $list->addAction(
 282        id(new PhabricatorActionView())
 283          ->setName(pht('Run Plan Manually'))
 284          ->setHref($this->getApplicationURI("plan/run/{$id}/"))
 285          ->setWorkflow(true)
 286          ->setDisabled(!$can_edit)
 287          ->setIcon('fa-play-circle'));
 288  
 289      return $list;
 290    }
 291  
 292    private function buildPropertyLists(
 293      PHUIObjectBoxView $box,
 294      HarbormasterBuildPlan $plan,
 295      PhabricatorActionListView $actions) {
 296      $request = $this->getRequest();
 297      $viewer = $request->getUser();
 298  
 299      $properties = id(new PHUIPropertyListView())
 300        ->setUser($viewer)
 301        ->setObject($plan)
 302        ->setActionList($actions);
 303      $box->addPropertyList($properties);
 304  
 305      $properties->addProperty(
 306        pht('Created'),
 307        phabricator_datetime($plan->getDateCreated(), $viewer));
 308  
 309    }
 310  
 311    private function buildArtifactList(
 312      array $artifacts,
 313      $kind,
 314      $name,
 315      array $available_artifacts) {
 316      $has_conflicts = false;
 317  
 318      if (!$artifacts) {
 319        return array(null, $has_conflicts);
 320      }
 321  
 322      $this->requireResource('harbormaster-css');
 323  
 324      $header = phutil_tag(
 325        'div',
 326        array(
 327          'class' => 'harbormaster-artifact-summary-header',
 328        ),
 329        $name);
 330  
 331      $is_input = ($kind == 'in');
 332  
 333      $list = new PHUIStatusListView();
 334      foreach ($artifacts as $artifact) {
 335        $error = null;
 336  
 337        $key = idx($artifact, 'key');
 338        if (!strlen($key)) {
 339          $bound = phutil_tag('em', array(), pht('(null)'));
 340          if ($is_input) {
 341            // This is an unbound input. For now, all inputs are always required.
 342            $icon = PHUIStatusItemView::ICON_WARNING;
 343            $color = 'red';
 344            $icon_label = pht('Required Input');
 345            $has_conflicts = true;
 346            $error = pht('This input is required, but not configured.');
 347          } else {
 348            // This is an unnamed output. Outputs do not necessarily need to be
 349            // named.
 350            $icon = PHUIStatusItemView::ICON_OPEN;
 351            $color = 'bluegrey';
 352            $icon_label = pht('Unused Output');
 353          }
 354        } else {
 355          $bound = phutil_tag('strong', array(), $key);
 356          if ($is_input) {
 357            if (isset($available_artifacts[$key])) {
 358              if ($available_artifacts[$key] == idx($artifact, 'type')) {
 359                $icon = PHUIStatusItemView::ICON_ACCEPT;
 360                $color = 'green';
 361                $icon_label = pht('Valid Input');
 362              } else {
 363                $icon = PHUIStatusItemView::ICON_WARNING;
 364                $color = 'red';
 365                $icon_label = pht('Bad Input Type');
 366                $has_conflicts = true;
 367                $error = pht(
 368                  'This input is bound to the wrong artifact type. It is bound '.
 369                  'to a "%s" artifact, but should be bound to a "%s" artifact.',
 370                  $available_artifacts[$key],
 371                  idx($artifact, 'type'));
 372              }
 373            } else {
 374              $icon = PHUIStatusItemView::ICON_QUESTION;
 375              $color = 'red';
 376              $icon_label = pht('Unknown Input');
 377              $has_conflicts = true;
 378              $error = pht(
 379                'This input is bound to an artifact ("%s") which does not exist '.
 380                'at this stage in the build process.',
 381                $key);
 382            }
 383          } else {
 384            $icon = PHUIStatusItemView::ICON_DOWN;
 385            $color = 'green';
 386            $icon_label = pht('Valid Output');
 387          }
 388        }
 389  
 390        if ($error) {
 391          $note = array(
 392            phutil_tag('strong', array(), pht('ERROR:')),
 393            ' ',
 394            $error,
 395          );
 396        } else {
 397          $note = $bound;
 398        }
 399  
 400        $list->addItem(
 401          id(new PHUIStatusItemView())
 402            ->setIcon($icon, $color, $icon_label)
 403            ->setTarget($artifact['name'])
 404            ->setNote($note));
 405      }
 406  
 407      $ui = array(
 408        $header,
 409        $list,
 410      );
 411  
 412      return array($ui, $has_conflicts);
 413    }
 414  
 415    private function buildDependsOnList(
 416      array $step_phids,
 417      $name,
 418      array $steps) {
 419      $has_conflicts = false;
 420  
 421      if (count($step_phids) === 0) {
 422        return null;
 423      }
 424  
 425      $this->requireResource('harbormaster-css');
 426  
 427      $steps = mpull($steps, null, 'getPHID');
 428  
 429      $header = phutil_tag(
 430        'div',
 431        array(
 432          'class' => 'harbormaster-artifact-summary-header',
 433        ),
 434        $name);
 435  
 436      $list = new PHUIStatusListView();
 437      foreach ($step_phids as $step_phid) {
 438        $error = null;
 439  
 440        if (idx($steps, $step_phid) === null) {
 441          $icon = PHUIStatusItemView::ICON_WARNING;
 442          $color = 'red';
 443          $icon_label = pht('Missing Dependency');
 444          $has_conflicts = true;
 445          $error = pht(
 446            'This dependency specifies a build step which doesn\'t exist.');
 447        } else {
 448          $bound = phutil_tag(
 449            'strong',
 450            array(),
 451            idx($steps, $step_phid)->getName());
 452          $icon = PHUIStatusItemView::ICON_ACCEPT;
 453          $color = 'green';
 454          $icon_label = pht('Valid Input');
 455        }
 456  
 457        if ($error) {
 458          $note = array(
 459            phutil_tag('strong', array(), pht('ERROR:')),
 460            ' ',
 461            $error,
 462          );
 463        } else {
 464          $note = $bound;
 465        }
 466  
 467        $list->addItem(
 468          id(new PHUIStatusItemView())
 469            ->setIcon($icon, $color, $icon_label)
 470            ->setTarget(pht('Build Step'))
 471            ->setNote($note));
 472      }
 473  
 474      $ui = array(
 475        $header,
 476        $list,
 477      );
 478  
 479      return array($ui, $has_conflicts);
 480    }
 481  }


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