Changeset View
Changeset View
Standalone View
Standalone View
src/applications/harbormaster/engine/HarbormasterBuildEngine.php
<?php | <?php | ||||
/** | /** | ||||
* Moves a build forward by queuing build tasks, canceling or restarting the | * Moves a build forward by queuing build tasks, canceling or restarting the | ||||
* build, or failing it in response to task failures. | * build, or failing it in response to task failures. | ||||
*/ | */ | ||||
final class HarbormasterBuildEngine extends Phobject { | final class HarbormasterBuildEngine extends Phobject { | ||||
private $build; | private $build; | ||||
private $viewer; | private $viewer; | ||||
private $newBuildTargets = array(); | private $newBuildTargets = array(); | ||||
private $artifactReleaseQueue = array(); | private $artifactReleaseQueue = array(); | ||||
private $forceBuildableUpdate; | private $forceBuildableUpdate; | ||||
private $consumedNewMessages = false; | |||||
public function setForceBuildableUpdate($force_buildable_update) { | public function setForceBuildableUpdate($force_buildable_update) { | ||||
$this->forceBuildableUpdate = $force_buildable_update; | $this->forceBuildableUpdate = $force_buildable_update; | ||||
return $this; | return $this; | ||||
} | } | ||||
public function shouldForceBuildableUpdate() { | public function shouldForceBuildableUpdate() { | ||||
return $this->forceBuildableUpdate; | return $this->forceBuildableUpdate; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | foreach ($this->getNewBuildTargets() as $target) { | ||||
array( | array( | ||||
'objectPHID' => $target->getPHID(), | 'objectPHID' => $target->getPHID(), | ||||
)); | )); | ||||
} | } | ||||
// If the build changed status, we might need to update the overall status | // If the build changed status, we might need to update the overall status | ||||
// on the buildable. | // on the buildable. | ||||
$new_status = $build->getBuildStatus(); | $new_status = $build->getBuildStatus(); | ||||
if ($new_status != $old_status || $this->shouldForceBuildableUpdate()) { | if ($new_status != $old_status || | ||||
$this->consumedNewMessages || | |||||
$this->shouldForceBuildableUpdate()) { | |||||
$this->updateBuildable($build->getBuildable()); | $this->updateBuildable($build->getBuildable()); | ||||
} | } | ||||
$this->releaseQueuedArtifacts(); | $this->releaseQueuedArtifacts(); | ||||
// If we are no longer building for any reason, release all artifacts. | // If we are no longer building for any reason, release all artifacts. | ||||
if (!$build->isBuilding()) { | if (!$build->isBuilding()) { | ||||
$this->releaseAllArtifacts($build); | $this->releaseAllArtifacts($build); | ||||
▲ Show 20 Lines • Show All 267 Lines • ▼ Show 20 Lines | private function updateWaitingTargets(array $targets) { | ||||
// We only care about messages for targets which are actually in a waiting | // We only care about messages for targets which are actually in a waiting | ||||
// state. | // state. | ||||
$waiting_targets = array(); | $waiting_targets = array(); | ||||
foreach ($targets as $target) { | foreach ($targets as $target) { | ||||
if ($target->isWaiting()) { | if ($target->isWaiting()) { | ||||
$waiting_targets[$target->getPHID()] = $target; | $waiting_targets[$target->getPHID()] = $target; | ||||
} | } | ||||
} | } | ||||
if (!$waiting_targets) { | if (!$waiting_targets) { | ||||
return; | return; | ||||
} | } | ||||
$messages = id(new HarbormasterBuildMessageQuery()) | $messages = id(new HarbormasterBuildMessageQuery()) | ||||
->setViewer($this->getViewer()) | ->setViewer($this->getViewer()) | ||||
->withBuildTargetPHIDs(array_keys($waiting_targets)) | ->withBuildTargetPHIDs(array_keys($waiting_targets)) | ||||
->withConsumed(false) | ->withConsumed(false) | ||||
->execute(); | ->execute(); | ||||
$updated_targets = array(); | |||||
foreach ($messages as $message) { | foreach ($messages as $message) { | ||||
$target = $waiting_targets[$message->getBuildTargetPHID()]; | $target = $waiting_targets[$message->getBuildTargetPHID()]; | ||||
switch ($message->getType()) { | switch ($message->getType()) { | ||||
case HarbormasterMessageType::MESSAGE_PASS: | case HarbormasterMessageType::MESSAGE_PASS: | ||||
$new_status = HarbormasterBuildTarget::STATUS_PASSED; | $new_status = HarbormasterBuildTarget::STATUS_PASSED; | ||||
break; | break; | ||||
case HarbormasterMessageType::MESSAGE_FAIL: | case HarbormasterMessageType::MESSAGE_FAIL: | ||||
$new_status = HarbormasterBuildTarget::STATUS_FAILED; | $new_status = HarbormasterBuildTarget::STATUS_FAILED; | ||||
break; | break; | ||||
case HarbormasterMessageType::MESSAGE_WORK: | case HarbormasterMessageType::MESSAGE_WORK: | ||||
default: | default: | ||||
$new_status = null; | $new_status = null; | ||||
break; | break; | ||||
} | } | ||||
if ($new_status !== null) { | if ($new_status !== null) { | ||||
$message->setIsConsumed(true); | |||||
$message->save(); | |||||
$target->setTargetStatus($new_status); | $target->setTargetStatus($new_status); | ||||
if ($target->isComplete()) { | if ($target->isComplete()) { | ||||
$target->setDateCompleted(PhabricatorTime::getNow()); | $target->setDateCompleted(PhabricatorTime::getNow()); | ||||
} | } | ||||
$target->save(); | $target->save(); | ||||
} | } | ||||
$this->consumedNewMessages = true; | |||||
$message->setIsConsumed(true); | |||||
$message->save(); | |||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Update the overall status of the buildable this build is attached to. | * 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 | * After a build changes state (for example, passes or fails) it may affect | ||||
* the overall state of the associated buildable. Compute the new aggregate | * the overall state of the associated buildable. Compute the new aggregate | ||||
* state and save it on the buildable. | * state and save it on the buildable. | ||||
* | * | ||||
* @param HarbormasterBuild The buildable to update. | * @param HarbormasterBuild The buildable to update. | ||||
* @return void | * @return void | ||||
*/ | */ | ||||
private function updateBuildable(HarbormasterBuildable $buildable) { | private function updateBuildable(HarbormasterBuildable $buildable) { | ||||
$viewer = $this->getViewer(); | $viewer = $this->getViewer(); | ||||
$lock_key = 'harbormaster.buildable:'.$buildable->getID(); | $lock_key = 'harbormaster.buildable:'.$buildable->getID(); | ||||
$lock = PhabricatorGlobalLock::newLock($lock_key)->lock(15); | $lock = PhabricatorGlobalLock::newLock($lock_key)->lock(15); | ||||
$buildable = id(new HarbormasterBuildableQuery()) | $buildable = id(new HarbormasterBuildableQuery()) | ||||
->setViewer($viewer) | ->setViewer($viewer) | ||||
->withIDs(array($buildable->getID())) | ->withIDs(array($buildable->getID())) | ||||
->needBuilds(true) | ->needBuilds(true) | ||||
->needTargets(true) | |||||
->executeOne(); | ->executeOne(); | ||||
// This doesn't effect the `should_publish` state. | |||||
$this->updateLintAndUnitStatus($buildable); | |||||
$all_pass = true; | $all_pass = true; | ||||
$any_fail = false; | $any_fail = false; | ||||
foreach ($buildable->getBuilds() as $build) { | foreach ($buildable->getBuilds() as $build) { | ||||
if ($build->getBuildStatus() != HarbormasterBuildStatus::STATUS_PASSED) { | if ($build->getBuildStatus() != HarbormasterBuildStatus::STATUS_PASSED) { | ||||
$all_pass = false; | $all_pass = false; | ||||
} | } | ||||
$any_fail = in_array($build->getBuildStatus(), array( | $any_fail = in_array($build->getBuildStatus(), array( | ||||
HarbormasterBuildStatus::STATUS_FAILED, | HarbormasterBuildStatus::STATUS_FAILED, | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | $editor = $object->getApplicationTransactionEditor() | ||||
->setContinueOnNoEffect(true) | ->setContinueOnNoEffect(true) | ||||
->setContinueOnMissingFields(true); | ->setContinueOnMissingFields(true); | ||||
$editor->applyTransactions( | $editor->applyTransactions( | ||||
$object->getApplicationTransactionObject(), | $object->getApplicationTransactionObject(), | ||||
array($template)); | array($template)); | ||||
} | } | ||||
/** | |||||
* Update lint and unit aggregated information. | |||||
* Invoked in the context of the Buildable lock. | |||||
*/ | |||||
private function updateLintAndUnitStatus(HarbormasterBuildable $buildable) { | |||||
$targets = array_mergev(mpull($buildable->getBuilds(), 'getBuildTargets')); | |||||
$target_phids = mpull($targets, 'getPHID'); | |||||
$buildable_lints = id(new HarbormasterBuildLintMessage())->loadAllWhere( | |||||
'buildTargetPHID IN (%Ls)', | |||||
$target_phids); | |||||
$buildable_units = id(new HarbormasterBuildUnitMessage())->loadAllWhere( | |||||
'buildTargetPHID IN (%Ls)', | |||||
$target_phids); | |||||
if ($buildable_lints) { | |||||
$severities = mpull($buildable_lints, 'getSeverity'); | |||||
$lint_severity = HarbormasterLintStatus::summarizeStatuses($severities); | |||||
} else { | |||||
$lint_severity = null; | |||||
} | |||||
if ($buildable_units) { | |||||
$coverage_map = array(); | |||||
foreach ($buildable_units as $unit) { | |||||
$coverage = $unit->getProperty('coverage', array()); | |||||
foreach ($coverage as $path => $coverage_data) { | |||||
$coverage_map[$path][] = $coverage_data; | |||||
} | |||||
} | |||||
$groups = mgroup($buildable_units, 'getResult'); | |||||
$unit_counts = array(); | |||||
foreach ($groups as $status => $group) { | |||||
$unit_counts[$status] = count($group); | |||||
} | |||||
$statuses = array_keys($unit_counts); | |||||
$unit_result = HarbormasterUnitStatus::summarizeStatuses($statuses); | |||||
foreach ($coverage_map as $path => $items) { | |||||
$coverage_map[$path] = ArcanistUnitTestResult::mergeCoverage($items); | |||||
} | |||||
} else { | |||||
$unit_result = null; | |||||
$coverage_map = null; | |||||
$unit_counts = null; | |||||
} | |||||
$buildable->setDetail( | |||||
HarbormasterBuildable::DETAIL_LINT_STATUS, | |||||
$lint_severity); | |||||
$buildable->setDetail( | |||||
HarbormasterBuildable::DETAIL_UNIT_STATUS, | |||||
$unit_result); | |||||
$buildable->setDetail( | |||||
HarbormasterBuildable::DETAIL_UNIT_COUNTS, | |||||
$unit_counts); | |||||
$buildable->setDetail( | |||||
HarbormasterBuildable::DETAIL_COVERAGE_MAP, | |||||
$coverage_map); | |||||
$buildable->save(); | |||||
} | |||||
private function releaseAllArtifacts(HarbormasterBuild $build) { | private function releaseAllArtifacts(HarbormasterBuild $build) { | ||||
$targets = id(new HarbormasterBuildTargetQuery()) | $targets = id(new HarbormasterBuildTargetQuery()) | ||||
->setViewer(PhabricatorUser::getOmnipotentUser()) | ->setViewer(PhabricatorUser::getOmnipotentUser()) | ||||
->withBuildPHIDs(array($build->getPHID())) | ->withBuildPHIDs(array($build->getPHID())) | ||||
->withBuildGenerations(array($build->getBuildGeneration())) | ->withBuildGenerations(array($build->getBuildGeneration())) | ||||
->execute(); | ->execute(); | ||||
if (count($targets) === 0) { | if (count($targets) === 0) { | ||||
Show All 23 Lines |