@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.

Allow "harbormaster.sendmessage" to send control command (pause, restart, abort, resume) to Builds/Buildables

Summary: Ref T13072. Expand the role of "harbormaster.sendmessage" and allow it to send control messages to Builds and Buildables.

Test Plan: Read documentation, sent commands to Builds and Buildables, hit a bunch of error cases, will deploy to catch full-lifecycle Build Target use cases.

Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam

Maniphest Tasks: T13072

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

+555 -109
+522 -105
src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php
··· 9 9 10 10 public function getMethodSummary() { 11 11 return pht( 12 - 'Send a message about the status of a build target to Harbormaster, '. 13 - 'notifying the application of build results in an external system.'); 12 + 'Modify running builds, and report build results.'); 14 13 } 15 14 16 15 public function getMethodDescription() { 16 + return pht(<<<EOREMARKUP 17 + Pause, abort, restart, and report results for builds. 18 + EOREMARKUP 19 + ); 20 + } 21 + 22 + protected function newDocumentationPages(PhabricatorUser $viewer) { 23 + $pages = array(); 24 + 25 + $pages[] = $this->newSendingDocumentationBoxPage($viewer); 26 + $pages[] = $this->newBuildsDocumentationBoxPage($viewer); 27 + $pages[] = $this->newCommandsDocumentationBoxPage($viewer); 28 + $pages[] = $this->newTargetsDocumentationBoxPage($viewer); 29 + $pages[] = $this->newUnitDocumentationBoxPage($viewer); 30 + $pages[] = $this->newLintDocumentationBoxPage($viewer); 31 + 32 + return $pages; 33 + } 34 + 35 + private function newSendingDocumentationBoxPage(PhabricatorUser $viewer) { 36 + $title = pht('Sending Messages'); 37 + $content = pht(<<<EOREMARKUP 38 + Harbormaster build objects work somewhat differently from objects in many other 39 + applications. Most application objects can be edited directly using synchronous 40 + APIs (like `maniphest.edit`, `differential.revision.edit`, and so on). 41 + 42 + However, builds require long-running background processing and Habormaster 43 + objects have a more complex lifecycle than most other application objects and 44 + may spend significant periods of time locked by daemon processes during build 45 + execition. A synchronous edit might need to wait an arbitrarily long amount of 46 + time for this lock to become available so the edit could be applied. 47 + 48 + Additionally, some edits may also require an arbitrarily long amount of time to 49 + //complete//. For example, aborting a build may execute cleanup steps which 50 + take minutes (or even hours) to complete. 51 + 52 + Since a synchronous API could not guarantee it could return results to the 53 + caller in a reasonable amount of time, the edit API for Harbormaster build 54 + objects is asynchronous: to update a Harbormaster build or build target, use 55 + this API (`harbormaster.sendmessage`) to send it a message describing an edit 56 + you would like to effect or additional information you want to provide. 57 + The message will be processed by the daemons once the build or target reaches 58 + a suitable state to receive messages. 59 + 60 + Select an object to send a message to using the `receiver` parameter. This 61 + API method can send messages to multiple types of objects: 62 + 63 + <table> 64 + <tr> 65 + <th>Object Type</th> 66 + <th>PHID Example</th> 67 + <th>Description</th> 68 + </tr> 69 + <tr> 70 + <td>Harbormaster Buildable</td> 71 + <td>`PHID-HMBB-...`</td> 72 + <td>%s</td> 73 + </tr> 74 + <tr> 75 + <td>Harbormaster Build</td> 76 + <td>`PHID-HMBD-...`</td> 77 + <td>%s</td> 78 + </tr> 79 + <tr> 80 + <td>Harbormaster Build Target</td> 81 + <td>`PHID-HMBT-...`</td> 82 + <td>%s</td> 83 + </tr> 84 + </table> 85 + 86 + See below for specifics on sending messages to different object types. 87 + EOREMARKUP 88 + , 89 + pht( 90 + 'Buildables may receive control commands like "abort" and "restart". '. 91 + 'Sending a control command to a Buildable is the same as sending it '. 92 + 'to each Build for the Buildable.'), 93 + pht( 94 + 'Builds may receive control commands like "pause", "resume", "abort", '. 95 + 'and "restart".'), 96 + pht( 97 + 'Build Targets may receive build status and result messages, like '. 98 + '"pass" or "fail".')); 99 + 100 + $content = $this->newRemarkupDocumentationView($content); 101 + 102 + return $this->newDocumentationBoxPage($viewer, $title, $content) 103 + ->setAnchor('sending') 104 + ->setIconIcon('fa-envelope-o'); 105 + } 106 + 107 + private function newBuildsDocumentationBoxPage(PhabricatorUser $viewer) { 108 + $title = pht('Updating Builds'); 109 + 110 + $content = pht(<<<EOREMARKUP 111 + You can use this method (`harbormaster.sendmessage`) to send control commands 112 + to Buildables and Builds. 113 + 114 + Specify the Build or Buildable to receive the control command by providing its 115 + PHID in the `receiver` parameter. 116 + 117 + Sending a control command to a Buildable has the same effect as sending it to 118 + each Build for the Buildable. For example, sending a "Pause" message to a 119 + Buildable will pause all builds for the Buildable (or at least attempt to). 120 + 121 + When sending control commands, the `unit` and `lint` parameters of this API 122 + method must be omitted. You can not report lint or unit results directly to 123 + a Build or Buildable, and can not report them alongside a control command. 124 + 125 + More broadly, you can not report build results directly to a Build or 126 + Buildable. Instead, report results to a Build Target. 127 + 128 + See below for a list of control commands. 129 + 130 + EOREMARKUP 131 + ); 132 + 133 + $content = $this->newRemarkupDocumentationView($content); 134 + 135 + return $this->newDocumentationBoxPage($viewer, $title, $content) 136 + ->setAnchor('builds') 137 + ->setIconIcon('fa-cubes'); 138 + } 139 + 140 + private function newCommandsDocumentationBoxPage(PhabricatorUser $viewer) { 141 + $messages = HarbormasterBuildMessageTransaction::getAllMessages(); 142 + 143 + $rows = array(); 144 + 145 + $rows[] = '<tr>'; 146 + $rows[] = '<th>'.pht('Message Type').'</th>'; 147 + $rows[] = '<th>'.pht('Description').'</th>'; 148 + $rows[] = '</tr>'; 149 + 150 + foreach ($messages as $message) { 151 + $row = array(); 152 + 153 + $row[] = sprintf( 154 + '<td>`%s`</td>', 155 + $message->getHarbormasterBuildMessageType()); 156 + 157 + $row[] = sprintf( 158 + '<td>%s</td>', 159 + $message->getHarbormasterBuildMessageDescription()); 160 + 161 + $rows[] = sprintf( 162 + '<tr>%s</tr>', 163 + implode("\n", $row)); 164 + } 165 + 166 + $message_table = sprintf( 167 + '<table>%s</table>', 168 + implode("\n", $rows)); 169 + 170 + $title = pht('Control Commands'); 171 + 172 + $content = pht(<<<EOREMARKUP 173 + You can use this method to send control commands to Buildables and Builds. 174 + 175 + This table summarizes which object types may receive control commands: 176 + 177 + <table> 178 + <tr> 179 + <th>Object Type</th> 180 + <th>PHID Example</th> 181 + <th /> 182 + <th>Description</th> 183 + </tr> 184 + <tr> 185 + <td>Harbormaster Buildable</td> 186 + <td>`PHID-HMBB-...`</td> 187 + <td>{icon check color=green}</td> 188 + <td>Buildables may receive control commands.</td> 189 + </tr> 190 + <tr> 191 + <td>Harbormaster Build</td> 192 + <td>`PHID-HMBD-...`</td> 193 + <td>{icon check color=green}</td> 194 + <td>Builds may receive control commands.</td> 195 + </tr> 196 + <tr> 197 + <td>Harbormaster Build Target</td> 198 + <td>`PHID-HMBT-...`</td> 199 + <td>{icon times color=red}</td> 200 + <td>You may **NOT** send control commands to build targets.</td> 201 + </tr> 202 + </table> 203 + 204 + You can send these commands: 205 + 206 + %s 207 + 208 + To send a command message, specify the PHID of the object you would like to 209 + receive the message using the `receiver` parameter, and specify the message 210 + type using the `type` parameter. 211 + 212 + EOREMARKUP 213 + , 214 + $message_table); 215 + 216 + $content = $this->newRemarkupDocumentationView($content); 217 + 218 + return $this->newDocumentationBoxPage($viewer, $title, $content) 219 + ->setAnchor('commands') 220 + ->setIconIcon('fa-exclamation-triangle'); 221 + } 222 + 223 + private function newTargetsDocumentationBoxPage(PhabricatorUser $viewer) { 17 224 $messages = HarbormasterMessageType::getAllMessages(); 18 225 19 - $head_type = pht('Constant'); 226 + $head_type = pht('Type'); 20 227 $head_desc = pht('Description'); 21 - $head_key = pht('Key'); 22 - $head_type = pht('Type'); 23 - $head_name = pht('Name'); 24 228 25 229 $rows = array(); 26 230 $rows[] = "| {$head_type} | {$head_desc} |"; ··· 31 235 } 32 236 $message_table = implode("\n", $rows); 33 237 238 + $content = pht(<<<EOREMARKUP 239 + If you run external builds, you can use this method to publish build results 240 + back into Harbormaster after the external system finishes work (or as it makes 241 + progress). 242 + 243 + To report build status or results, you must send a message to the appropriate 244 + Build Target. This table summarizes which object types may receive build status 245 + and result messages: 246 + 247 + <table> 248 + <tr> 249 + <th>Object Type</th> 250 + <th>PHID Example</th> 251 + <th /> 252 + <th>Description</th> 253 + </tr> 254 + <tr> 255 + <td>Harbormaster Buildable</td> 256 + <td>`PHID-HMBB-...`</td> 257 + <td>{icon times color=red}</td> 258 + <td>Buildables may **NOT** receive status or result messages.</td> 259 + </tr> 260 + <tr> 261 + <td>Harbormaster Build</td> 262 + <td>`PHID-HMBD-...`</td> 263 + <td>{icon times color=red}</td> 264 + <td>Builds may **NOT** receive status or result messages.</td> 265 + </tr> 266 + <tr> 267 + <td>Harbormaster Build Target</td> 268 + <td>`PHID-HMBT-...`</td> 269 + <td>{icon check color=green}</td> 270 + <td>Report build status and results to Build Targets.</td> 271 + </tr> 272 + </table> 273 + 274 + The simplest way to use this method to report build results is to call it once 275 + after the build finishes with a `pass` or `fail` message. This will record the 276 + build result, and continue the next step in the build if the build was waiting 277 + for a result. 278 + 279 + When you send a status message about a build target, you can optionally include 280 + detailed `lint` or `unit` results alongside the message. See below for details. 281 + 282 + If you want to report intermediate results but a build hasn't completed yet, 283 + you can use the `work` message. This message doesn't have any direct effects, 284 + but allows you to send additional data to update the progress of the build 285 + target. The target will continue waiting for a completion message, but the UI 286 + will update to show the progress which has been made. 287 + 288 + When sending a message to a build target to report the status or results of 289 + a build, your message must include a `type` which describes the overall state 290 + of the build. For example, use `pass` to tell Harbormaster that a build target 291 + completed successfully. 292 + 293 + Supported message types are: 294 + 295 + %s 296 + 297 + EOREMARKUP 298 + , 299 + $message_table); 300 + 301 + $title = pht('Updating Build Targets'); 302 + 303 + $content = $this->newRemarkupDocumentationView($content); 304 + 305 + return $this->newDocumentationBoxPage($viewer, $title, $content) 306 + ->setAnchor('targets') 307 + ->setIconIcon('fa-bullseye'); 308 + } 309 + 310 + private function newUnitDocumentationBoxPage(PhabricatorUser $viewer) { 311 + $head_key = pht('Key'); 312 + $head_desc = pht('Description'); 313 + $head_name = pht('Name'); 314 + $head_type = pht('Type'); 315 + 34 316 $rows = array(); 35 317 $rows[] = "| {$head_key} | {$head_type} | {$head_desc} |"; 36 318 $rows[] = '|-------------|--------------|--------------|'; ··· 55 337 } 56 338 $result_table = implode("\n", $rows); 57 339 340 + $valid_unit = array( 341 + array( 342 + 'name' => 'PassingTest', 343 + 'result' => ArcanistUnitTestResult::RESULT_PASS, 344 + ), 345 + array( 346 + 'name' => 'FailingTest', 347 + 'result' => ArcanistUnitTestResult::RESULT_FAIL, 348 + ), 349 + ); 350 + 351 + $json = new PhutilJSON(); 352 + $valid_unit = $json->encodeAsList($valid_unit); 353 + 354 + 355 + $title = pht('Reporting Unit Results'); 356 + 357 + $content = pht(<<<EOREMARKUP 358 + You can report test results when updating the state of a build target. The 359 + simplest way to do this is to report all the results alongside a `pass` or 360 + `fail` message, but you can also send a `work` message to report intermediate 361 + results. 362 + 363 + 364 + To provide unit test results, pass a list of results in the `unit` 365 + parameter. Each result should be a dictionary with these keys: 366 + 367 + %s 368 + 369 + The `result` parameter recognizes these test results: 370 + 371 + %s 372 + 373 + This is a simple, valid value for the `unit` parameter. It reports one passing 374 + test and one failing test: 375 + 376 + ```lang=json 377 + %s 378 + ``` 379 + EOREMARKUP 380 + , 381 + $unit_table, 382 + $result_table, 383 + $valid_unit); 384 + 385 + $content = $this->newRemarkupDocumentationView($content); 386 + 387 + return $this->newDocumentationBoxPage($viewer, $title, $content) 388 + ->setAnchor('unit'); 389 + } 390 + 391 + private function newLintDocumentationBoxPage(PhabricatorUser $viewer) { 392 + 393 + $head_key = pht('Key'); 394 + $head_desc = pht('Description'); 395 + $head_name = pht('Name'); 396 + $head_type = pht('Type'); 397 + 58 398 $rows = array(); 59 399 $rows[] = "| {$head_key} | {$head_type} | {$head_desc} |"; 60 400 $rows[] = '|-------------|--------------|--------------|'; ··· 76 416 } 77 417 $severity_table = implode("\n", $rows); 78 418 79 - $valid_unit = array( 80 - array( 81 - 'name' => 'PassingTest', 82 - 'result' => ArcanistUnitTestResult::RESULT_PASS, 83 - ), 84 - array( 85 - 'name' => 'FailingTest', 86 - 'result' => ArcanistUnitTestResult::RESULT_FAIL, 87 - ), 88 - ); 89 - 90 419 $valid_lint = array( 91 420 array( 92 421 'name' => pht('Syntax Error'), ··· 109 438 ); 110 439 111 440 $json = new PhutilJSON(); 112 - $valid_unit = $json->encodeAsList($valid_unit); 113 441 $valid_lint = $json->encodeAsList($valid_lint); 114 442 115 - return pht( 116 - "Send a message about the status of a build target to Harbormaster, ". 117 - "notifying the application of build results in an external system.". 118 - "\n\n". 119 - "Sending Messages\n". 120 - "================\n". 121 - "If you run external builds, you can use this method to publish build ". 122 - "results back into Harbormaster after the external system finishes work ". 123 - "or as it makes progress.". 124 - "\n\n". 125 - "The simplest way to use this method is to call it once after the ". 126 - "build finishes with a `pass` or `fail` message. This will record the ". 127 - "build result, and continue the next step in the build if the build was ". 128 - "waiting for a result.". 129 - "\n\n". 130 - "When you send a status message about a build target, you can ". 131 - "optionally include detailed `lint` or `unit` results alongside the ". 132 - "message. See below for details.". 133 - "\n\n". 134 - "If you want to report intermediate results but a build hasn't ". 135 - "completed yet, you can use the `work` message. This message doesn't ". 136 - "have any direct effects, but allows you to send additional data to ". 137 - "update the progress of the build target. The target will continue ". 138 - "waiting for a completion message, but the UI will update to show the ". 139 - "progress which has been made.". 140 - "\n\n". 141 - "Message Types\n". 142 - "=============\n". 143 - "When you send Harbormaster a message, you must include a `type`, ". 144 - "which describes the overall state of the build. For example, use ". 145 - "`pass` to tell Harbormaster that a build completed successfully.". 146 - "\n\n". 147 - "Supported message types are:". 148 - "\n\n". 149 - "%s". 150 - "\n\n". 151 - "Unit Results\n". 152 - "============\n". 153 - "You can report test results alongside a message. The simplest way to ". 154 - "do this is to report all the results alongside a `pass` or `fail` ". 155 - "message, but you can also send a `work` message to report intermediate ". 156 - "results.\n\n". 157 - "To provide unit test results, pass a list of results in the `unit` ". 158 - "parameter. Each result should be a dictionary with these keys:". 159 - "\n\n". 160 - "%s". 161 - "\n\n". 162 - "The `result` parameter recognizes these test results:". 163 - "\n\n". 164 - "%s". 165 - "\n\n". 166 - "This is a simple, valid value for the `unit` parameter. It reports ". 167 - "one passing test and one failing test:\n\n". 168 - "\n\n". 169 - "```lang=json\n". 170 - "%s". 171 - "```". 172 - "\n\n". 173 - "Lint Results\n". 174 - "============\n". 175 - "Like unit test results, you can report lint results alongside a ". 176 - "message. The `lint` parameter should contain results as a list of ". 177 - "dictionaries with these keys:". 178 - "\n\n". 179 - "%s". 180 - "\n\n". 181 - "The `severity` parameter recognizes these severity levels:". 182 - "\n\n". 183 - "%s". 184 - "\n\n". 185 - "This is a simple, valid value for the `lint` parameter. It reports one ". 186 - "error and one warning:". 187 - "\n\n". 188 - "```lang=json\n". 189 - "%s". 190 - "```". 191 - "\n\n", 192 - $message_table, 193 - $unit_table, 194 - $result_table, 195 - $valid_unit, 443 + $title = pht('Reporting Lint Results'); 444 + $content = pht(<<<EOREMARKUP 445 + Like unit test results, you can report lint results when updating the state 446 + of a build target. The `lint` parameter should contain results as a list of 447 + dictionaries with these keys: 448 + 449 + %s 450 + 451 + The `severity` parameter recognizes these severity levels: 452 + 453 + %s 454 + 455 + This is a simple, valid value for the `lint` parameter. It reports one error 456 + and one warning: 457 + 458 + ```lang=json 459 + %s 460 + ``` 461 + 462 + EOREMARKUP 463 + , 196 464 $lint_table, 197 465 $severity_table, 198 466 $valid_lint); 467 + 468 + $content = $this->newRemarkupDocumentationView($content); 469 + 470 + return $this->newDocumentationBoxPage($viewer, $title, $content) 471 + ->setAnchor('lint'); 199 472 } 200 473 201 474 protected function defineParamTypes() { 202 475 $messages = HarbormasterMessageType::getAllMessages(); 476 + 477 + $more_messages = HarbormasterBuildMessageTransaction::getAllMessages(); 478 + $more_messages = mpull($more_messages, 'getHarbormasterBuildMessageType'); 479 + 480 + $messages = array_merge($messages, $more_messages); 481 + $messages = array_unique($messages); 482 + 483 + sort($messages); 484 + 203 485 $type_const = $this->formatStringConstants($messages); 204 486 205 487 return array( 206 - 'buildTargetPHID' => 'required phid', 488 + 'receiver' => 'required string|phid', 207 489 'type' => 'required '.$type_const, 208 490 'unit' => 'optional list<wild>', 209 491 'lint' => 'optional list<wild>', 492 + 'buildTargetPHID' => 'deprecated optional phid', 210 493 ); 211 494 } 212 495 ··· 215 498 } 216 499 217 500 protected function execute(ConduitAPIRequest $request) { 218 - $viewer = $request->getUser(); 501 + $viewer = $request->getViewer(); 502 + 503 + $receiver_name = $request->getValue('receiver'); 219 504 220 505 $build_target_phid = $request->getValue('buildTargetPHID'); 506 + if ($build_target_phid !== null) { 507 + if ($receiver_name === null) { 508 + $receiver_name = $build_target_phid; 509 + } else { 510 + throw new Exception( 511 + pht( 512 + 'Call specifies both "receiver" and "buildTargetPHID". '. 513 + 'When using the modern "receiver" parameter, omit the '. 514 + 'deprecated "buildTargetPHID" parameter.')); 515 + } 516 + } 517 + 518 + if (!strlen($receiver_name)) { 519 + throw new Exception( 520 + pht( 521 + 'Call omits required "receiver" parameter. Specify the PHID '. 522 + 'of the object you want to send a message to.')); 523 + } 524 + 221 525 $message_type = $request->getValue('type'); 526 + if (!strlen($message_type)) { 527 + throw new Exception( 528 + pht( 529 + 'Call omits required "type" parameter. Specify the type of '. 530 + 'message you want to send.')); 531 + } 222 532 223 - $build_target = id(new HarbormasterBuildTargetQuery()) 533 + $receiver_object = id(new PhabricatorObjectQuery()) 224 534 ->setViewer($viewer) 225 - ->withPHIDs(array($build_target_phid)) 535 + ->withNames(array($receiver_name)) 226 536 ->executeOne(); 227 - if (!$build_target) { 228 - throw new Exception(pht('No such build target!')); 537 + if (!$receiver_object) { 538 + throw new Exception( 539 + pht( 540 + 'Unable to load object "%s" to receive message.', 541 + $receiver_name)); 542 + } 543 + 544 + $is_target = ($receiver_object instanceof HarbormasterBuildTarget); 545 + if ($is_target) { 546 + return $this->sendToTarget($request, $message_type, $receiver_object); 547 + } 548 + 549 + if ($request->getValue('unit') !== null) { 550 + throw new Exception( 551 + pht( 552 + 'Call includes "unit" parameter. This parameter must be omitted '. 553 + 'when the receiver is not a Build Target.')); 554 + } 555 + 556 + if ($request->getValue('lint') !== null) { 557 + throw new Exception( 558 + pht( 559 + 'Call includes "lint" parameter. This parameter must be omitted '. 560 + 'when the receiver is not a Build Target.')); 561 + } 562 + 563 + $is_build = ($receiver_object instanceof HarbormasterBuild); 564 + if ($is_build) { 565 + return $this->sendToBuild($request, $message_type, $receiver_object); 229 566 } 230 567 568 + $is_buildable = ($receiver_object instanceof HarbormasterBuildable); 569 + if ($is_buildable) { 570 + return $this->sendToBuildable($request, $message_type, $receiver_object); 571 + } 572 + 573 + throw new Exception( 574 + pht( 575 + 'Receiver object (of class "%s") is not a valid receiver.', 576 + get_class($receiver_object))); 577 + } 578 + 579 + private function sendToTarget( 580 + ConduitAPIRequest $request, 581 + $message_type, 582 + HarbormasterBuildTarget $build_target) { 583 + $viewer = $request->getViewer(); 584 + 231 585 $save = array(); 232 586 233 587 $lint_messages = $request->getValue('lint', array()); ··· 268 622 )); 269 623 270 624 return null; 625 + } 626 + 627 + private function sendToBuild( 628 + ConduitAPIRequest $request, 629 + $message_type, 630 + HarbormasterBuild $build) { 631 + $viewer = $request->getViewer(); 632 + 633 + $xaction = 634 + HarbormasterBuildMessageTransaction::getTransactionObjectForMessageType( 635 + $message_type); 636 + if (!$xaction) { 637 + throw new Exception( 638 + pht( 639 + 'Message type "%s" is not supported.', 640 + $message_type)); 641 + } 642 + 643 + // NOTE: This is a slightly weaker check than we perform in the web UI. 644 + // We allow API callers to send a "pause" message to a pausing build, 645 + // for example, even though the message will have no effect. 646 + $xaction->assertCanApplyMessage($viewer, $build); 647 + 648 + $build->sendMessage($viewer, $xaction->getHarbormasterBuildMessageType()); 649 + } 650 + 651 + private function sendToBuildable( 652 + ConduitAPIRequest $request, 653 + $message_type, 654 + HarbormasterBuildable $buildable) { 655 + $viewer = $request->getViewer(); 656 + 657 + $xaction = 658 + HarbormasterBuildMessageTransaction::getTransactionObjectForMessageType( 659 + $message_type); 660 + if (!$xaction) { 661 + throw new Exception( 662 + pht( 663 + 'Message type "%s" is not supported.', 664 + $message_type)); 665 + } 666 + 667 + // Reload the Buildable to load Builds. 668 + $buildable = id(new HarbormasterBuildableQuery()) 669 + ->setViewer($viewer) 670 + ->withIDs(array($buildable->getID())) 671 + ->needBuilds(true) 672 + ->executeOne(); 673 + 674 + $can_send = array(); 675 + foreach ($buildable->getBuilds() as $build) { 676 + if ($xaction->canApplyMessage($viewer, $build)) { 677 + $can_send[] = $build; 678 + } 679 + } 680 + 681 + // NOTE: This doesn't actually apply a transaction to the Buildable, 682 + // but that transaction is purely informational and should probably be 683 + // implemented as a Message. 684 + 685 + foreach ($can_send as $build) { 686 + $build->sendMessage($viewer, $xaction->getHarbormasterBuildMessageType()); 687 + } 271 688 } 272 689 273 690 }
+5 -1
src/applications/harbormaster/exception/HarbormasterRestartException.php
··· 9 9 $this->setTitle($title); 10 10 $this->appendParagraph($body); 11 11 12 - parent::__construct($title); 12 + parent::__construct( 13 + pht( 14 + '%s: %s', 15 + $title, 16 + $body)); 13 17 } 14 18 15 19 public function setTitle($title) {
+4
src/applications/harbormaster/xaction/build/HarbormasterBuildMessageAbortTransaction.php
··· 27 27 'Progress on this build will be discarded. Really abort build?'); 28 28 } 29 29 30 + public function getHarbormasterBuildMessageDescription() { 31 + return pht('Abort the build, discarding progress.'); 32 + } 33 + 30 34 public function newBuildableConfirmPromptTitle( 31 35 array $builds, 32 36 array $sendable) {
+5
src/applications/harbormaster/xaction/build/HarbormasterBuildMessagePauseTransaction.php
··· 28 28 'complete. You can resume the build later.'); 29 29 } 30 30 31 + 32 + public function getHarbormasterBuildMessageDescription() { 33 + return pht('Pause the build.'); 34 + } 35 + 31 36 public function newBuildableConfirmPromptTitle( 32 37 array $builds, 33 38 array $sendable) {
+5
src/applications/harbormaster/xaction/build/HarbormasterBuildMessageRestartTransaction.php
··· 28 28 'Side effects of the build will occur again. Really restart build?'); 29 29 } 30 30 31 + 32 + public function getHarbormasterBuildMessageDescription() { 33 + return pht('Restart the build, discarding all progress.'); 34 + } 35 + 31 36 public function newBuildableConfirmPromptTitle( 32 37 array $builds, 33 38 array $sendable) {
+5 -1
src/applications/harbormaster/xaction/build/HarbormasterBuildMessageResumeTransaction.php
··· 27 27 'Work will continue on the build. Really resume?'); 28 28 } 29 29 30 + public function getHarbormasterBuildMessageDescription() { 31 + return pht('Resume work on a previously paused build.'); 32 + } 33 + 30 34 public function newBuildableConfirmPromptTitle( 31 35 array $builds, 32 36 array $sendable) { ··· 77 81 'You can not resume a build that uses an autoplan.')); 78 82 } 79 83 80 - if (!$build->isPaused()) { 84 + if (!$build->isPaused() && !$build->isPausing()) { 81 85 throw new HarbormasterRestartException( 82 86 pht('Unable to Resume Build'), 83 87 pht(
+9 -2
src/applications/harbormaster/xaction/build/HarbormasterBuildMessageTransaction.php
··· 8 8 } 9 9 10 10 abstract public function getHarbormasterBuildMessageName(); 11 + abstract public function getHarbormasterBuildMessageDescription(); 11 12 abstract public function getHarbormasterBuildableMessageName(); 12 13 abstract public function getHarbormasterBuildableMessageEffect(); 13 14 ··· 42 43 ); 43 44 } 44 45 45 - final public static function getTransactionObjectForMessageType( 46 - $message_type) { 46 + final public static function getAllMessages() { 47 47 $message_xactions = id(new PhutilClassMapQuery()) 48 48 ->setAncestorClass(__CLASS__) 49 49 ->execute(); 50 + 51 + return $message_xactions; 52 + } 53 + 54 + final public static function getTransactionObjectForMessageType( 55 + $message_type) { 56 + $message_xactions = self::getAllMessages(); 50 57 51 58 foreach ($message_xactions as $message_xaction) { 52 59 $xaction_type = $message_xaction->getHarbormasterBuildMessageType();