Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php b/src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php
index 6d68b0a8b6..9c3f88d631 100644
--- a/src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php
+++ b/src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php
@@ -1,268 +1,273 @@
<?php
final class HarbormasterSendMessageConduitAPIMethod
extends HarbormasterConduitAPIMethod {
public function getAPIMethodName() {
return 'harbormaster.sendmessage';
}
public function getMethodSummary() {
return pht(
'Send a message about the status of a build target to Harbormaster, '.
'notifying the application of build results in an external system.');
}
public function getMethodDescription() {
$messages = HarbormasterMessageType::getAllMessages();
$head_type = pht('Constant');
$head_desc = pht('Description');
$head_key = pht('Key');
$head_type = pht('Type');
$head_name = pht('Name');
$rows = array();
$rows[] = "| {$head_type} | {$head_desc} |";
$rows[] = '|--------------|--------------|';
foreach ($messages as $message) {
$description = HarbormasterMessageType::getMessageDescription($message);
$rows[] = "| `{$message}` | {$description} |";
}
$message_table = implode("\n", $rows);
$rows = array();
$rows[] = "| {$head_key} | {$head_type} | {$head_desc} |";
$rows[] = '|-------------|--------------|--------------|';
$unit_spec = HarbormasterBuildUnitMessage::getParameterSpec();
foreach ($unit_spec as $key => $parameter) {
$type = idx($parameter, 'type');
$type = str_replace('|', ' '.pht('or').' ', $type);
$description = idx($parameter, 'description');
$rows[] = "| `{$key}` | //{$type}// | {$description} |";
}
$unit_table = implode("\n", $rows);
$rows = array();
$rows[] = "| {$head_key} | {$head_name} | {$head_desc} |";
$rows[] = '|-------------|--------------|--------------|';
$results = ArcanistUnitTestResult::getAllResultCodes();
foreach ($results as $result_code) {
$name = ArcanistUnitTestResult::getResultCodeName($result_code);
$description = ArcanistUnitTestResult::getResultCodeDescription(
$result_code);
$rows[] = "| `{$result_code}` | **{$name}** | {$description} |";
}
$result_table = implode("\n", $rows);
$rows = array();
$rows[] = "| {$head_key} | {$head_type} | {$head_desc} |";
$rows[] = '|-------------|--------------|--------------|';
$lint_spec = HarbormasterBuildLintMessage::getParameterSpec();
foreach ($lint_spec as $key => $parameter) {
$type = idx($parameter, 'type');
$type = str_replace('|', ' '.pht('or').' ', $type);
$description = idx($parameter, 'description');
$rows[] = "| `{$key}` | //{$type}// | {$description} |";
}
$lint_table = implode("\n", $rows);
$rows = array();
$rows[] = "| {$head_key} | {$head_name} |";
$rows[] = '|-------------|--------------|';
$severities = ArcanistLintSeverity::getLintSeverities();
foreach ($severities as $key => $name) {
$rows[] = "| `{$key}` | **{$name}** |";
}
$severity_table = implode("\n", $rows);
$valid_unit = array(
array(
'name' => 'PassingTest',
'result' => ArcanistUnitTestResult::RESULT_PASS,
),
array(
'name' => 'FailingTest',
'result' => ArcanistUnitTestResult::RESULT_FAIL,
),
);
$valid_lint = array(
array(
'name' => pht('Syntax Error'),
'code' => 'EXAMPLE1',
'severity' => ArcanistLintSeverity::SEVERITY_ERROR,
'path' => 'path/to/example.c',
'line' => 17,
'char' => 3,
),
array(
'name' => pht('Not A Haiku'),
'code' => 'EXAMPLE2',
'severity' => ArcanistLintSeverity::SEVERITY_ERROR,
'path' => 'path/to/source.cpp',
'line' => 23,
'char' => 1,
'description' => pht(
'This function definition is not a haiku.'),
),
);
$json = new PhutilJSON();
$valid_unit = $json->encodeAsList($valid_unit);
$valid_lint = $json->encodeAsList($valid_lint);
return pht(
"Send a message about the status of a build target to Harbormaster, ".
"notifying the application of build results in an external system.".
"\n\n".
"Sending Messages\n".
"================\n".
"If you run external builds, you can use this method to publish build ".
"results back into Harbormaster after the external system finishes work ".
"or as it makes progress.".
"\n\n".
"The simplest way to use this method is to call it once after the ".
"build finishes with a `pass` or `fail` message. This will record the ".
"build result, and continue the next step in the build if the build was ".
"waiting for a result.".
"\n\n".
"When you send a status message about a build target, you can ".
"optionally include detailed `lint` or `unit` results alongside the ".
"message. See below for details.".
"\n\n".
"If you want to report intermediate results but a build hasn't ".
"completed yet, you can use the `work` message. This message doesn't ".
"have any direct effects, but allows you to send additional data to ".
"update the progress of the build target. The target will continue ".
"waiting for a completion message, but the UI will update to show the ".
"progress which has been made.".
"\n\n".
"Message Types\n".
"=============\n".
"When you send Harbormaster a message, you must include a `type`, ".
"which describes the overall state of the build. For example, use ".
"`pass` to tell Harbomaster that a build completed successfully.".
"\n\n".
"Supported message types are:".
"\n\n".
"%s".
"\n\n".
"Unit Results\n".
"============\n".
"You can report test results alongside a message. The simplest way to ".
"do this is to report all the results alongside a `pass` or `fail` ".
"message, but you can also send a `work` message to report intermediate ".
"results.\n\n".
"To provide unit test results, pass a list of results in the `unit` ".
"parameter. Each result shoud be a dictionary with these keys:".
"\n\n".
"%s".
"\n\n".
"The `result` parameter recognizes these test results:".
"\n\n".
"%s".
"\n\n".
"This is a simple, valid value for the `unit` parameter. It reports ".
"one passing test and one failing test:\n\n".
"\n\n".
"```lang=json\n".
"%s".
"```".
"\n\n".
"Lint Results\n".
"============\n".
"Like unit test results, you can report lint results alongside a ".
"message. The `lint` parameter should contain results as a list of ".
"dictionaries with these keys:".
"\n\n".
"%s".
"\n\n".
"The `severity` parameter recognizes these severity levels:".
"\n\n".
"%s".
"\n\n".
"This is a simple, valid value for the `lint` parameter. It reports one ".
"error and one warning:".
"\n\n".
"```lang=json\n".
"%s".
"```".
"\n\n",
$message_table,
$unit_table,
$result_table,
$valid_unit,
$lint_table,
$severity_table,
$valid_lint);
}
protected function defineParamTypes() {
$messages = HarbormasterMessageType::getAllMessages();
$type_const = $this->formatStringConstants($messages);
return array(
'buildTargetPHID' => 'required phid',
'type' => 'required '.$type_const,
'unit' => 'optional list<wild>',
'lint' => 'optional list<wild>',
);
}
protected function defineReturnType() {
return 'void';
}
protected function execute(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$build_target_phid = $request->getValue('buildTargetPHID');
$message_type = $request->getValue('type');
$build_target = id(new HarbormasterBuildTargetQuery())
->setViewer($viewer)
->withPHIDs(array($build_target_phid))
->executeOne();
if (!$build_target) {
throw new Exception(pht('No such build target!'));
}
$save = array();
$lint_messages = $request->getValue('lint', array());
foreach ($lint_messages as $lint) {
$save[] = HarbormasterBuildLintMessage::newFromDictionary(
$build_target,
$lint);
}
$unit_messages = $request->getValue('unit', array());
foreach ($unit_messages as $unit) {
$save[] = HarbormasterBuildUnitMessage::newFromDictionary(
$build_target,
$unit);
}
$save[] = HarbormasterBuildMessage::initializeNewMessage($viewer)
->setBuildTargetPHID($build_target->getPHID())
->setType($message_type);
$build_target->openTransaction();
foreach ($save as $object) {
$object->save();
}
$build_target->saveTransaction();
// If the build has completely paused because all steps are blocked on
// waiting targets, this will resume it.
+ $build = $build_target->getBuild();
+
PhabricatorWorker::scheduleTask(
'HarbormasterBuildWorker',
array(
- 'buildID' => $build_target->getBuild()->getID(),
+ 'buildID' => $build->getID(),
+ ),
+ array(
+ 'objectPHID' => $build->getPHID(),
));
return null;
}
}
diff --git a/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php
index b8c39146cb..0727a473ec 100644
--- a/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php
+++ b/src/applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php
@@ -1,117 +1,120 @@
<?php
final class HarbormasterBuildTransactionEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorHarbormasterApplication';
}
public function getEditorObjectsDescription() {
return pht('Harbormaster Builds');
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = HarbormasterBuildTransaction::TYPE_CREATE;
$types[] = HarbormasterBuildTransaction::TYPE_COMMAND;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case HarbormasterBuildTransaction::TYPE_CREATE:
case HarbormasterBuildTransaction::TYPE_COMMAND:
return null;
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case HarbormasterBuildTransaction::TYPE_CREATE:
return true;
case HarbormasterBuildTransaction::TYPE_COMMAND:
return $xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case HarbormasterBuildTransaction::TYPE_CREATE:
return;
case HarbormasterBuildTransaction::TYPE_COMMAND:
return $this->executeBuildCommand($object, $xaction);
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
private function executeBuildCommand(
HarbormasterBuild $build,
HarbormasterBuildTransaction $xaction) {
$command = $xaction->getNewValue();
switch ($command) {
case HarbormasterBuildCommand::COMMAND_RESTART:
$issuable = $build->canRestartBuild();
break;
case HarbormasterBuildCommand::COMMAND_PAUSE:
$issuable = $build->canPauseBuild();
break;
case HarbormasterBuildCommand::COMMAND_RESUME:
$issuable = $build->canResumeBuild();
break;
case HarbormasterBuildCommand::COMMAND_ABORT:
$issuable = $build->canAbortBuild();
break;
default:
throw new Exception(pht('Unknown command %s', $command));
}
if (!$issuable) {
return;
}
id(new HarbormasterBuildCommand())
->setAuthorPHID($xaction->getAuthorPHID())
->setTargetPHID($build->getPHID())
->setCommand($command)
->save();
PhabricatorWorker::scheduleTask(
'HarbormasterBuildWorker',
array(
'buildID' => $build->getID(),
+ ),
+ array(
+ 'objectPHID' => $build->getPHID(),
));
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case HarbormasterBuildTransaction::TYPE_CREATE:
case HarbormasterBuildTransaction::TYPE_COMMAND:
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
}
diff --git a/src/applications/harbormaster/engine/HarbormasterBuildEngine.php b/src/applications/harbormaster/engine/HarbormasterBuildEngine.php
index 36ca48b060..5eabd85034 100644
--- a/src/applications/harbormaster/engine/HarbormasterBuildEngine.php
+++ b/src/applications/harbormaster/engine/HarbormasterBuildEngine.php
@@ -1,497 +1,500 @@
<?php
/**
* Moves a build forward by queuing build tasks, canceling or restarting the
* build, or failing it in response to task failures.
*/
final class HarbormasterBuildEngine extends Phobject {
private $build;
private $viewer;
private $newBuildTargets = array();
private $forceBuildableUpdate;
public function setForceBuildableUpdate($force_buildable_update) {
$this->forceBuildableUpdate = $force_buildable_update;
return $this;
}
public function shouldForceBuildableUpdate() {
return $this->forceBuildableUpdate;
}
public function queueNewBuildTarget(HarbormasterBuildTarget $target) {
$this->newBuildTargets[] = $target;
return $this;
}
public function getNewBuildTargets() {
return $this->newBuildTargets;
}
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setBuild(HarbormasterBuild $build) {
$this->build = $build;
return $this;
}
public function getBuild() {
return $this->build;
}
public function continueBuild() {
$build = $this->getBuild();
$lock_key = 'harbormaster.build:'.$build->getID();
$lock = PhabricatorGlobalLock::newLock($lock_key)->lock(15);
$build->reload();
$old_status = $build->getBuildStatus();
try {
$this->updateBuild($build);
} catch (Exception $ex) {
// If any exception is raised, the build is marked as a failure and the
// exception is re-thrown (this ensures we don't leave builds in an
// inconsistent state).
$build->setBuildStatus(HarbormasterBuild::STATUS_ERROR);
$build->save();
$lock->unlock();
$this->releaseAllArtifacts($build);
throw $ex;
}
$lock->unlock();
// NOTE: We queue new targets after releasing the lock so that in-process
// execution via `bin/harbormaster` does not reenter the locked region.
foreach ($this->getNewBuildTargets() as $target) {
$task = PhabricatorWorker::scheduleTask(
'HarbormasterTargetWorker',
array(
'targetID' => $target->getID(),
+ ),
+ array(
+ 'objectPHID' => $target->getPHID(),
));
}
// If the build changed status, we might need to update the overall status
// on the buildable.
$new_status = $build->getBuildStatus();
if ($new_status != $old_status || $this->shouldForceBuildableUpdate()) {
$this->updateBuildable($build->getBuildable());
}
// If we are no longer building for any reason, release all artifacts.
if (!$build->isBuilding()) {
$this->releaseAllArtifacts($build);
}
}
private function updateBuild(HarbormasterBuild $build) {
if ($build->isAborting()) {
$this->releaseAllArtifacts($build);
$build->setBuildStatus(HarbormasterBuild::STATUS_ABORTED);
$build->save();
}
if (($build->getBuildStatus() == HarbormasterBuild::STATUS_PENDING) ||
($build->isRestarting())) {
$this->restartBuild($build);
$build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING);
$build->save();
}
if ($build->isResuming()) {
$build->setBuildStatus(HarbormasterBuild::STATUS_BUILDING);
$build->save();
}
if ($build->isPausing() && !$build->isComplete()) {
$build->setBuildStatus(HarbormasterBuild::STATUS_PAUSED);
$build->save();
}
$build->deleteUnprocessedCommands();
if ($build->getBuildStatus() == HarbormasterBuild::STATUS_BUILDING) {
$this->updateBuildSteps($build);
}
}
private function restartBuild(HarbormasterBuild $build) {
// We're restarting the build, so release all previous artifacts.
$this->releaseAllArtifacts($build);
// Increment the build generation counter on the build.
$build->setBuildGeneration($build->getBuildGeneration() + 1);
// Currently running targets should periodically check their build
// generation (which won't have changed) against the build's generation.
// If it is different, they will automatically stop what they're doing
// and abort.
// Previously we used to delete targets, logs and artifacts here. Instead
// leave them around so users can view previous generations of this build.
}
private function updateBuildSteps(HarbormasterBuild $build) {
$targets = id(new HarbormasterBuildTargetQuery())
->setViewer($this->getViewer())
->withBuildPHIDs(array($build->getPHID()))
->withBuildGenerations(array($build->getBuildGeneration()))
->execute();
$this->updateWaitingTargets($targets);
$targets = mgroup($targets, 'getBuildStepPHID');
$steps = id(new HarbormasterBuildStepQuery())
->setViewer($this->getViewer())
->withBuildPlanPHIDs(array($build->getBuildPlan()->getPHID()))
->execute();
// Identify steps which are in various states.
$queued = array();
$underway = array();
$waiting = array();
$complete = array();
$failed = array();
foreach ($steps as $step) {
$step_targets = idx($targets, $step->getPHID(), array());
if ($step_targets) {
$is_queued = false;
$is_underway = false;
foreach ($step_targets as $target) {
if ($target->isUnderway()) {
$is_underway = true;
break;
}
}
$is_waiting = false;
foreach ($step_targets as $target) {
if ($target->isWaiting()) {
$is_waiting = true;
break;
}
}
$is_complete = true;
foreach ($step_targets as $target) {
if (!$target->isComplete()) {
$is_complete = false;
break;
}
}
$is_failed = false;
foreach ($step_targets as $target) {
if ($target->isFailed()) {
$is_failed = true;
break;
}
}
} else {
$is_queued = true;
$is_underway = false;
$is_waiting = false;
$is_complete = false;
$is_failed = false;
}
if ($is_queued) {
$queued[$step->getPHID()] = true;
}
if ($is_underway) {
$underway[$step->getPHID()] = true;
}
if ($is_waiting) {
$waiting[$step->getPHID()] = true;
}
if ($is_complete) {
$complete[$step->getPHID()] = true;
}
if ($is_failed) {
$failed[$step->getPHID()] = true;
}
}
// If any step failed, fail the whole build, then bail.
if (count($failed)) {
$build->setBuildStatus(HarbormasterBuild::STATUS_FAILED);
$build->save();
return;
}
// If every step is complete, we're done with this build. Mark it passed
// and bail.
if (count($complete) == count($steps)) {
$build->setBuildStatus(HarbormasterBuild::STATUS_PASSED);
$build->save();
return;
}
// Identify all the steps which are ready to run (because all their
// dependencies are complete).
$runnable = array();
foreach ($steps as $step) {
$dependencies = $step->getStepImplementation()->getDependencies($step);
if (isset($queued[$step->getPHID()])) {
$can_run = true;
foreach ($dependencies as $dependency) {
if (empty($complete[$dependency])) {
$can_run = false;
break;
}
}
if ($can_run) {
$runnable[] = $step;
}
}
}
if (!$runnable && !$waiting && !$underway) {
// This means the build is deadlocked, and the user has configured
// circular dependencies.
$build->setBuildStatus(HarbormasterBuild::STATUS_DEADLOCKED);
$build->save();
return;
}
foreach ($runnable as $runnable_step) {
$target = HarbormasterBuildTarget::initializeNewBuildTarget(
$build,
$runnable_step,
$build->retrieveVariablesFromBuild());
$target->save();
$this->queueNewBuildTarget($target);
}
}
/**
* Process messages which were sent to these targets, kicking applicable
* targets out of "Waiting" and into either "Passed" or "Failed".
*
* @param list<HarbormasterBuildTarget> List of targets to process.
* @return void
*/
private function updateWaitingTargets(array $targets) {
assert_instances_of($targets, 'HarbormasterBuildTarget');
// We only care about messages for targets which are actually in a waiting
// state.
$waiting_targets = array();
foreach ($targets as $target) {
if ($target->isWaiting()) {
$waiting_targets[$target->getPHID()] = $target;
}
}
if (!$waiting_targets) {
return;
}
$messages = id(new HarbormasterBuildMessageQuery())
->setViewer($this->getViewer())
->withBuildTargetPHIDs(array_keys($waiting_targets))
->withConsumed(false)
->execute();
foreach ($messages as $message) {
$target = $waiting_targets[$message->getBuildTargetPHID()];
switch ($message->getType()) {
case HarbormasterMessageType::MESSAGE_PASS:
$new_status = HarbormasterBuildTarget::STATUS_PASSED;
break;
case HarbormasterMessageType::MESSAGE_FAIL:
$new_status = HarbormasterBuildTarget::STATUS_FAILED;
break;
case HarbormasterMessageType::MESSAGE_WORK:
default:
$new_status = null;
break;
}
if ($new_status !== null) {
$message->setIsConsumed(true);
$message->save();
$target->setTargetStatus($new_status);
if ($target->isComplete()) {
$target->setDateCompleted(PhabricatorTime::getNow());
}
$target->save();
}
}
}
/**
* Update the overall status of the buildable this build is attached to.
*
* After a build changes state (for example, passes or fails) it may affect
* the overall state of the associated buildable. Compute the new aggregate
* state and save it on the buildable.
*
* @param HarbormasterBuild The buildable to update.
* @return void
*/
private function updateBuildable(HarbormasterBuildable $buildable) {
$viewer = $this->getViewer();
$lock_key = 'harbormaster.buildable:'.$buildable->getID();
$lock = PhabricatorGlobalLock::newLock($lock_key)->lock(15);
$buildable = id(new HarbormasterBuildableQuery())
->setViewer($viewer)
->withIDs(array($buildable->getID()))
->needBuilds(true)
->executeOne();
$all_pass = true;
$any_fail = false;
foreach ($buildable->getBuilds() as $build) {
if ($build->getBuildStatus() != HarbormasterBuild::STATUS_PASSED) {
$all_pass = false;
}
if ($build->getBuildStatus() == HarbormasterBuild::STATUS_FAILED ||
$build->getBuildStatus() == HarbormasterBuild::STATUS_ERROR ||
$build->getBuildStatus() == HarbormasterBuild::STATUS_DEADLOCKED) {
$any_fail = true;
}
}
if ($any_fail) {
$new_status = HarbormasterBuildable::STATUS_FAILED;
} else if ($all_pass) {
$new_status = HarbormasterBuildable::STATUS_PASSED;
} else {
$new_status = HarbormasterBuildable::STATUS_BUILDING;
}
$old_status = $buildable->getBuildableStatus();
$did_update = ($old_status != $new_status);
if ($did_update) {
$buildable->setBuildableStatus($new_status);
$buildable->save();
}
$lock->unlock();
// If we changed the buildable status, try to post a transaction to the
// object about it. We can safely do this outside of the locked region.
// NOTE: We only post transactions for automatic buildables, not for
// manual ones: manual builds are test builds, whoever is doing tests
// can look at the results themselves, and other users generally don't
// care about the outcome.
$should_publish = $did_update &&
$new_status != HarbormasterBuildable::STATUS_BUILDING &&
!$buildable->getIsManualBuildable();
if (!$should_publish) {
return;
}
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs(array($buildable->getBuildablePHID()))
->executeOne();
if (!$object) {
return;
}
if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
return;
}
// TODO: Publishing these transactions is causing a race. See T8650.
// We shouldn't be publishing to diffs anyway.
if ($object instanceof DifferentialDiff) {
return;
}
$template = $object->getApplicationTransactionTemplate();
if (!$template) {
return;
}
$template
->setTransactionType(PhabricatorTransactions::TYPE_BUILDABLE)
->setMetadataValue(
'harbormaster:buildablePHID',
$buildable->getPHID())
->setOldValue($old_status)
->setNewValue($new_status);
$harbormaster_phid = id(new PhabricatorHarbormasterApplication())
->getPHID();
$daemon_source = PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_DAEMON,
array());
$editor = $object->getApplicationTransactionEditor()
->setActor($viewer)
->setActingAsPHID($harbormaster_phid)
->setContentSource($daemon_source)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$editor->applyTransactions(
$object->getApplicationTransactionObject(),
array($template));
}
private function releaseAllArtifacts(HarbormasterBuild $build) {
$targets = id(new HarbormasterBuildTargetQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withBuildPHIDs(array($build->getPHID()))
->withBuildGenerations(array($build->getBuildGeneration()))
->execute();
if (count($targets) === 0) {
return;
}
$target_phids = mpull($targets, 'getPHID');
$artifacts = id(new HarbormasterBuildArtifactQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withBuildTargetPHIDs($target_phids)
->execute();
foreach ($artifacts as $artifact) {
$artifact->releaseArtifact();
}
}
}
diff --git a/src/applications/harbormaster/storage/HarbormasterBuildable.php b/src/applications/harbormaster/storage/HarbormasterBuildable.php
index 3a7df73f52..6286072b42 100644
--- a/src/applications/harbormaster/storage/HarbormasterBuildable.php
+++ b/src/applications/harbormaster/storage/HarbormasterBuildable.php
@@ -1,338 +1,341 @@
<?php
final class HarbormasterBuildable extends HarbormasterDAO
implements
PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface,
HarbormasterBuildableInterface {
protected $buildablePHID;
protected $containerPHID;
protected $buildableStatus;
protected $isManualBuildable;
private $buildableObject = self::ATTACHABLE;
private $containerObject = self::ATTACHABLE;
private $buildableHandle = self::ATTACHABLE;
private $containerHandle = self::ATTACHABLE;
private $builds = self::ATTACHABLE;
const STATUS_BUILDING = 'building';
const STATUS_PASSED = 'passed';
const STATUS_FAILED = 'failed';
public static function getBuildableStatusName($status) {
switch ($status) {
case self::STATUS_BUILDING:
return pht('Building');
case self::STATUS_PASSED:
return pht('Passed');
case self::STATUS_FAILED:
return pht('Failed');
default:
return pht('Unknown');
}
}
public static function getBuildableStatusIcon($status) {
switch ($status) {
case self::STATUS_BUILDING:
return PHUIStatusItemView::ICON_RIGHT;
case self::STATUS_PASSED:
return PHUIStatusItemView::ICON_ACCEPT;
case self::STATUS_FAILED:
return PHUIStatusItemView::ICON_REJECT;
default:
return PHUIStatusItemView::ICON_QUESTION;
}
}
public static function getBuildableStatusColor($status) {
switch ($status) {
case self::STATUS_BUILDING:
return 'blue';
case self::STATUS_PASSED:
return 'green';
case self::STATUS_FAILED:
return 'red';
default:
return 'bluegrey';
}
}
public static function initializeNewBuildable(PhabricatorUser $actor) {
return id(new HarbormasterBuildable())
->setIsManualBuildable(0)
->setBuildableStatus(self::STATUS_BUILDING);
}
public function getMonogram() {
return 'B'.$this->getID();
}
/**
* Returns an existing buildable for the object's PHID or creates a
* new buildable implicitly if needed.
*/
public static function createOrLoadExisting(
PhabricatorUser $actor,
$buildable_object_phid,
$container_object_phid) {
$buildable = id(new HarbormasterBuildableQuery())
->setViewer($actor)
->withBuildablePHIDs(array($buildable_object_phid))
->withManualBuildables(false)
->setLimit(1)
->executeOne();
if ($buildable) {
return $buildable;
}
$buildable = self::initializeNewBuildable($actor)
->setBuildablePHID($buildable_object_phid)
->setContainerPHID($container_object_phid);
$buildable->save();
return $buildable;
}
/**
* Start builds for a given buildable.
*
* @param phid PHID of the object to build.
* @param phid Container PHID for the buildable.
* @param list<HarbormasterBuildRequest> List of builds to perform.
* @return void
*/
public static function applyBuildPlans(
$phid,
$container_phid,
array $requests) {
assert_instances_of($requests, 'HarbormasterBuildRequest');
if (!$requests) {
return;
}
// Skip all of this logic if the Harbormaster application
// isn't currently installed.
$harbormaster_app = 'PhabricatorHarbormasterApplication';
if (!PhabricatorApplication::isClassInstalled($harbormaster_app)) {
return;
}
$viewer = PhabricatorUser::getOmnipotentUser();
$buildable = self::createOrLoadExisting(
$viewer,
$phid,
$container_phid);
$plan_phids = mpull($requests, 'getBuildPlanPHID');
$plans = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withPHIDs($plan_phids)
->execute();
$plans = mpull($plans, null, 'getPHID');
foreach ($requests as $request) {
$plan_phid = $request->getBuildPlanPHID();
$plan = idx($plans, $plan_phid);
if (!$plan) {
throw new Exception(
pht(
'Failed to load build plan ("%s").',
$plan_phid));
}
if ($plan->isDisabled()) {
// TODO: This should be communicated more clearly -- maybe we should
// create the build but set the status to "disabled" or "derelict".
continue;
}
$parameters = $request->getBuildParameters();
$buildable->applyPlan($plan, $parameters, $request->getInitiatorPHID());
}
}
public function applyPlan(
HarbormasterBuildPlan $plan,
array $parameters,
$initiator_phid) {
$viewer = PhabricatorUser::getOmnipotentUser();
$build = HarbormasterBuild::initializeNewBuild($viewer)
->setBuildablePHID($this->getPHID())
->setBuildPlanPHID($plan->getPHID())
->setBuildParameters($parameters)
->setBuildStatus(HarbormasterBuild::STATUS_PENDING);
if ($initiator_phid) {
$build->setInitiatorPHID($initiator_phid);
}
$auto_key = $plan->getPlanAutoKey();
if ($auto_key) {
$build->setPlanAutoKey($auto_key);
}
$build->save();
PhabricatorWorker::scheduleTask(
'HarbormasterBuildWorker',
array(
'buildID' => $build->getID(),
+ ),
+ array(
+ 'objectPHID' => $build->getPHID(),
));
return $build;
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'containerPHID' => 'phid?',
'buildableStatus' => 'text32',
'isManualBuildable' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'key_buildable' => array(
'columns' => array('buildablePHID'),
),
'key_container' => array(
'columns' => array('containerPHID'),
),
'key_manual' => array(
'columns' => array('isManualBuildable'),
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
HarbormasterBuildablePHIDType::TYPECONST);
}
public function attachBuildableObject($buildable_object) {
$this->buildableObject = $buildable_object;
return $this;
}
public function getBuildableObject() {
return $this->assertAttached($this->buildableObject);
}
public function attachContainerObject($container_object) {
$this->containerObject = $container_object;
return $this;
}
public function getContainerObject() {
return $this->assertAttached($this->containerObject);
}
public function attachContainerHandle($container_handle) {
$this->containerHandle = $container_handle;
return $this;
}
public function getContainerHandle() {
return $this->assertAttached($this->containerHandle);
}
public function attachBuildableHandle($buildable_handle) {
$this->buildableHandle = $buildable_handle;
return $this;
}
public function getBuildableHandle() {
return $this->assertAttached($this->buildableHandle);
}
public function attachBuilds(array $builds) {
assert_instances_of($builds, 'HarbormasterBuild');
$this->builds = $builds;
return $this;
}
public function getBuilds() {
return $this->assertAttached($this->builds);
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new HarbormasterBuildableTransactionEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new HarbormasterBuildableTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
return $this->getBuildableObject()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getBuildableObject()->hasAutomaticCapability(
$capability,
$viewer);
}
public function describeAutomaticCapability($capability) {
return pht('A buildable inherits policies from the underlying object.');
}
/* -( HarbormasterBuildableInterface )------------------------------------- */
public function getHarbormasterBuildablePHID() {
// NOTE: This is essentially just for convenience, as it allows you create
// a copy of a buildable by specifying `B123` without bothering to go
// look up the underlying object.
return $this->getBuildablePHID();
}
public function getHarbormasterContainerPHID() {
return $this->getContainerPHID();
}
public function getBuildVariables() {
return array();
}
public function getAvailableBuildVariables() {
return array();
}
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Aug 15, 12:32 AM (3 d, 9 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
196582
Default Alt Text
(37 KB)

Event Timeline