[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
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 }
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 |