Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php
index 7f63eda072..eadf4c94f5 100644
--- a/src/applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php
@@ -1,115 +1,98 @@
<?php
final class HarbormasterWaitForPreviousBuildStepImplementation
extends HarbormasterBuildStepImplementation {
public function getName() {
return pht('Wait for Previous Commits to Build');
}
public function getGenericDescription() {
return pht(
'Wait for previous commits to finish building the current plan '.
'before continuing.');
}
public function getBuildStepGroupKey() {
return HarbormasterPrototypeBuildStepGroup::GROUPKEY;
}
public function execute(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
// We can only wait when building against commits.
$buildable = $build->getBuildable();
$object = $buildable->getBuildableObject();
if (!($object instanceof PhabricatorRepositoryCommit)) {
return;
}
// Block until all previous builds of the same build plan have
// finished.
$plan = $build->getBuildPlan();
-
- $existing_logs = id(new HarbormasterBuildLogQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withBuildTargetPHIDs(array($build_target->getPHID()))
- ->execute();
-
- if ($existing_logs) {
- $log = head($existing_logs);
- } else {
- $log = $build->createLog($build_target, 'waiting', 'blockers');
- }
-
$blockers = $this->getBlockers($object, $plan, $build);
- if ($blockers) {
- $log->start();
- $log->append(pht("Blocked by: %s\n", implode(',', $blockers)));
- $log->finalize();
- }
if ($blockers) {
throw new PhabricatorWorkerYieldException(15);
}
}
private function getBlockers(
PhabricatorRepositoryCommit $commit,
HarbormasterBuildPlan $plan,
HarbormasterBuild $source) {
$call = new ConduitCall(
'diffusion.commitparentsquery',
array(
'commit' => $commit->getCommitIdentifier(),
'repository' => $commit->getRepository()->getPHID(),
));
$call->setUser(PhabricatorUser::getOmnipotentUser());
$parents = $call->execute();
$parents = id(new DiffusionCommitQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withRepository($commit->getRepository())
->withIdentifiers($parents)
->execute();
$blockers = array();
$build_objects = array();
foreach ($parents as $parent) {
if (!$parent->isImported()) {
$blockers[] = pht('Commit %s', $parent->getCommitIdentifier());
} else {
$build_objects[] = $parent->getPHID();
}
}
if ($build_objects) {
$buildables = id(new HarbormasterBuildableQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withBuildablePHIDs($build_objects)
->withManualBuildables(false)
->execute();
$buildable_phids = mpull($buildables, 'getPHID');
if ($buildable_phids) {
$builds = id(new HarbormasterBuildQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withBuildablePHIDs($buildable_phids)
->withBuildPlanPHIDs(array($plan->getPHID()))
->execute();
foreach ($builds as $build) {
if (!$build->isComplete()) {
$blockers[] = pht('Build %d', $build->getID());
}
}
}
}
return $blockers;
}
}
diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuild.php b/src/applications/harbormaster/storage/build/HarbormasterBuild.php
index 5134d0efa0..4d9278c7bf 100644
--- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php
@@ -1,542 +1,525 @@
<?php
final class HarbormasterBuild extends HarbormasterDAO
implements
PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface {
protected $buildablePHID;
protected $buildPlanPHID;
protected $buildStatus;
protected $buildGeneration;
protected $buildParameters = array();
protected $initiatorPHID;
protected $planAutoKey;
private $buildable = self::ATTACHABLE;
private $buildPlan = self::ATTACHABLE;
private $buildTargets = self::ATTACHABLE;
private $unprocessedCommands = self::ATTACHABLE;
/**
* Not currently being built.
*/
const STATUS_INACTIVE = 'inactive';
/**
* Pending pick up by the Harbormaster daemon.
*/
const STATUS_PENDING = 'pending';
/**
* Current building the buildable.
*/
const STATUS_BUILDING = 'building';
/**
* The build has passed.
*/
const STATUS_PASSED = 'passed';
/**
* The build has failed.
*/
const STATUS_FAILED = 'failed';
/**
* The build has aborted.
*/
const STATUS_ABORTED = 'aborted';
/**
* The build encountered an unexpected error.
*/
const STATUS_ERROR = 'error';
/**
* The build has been paused.
*/
const STATUS_PAUSED = 'paused';
/**
* The build has been deadlocked.
*/
const STATUS_DEADLOCKED = 'deadlocked';
/**
* Get a human readable name for a build status constant.
*
* @param const Build status constant.
* @return string Human-readable name.
*/
public static function getBuildStatusName($status) {
switch ($status) {
case self::STATUS_INACTIVE:
return pht('Inactive');
case self::STATUS_PENDING:
return pht('Pending');
case self::STATUS_BUILDING:
return pht('Building');
case self::STATUS_PASSED:
return pht('Passed');
case self::STATUS_FAILED:
return pht('Failed');
case self::STATUS_ABORTED:
return pht('Aborted');
case self::STATUS_ERROR:
return pht('Unexpected Error');
case self::STATUS_PAUSED:
return pht('Paused');
case self::STATUS_DEADLOCKED:
return pht('Deadlocked');
default:
return pht('Unknown');
}
}
public static function getBuildStatusIcon($status) {
switch ($status) {
case self::STATUS_INACTIVE:
case self::STATUS_PENDING:
return PHUIStatusItemView::ICON_OPEN;
case self::STATUS_BUILDING:
return PHUIStatusItemView::ICON_RIGHT;
case self::STATUS_PASSED:
return PHUIStatusItemView::ICON_ACCEPT;
case self::STATUS_FAILED:
return PHUIStatusItemView::ICON_REJECT;
case self::STATUS_ABORTED:
return PHUIStatusItemView::ICON_MINUS;
case self::STATUS_ERROR:
return PHUIStatusItemView::ICON_MINUS;
case self::STATUS_PAUSED:
return PHUIStatusItemView::ICON_MINUS;
case self::STATUS_DEADLOCKED:
return PHUIStatusItemView::ICON_WARNING;
default:
return PHUIStatusItemView::ICON_QUESTION;
}
}
public static function getBuildStatusColor($status) {
switch ($status) {
case self::STATUS_INACTIVE:
return 'dark';
case self::STATUS_PENDING:
case self::STATUS_BUILDING:
return 'blue';
case self::STATUS_PASSED:
return 'green';
case self::STATUS_FAILED:
case self::STATUS_ABORTED:
case self::STATUS_ERROR:
case self::STATUS_DEADLOCKED:
return 'red';
case self::STATUS_PAUSED:
return 'dark';
default:
return 'bluegrey';
}
}
public static function initializeNewBuild(PhabricatorUser $actor) {
return id(new HarbormasterBuild())
->setBuildStatus(self::STATUS_INACTIVE)
->setBuildGeneration(0);
}
public function delete() {
$this->openTransaction();
$this->deleteUnprocessedCommands();
$result = parent::delete();
$this->saveTransaction();
return $result;
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'buildParameters' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'buildStatus' => 'text32',
'buildGeneration' => 'uint32',
'planAutoKey' => 'text32?',
'initiatorPHID' => 'phid?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_buildable' => array(
'columns' => array('buildablePHID'),
),
'key_plan' => array(
'columns' => array('buildPlanPHID'),
),
'key_status' => array(
'columns' => array('buildStatus'),
),
'key_planautokey' => array(
'columns' => array('buildablePHID', 'planAutoKey'),
'unique' => true,
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
HarbormasterBuildPHIDType::TYPECONST);
}
public function attachBuildable(HarbormasterBuildable $buildable) {
$this->buildable = $buildable;
return $this;
}
public function getBuildable() {
return $this->assertAttached($this->buildable);
}
public function getName() {
if ($this->getBuildPlan()) {
return $this->getBuildPlan()->getName();
}
return pht('Build');
}
public function attachBuildPlan(
HarbormasterBuildPlan $build_plan = null) {
$this->buildPlan = $build_plan;
return $this;
}
public function getBuildPlan() {
return $this->assertAttached($this->buildPlan);
}
public function getBuildTargets() {
return $this->assertAttached($this->buildTargets);
}
public function attachBuildTargets(array $targets) {
$this->buildTargets = $targets;
return $this;
}
public function isBuilding() {
return $this->getBuildStatus() === self::STATUS_PENDING ||
$this->getBuildStatus() === self::STATUS_BUILDING;
}
public function isAutobuild() {
return ($this->getPlanAutoKey() !== null);
}
- public function createLog(
- HarbormasterBuildTarget $build_target,
- $log_source,
- $log_type) {
-
- $log_source = id(new PhutilUTF8StringTruncator())
- ->setMaximumBytes(250)
- ->truncateString($log_source);
-
- $log = HarbormasterBuildLog::initializeNewBuildLog($build_target)
- ->setLogSource($log_source)
- ->setLogType($log_type)
- ->save();
-
- return $log;
- }
-
public function retrieveVariablesFromBuild() {
$results = array(
'buildable.diff' => null,
'buildable.revision' => null,
'buildable.commit' => null,
'repository.callsign' => null,
'repository.phid' => null,
'repository.vcs' => null,
'repository.uri' => null,
'step.timestamp' => null,
'build.id' => null,
'initiator.phid' => null,
);
foreach ($this->getBuildParameters() as $key => $value) {
$results['build/'.$key] = $value;
}
$buildable = $this->getBuildable();
$object = $buildable->getBuildableObject();
$object_variables = $object->getBuildVariables();
$results = $object_variables + $results;
$results['step.timestamp'] = time();
$results['build.id'] = $this->getID();
$results['initiator.phid'] = $this->getInitiatorPHID();
return $results;
}
public static function getAvailableBuildVariables() {
$objects = id(new PhutilClassMapQuery())
->setAncestorClass('HarbormasterBuildableInterface')
->execute();
$variables = array();
$variables[] = array(
'step.timestamp' => pht('The current UNIX timestamp.'),
'build.id' => pht('The ID of the current build.'),
'target.phid' => pht('The PHID of the current build target.'),
'initiator.phid' => pht(
'The PHID of the user or Object that initiated the build, '.
'if applicable.'),
);
foreach ($objects as $object) {
$variables[] = $object->getAvailableBuildVariables();
}
$variables = array_mergev($variables);
return $variables;
}
public function isComplete() {
switch ($this->getBuildStatus()) {
case self::STATUS_PASSED:
case self::STATUS_FAILED:
case self::STATUS_ABORTED:
case self::STATUS_ERROR:
case self::STATUS_PAUSED:
return true;
}
return false;
}
public function isPaused() {
return ($this->getBuildStatus() == self::STATUS_PAUSED);
}
public function getURI() {
$id = $this->getID();
return "/harbormaster/build/{$id}/";
}
/* -( Build Commands )----------------------------------------------------- */
private function getUnprocessedCommands() {
return $this->assertAttached($this->unprocessedCommands);
}
public function attachUnprocessedCommands(array $commands) {
$this->unprocessedCommands = $commands;
return $this;
}
public function canRestartBuild() {
if ($this->isAutobuild()) {
return false;
}
return !$this->isRestarting();
}
public function canPauseBuild() {
if ($this->isAutobuild()) {
return false;
}
return !$this->isComplete() &&
!$this->isPaused() &&
!$this->isPausing();
}
public function canAbortBuild() {
if ($this->isAutobuild()) {
return false;
}
return !$this->isComplete();
}
public function canResumeBuild() {
if ($this->isAutobuild()) {
return false;
}
return $this->isPaused() &&
!$this->isResuming();
}
public function isPausing() {
$is_pausing = false;
foreach ($this->getUnprocessedCommands() as $command_object) {
$command = $command_object->getCommand();
switch ($command) {
case HarbormasterBuildCommand::COMMAND_PAUSE:
$is_pausing = true;
break;
case HarbormasterBuildCommand::COMMAND_RESUME:
case HarbormasterBuildCommand::COMMAND_RESTART:
$is_pausing = false;
break;
case HarbormasterBuildCommand::COMMAND_ABORT:
$is_pausing = true;
break;
}
}
return $is_pausing;
}
public function isResuming() {
$is_resuming = false;
foreach ($this->getUnprocessedCommands() as $command_object) {
$command = $command_object->getCommand();
switch ($command) {
case HarbormasterBuildCommand::COMMAND_RESTART:
case HarbormasterBuildCommand::COMMAND_RESUME:
$is_resuming = true;
break;
case HarbormasterBuildCommand::COMMAND_PAUSE:
$is_resuming = false;
break;
case HarbormasterBuildCommand::COMMAND_ABORT:
$is_resuming = false;
break;
}
}
return $is_resuming;
}
public function isRestarting() {
$is_restarting = false;
foreach ($this->getUnprocessedCommands() as $command_object) {
$command = $command_object->getCommand();
switch ($command) {
case HarbormasterBuildCommand::COMMAND_RESTART:
$is_restarting = true;
break;
}
}
return $is_restarting;
}
public function isAborting() {
$is_aborting = false;
foreach ($this->getUnprocessedCommands() as $command_object) {
$command = $command_object->getCommand();
switch ($command) {
case HarbormasterBuildCommand::COMMAND_ABORT:
$is_aborting = true;
break;
}
}
return $is_aborting;
}
public function deleteUnprocessedCommands() {
foreach ($this->getUnprocessedCommands() as $key => $command_object) {
$command_object->delete();
unset($this->unprocessedCommands[$key]);
}
return $this;
}
public function canIssueCommand(PhabricatorUser $viewer, $command) {
try {
$this->assertCanIssueCommand($viewer, $command);
return true;
} catch (Exception $ex) {
return false;
}
}
public function assertCanIssueCommand(PhabricatorUser $viewer, $command) {
$need_edit = false;
switch ($command) {
case HarbormasterBuildCommand::COMMAND_RESTART:
break;
case HarbormasterBuildCommand::COMMAND_PAUSE:
case HarbormasterBuildCommand::COMMAND_RESUME:
case HarbormasterBuildCommand::COMMAND_ABORT:
$need_edit = true;
break;
default:
throw new Exception(
pht(
'Invalid Harbormaster build command "%s".',
$command));
}
// Issuing these commands requires that you be able to edit the build, to
// prevent enemy engineers from sabotaging your builds. See T9614.
if ($need_edit) {
PhabricatorPolicyFilter::requireCapability(
$viewer,
$this->getBuildPlan(),
PhabricatorPolicyCapability::CAN_EDIT);
}
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new HarbormasterBuildTransactionEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new HarbormasterBuildTransaction();
}
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->getBuildable()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getBuildable()->hasAutomaticCapability(
$capability,
$viewer);
}
public function describeAutomaticCapability($capability) {
return pht('A build inherits policies from its buildable.');
}
}
diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildLog.php b/src/applications/harbormaster/storage/build/HarbormasterBuildLog.php
index 85e7ae2411..8b7c5c85bb 100644
--- a/src/applications/harbormaster/storage/build/HarbormasterBuildLog.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuildLog.php
@@ -1,220 +1,221 @@
<?php
-final class HarbormasterBuildLog extends HarbormasterDAO
+final class HarbormasterBuildLog
+ extends HarbormasterDAO
implements PhabricatorPolicyInterface {
protected $buildTargetPHID;
protected $logSource;
protected $logType;
protected $duration;
protected $live;
private $buildTarget = self::ATTACHABLE;
- private $start;
const CHUNK_BYTE_LIMIT = 102400;
/**
* The log is encoded as plain text.
*/
const ENCODING_TEXT = 'text';
public function __destruct() {
- if ($this->start) {
- $this->finalize($this->start);
+ if ($this->getLive()) {
+ $this->closeBuildLog();
}
}
public static function initializeNewBuildLog(
HarbormasterBuildTarget $build_target) {
return id(new HarbormasterBuildLog())
->setBuildTargetPHID($build_target->getPHID())
->setDuration(null)
->setLive(0);
}
+ public function openBuildLog() {
+ if ($this->getLive()) {
+ throw new Exception(pht('This build log is already open!'));
+ }
+
+ return $this
+ ->setLive(1)
+ ->save();
+ }
+
+ public function closeBuildLog() {
+ if (!$this->getLive()) {
+ throw new Exception(pht('This build log is not open!'));
+ }
+
+ // TODO: Encode the log contents in a gzipped format.
+
+ $this->reload();
+
+ $start = $this->getDateCreated();
+ $now = PhabricatorTime::getNow();
+
+ return $this
+ ->setDuration($now - $start)
+ ->setLive(0)
+ ->save();
+ }
+
+
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
// T6203/NULLABILITY
// It seems like these should be non-nullable? All logs should have a
// source, etc.
'logSource' => 'text255?',
'logType' => 'text255?',
'duration' => 'uint32?',
'live' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'key_buildtarget' => array(
'columns' => array('buildTargetPHID'),
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
HarbormasterBuildLogPHIDType::TYPECONST);
}
public function attachBuildTarget(HarbormasterBuildTarget $build_target) {
$this->buildTarget = $build_target;
return $this;
}
public function getBuildTarget() {
return $this->assertAttached($this->buildTarget);
}
public function getName() {
return pht('Build Log');
}
- public function start() {
- if ($this->getLive()) {
- throw new Exception(
- pht('Live logging has already started for this log.'));
- }
-
- $this->setLive(1);
- $this->save();
-
- $this->start = PhabricatorTime::getNow();
-
- return time();
- }
-
public function append($content) {
if (!$this->getLive()) {
- throw new Exception(
- pht('Start logging before appending data to the log.'));
+ throw new PhutilInvalidStateException('openBuildLog');
}
- if (strlen($content) === 0) {
+
+ $content = (string)$content;
+ if (!strlen($content)) {
return;
}
// If the length of the content is greater than the chunk size limit,
// then we can never fit the content in a single record. We need to
// split our content out and call append on it for as many parts as there
// are to the content.
if (strlen($content) > self::CHUNK_BYTE_LIMIT) {
$current = $content;
while (strlen($current) > self::CHUNK_BYTE_LIMIT) {
$part = substr($current, 0, self::CHUNK_BYTE_LIMIT);
$current = substr($current, self::CHUNK_BYTE_LIMIT);
$this->append($part);
}
$this->append($current);
return;
}
// Retrieve the size of last chunk from the DB for this log. If the
// chunk is over 500K, then we need to create a new log entry.
$conn = $this->establishConnection('w');
$result = queryfx_all(
$conn,
'SELECT id, size, encoding '.
'FROM harbormaster_buildlogchunk '.
'WHERE logID = %d '.
'ORDER BY id DESC '.
'LIMIT 1',
$this->getID());
if (count($result) === 0 ||
$result[0]['size'] + strlen($content) > self::CHUNK_BYTE_LIMIT ||
$result[0]['encoding'] !== self::ENCODING_TEXT) {
// We must insert a new chunk because the data we are appending
// won't fit into the existing one, or we don't have any existing
// chunk data.
queryfx(
$conn,
'INSERT INTO harbormaster_buildlogchunk '.
'(logID, encoding, size, chunk) '.
'VALUES '.
'(%d, %s, %d, %B)',
$this->getID(),
self::ENCODING_TEXT,
strlen($content),
$content);
} else {
// We have a resulting record that we can append our content onto.
queryfx(
$conn,
'UPDATE harbormaster_buildlogchunk '.
'SET chunk = CONCAT(chunk, %B), size = LENGTH(CONCAT(chunk, %B))'.
'WHERE id = %d',
$content,
$content,
$result[0]['id']);
}
}
- public function finalize($start = 0) {
- if (!$this->getLive()) {
- // TODO: Clean up this API.
- return;
- }
-
- // TODO: Encode the log contents in a gzipped format.
- $this->reload();
- if ($start > 0) {
- $this->setDuration(time() - $start);
- }
- $this->setLive(0);
- $this->save();
- }
-
public function getLogText() {
// TODO: This won't cope very well if we're pulling like a 700MB
// log file out of the DB. We should probably implement some sort
// of optional limit parameter so that when we're rendering out only
// 25 lines in the UI, we don't wastefully read in the whole log.
// We have to read our content out of the database and stitch all of
// the log data back together.
$conn = $this->establishConnection('r');
$result = queryfx_all(
$conn,
'SELECT chunk '.
'FROM harbormaster_buildlogchunk '.
'WHERE logID = %d '.
'ORDER BY id ASC',
$this->getID());
$content = '';
foreach ($result as $row) {
$content .= $row['chunk'];
}
return $content;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
return $this->getBuildTarget()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getBuildTarget()->hasAutomaticCapability(
$capability,
$viewer);
}
public function describeAutomaticCapability($capability) {
return pht(
"Users must be able to see a build target to view it's build log.");
}
}
diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
index 27655189e6..8b47bdfc21 100644
--- a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
@@ -1,358 +1,357 @@
<?php
final class HarbormasterBuildTarget extends HarbormasterDAO
implements PhabricatorPolicyInterface {
protected $name;
protected $buildPHID;
protected $buildStepPHID;
protected $className;
protected $details;
protected $variables;
protected $targetStatus;
protected $dateStarted;
protected $dateCompleted;
protected $buildGeneration;
const STATUS_PENDING = 'target/pending';
const STATUS_BUILDING = 'target/building';
const STATUS_WAITING = 'target/waiting';
const STATUS_PASSED = 'target/passed';
const STATUS_FAILED = 'target/failed';
const STATUS_ABORTED = 'target/aborted';
private $build = self::ATTACHABLE;
private $buildStep = self::ATTACHABLE;
private $implementation;
public static function getBuildTargetStatusName($status) {
switch ($status) {
case self::STATUS_PENDING:
return pht('Pending');
case self::STATUS_BUILDING:
return pht('Building');
case self::STATUS_WAITING:
return pht('Waiting for Message');
case self::STATUS_PASSED:
return pht('Passed');
case self::STATUS_FAILED:
return pht('Failed');
case self::STATUS_ABORTED:
return pht('Aborted');
default:
return pht('Unknown');
}
}
public static function getBuildTargetStatusIcon($status) {
switch ($status) {
case self::STATUS_PENDING:
return PHUIStatusItemView::ICON_OPEN;
case self::STATUS_BUILDING:
case self::STATUS_WAITING:
return PHUIStatusItemView::ICON_RIGHT;
case self::STATUS_PASSED:
return PHUIStatusItemView::ICON_ACCEPT;
case self::STATUS_FAILED:
return PHUIStatusItemView::ICON_REJECT;
case self::STATUS_ABORTED:
return PHUIStatusItemView::ICON_MINUS;
default:
return PHUIStatusItemView::ICON_QUESTION;
}
}
public static function getBuildTargetStatusColor($status) {
switch ($status) {
case self::STATUS_PENDING:
case self::STATUS_BUILDING:
case self::STATUS_WAITING:
return 'blue';
case self::STATUS_PASSED:
return 'green';
case self::STATUS_FAILED:
case self::STATUS_ABORTED:
return 'red';
default:
return 'bluegrey';
}
}
public static function initializeNewBuildTarget(
HarbormasterBuild $build,
HarbormasterBuildStep $build_step,
array $variables) {
return id(new HarbormasterBuildTarget())
->setName($build_step->getName())
->setBuildPHID($build->getPHID())
->setBuildStepPHID($build_step->getPHID())
->setClassName($build_step->getClassName())
->setDetails($build_step->getDetails())
->setTargetStatus(self::STATUS_PENDING)
->setVariables($variables)
->setBuildGeneration($build->getBuildGeneration());
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'details' => self::SERIALIZATION_JSON,
'variables' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'className' => 'text255',
'targetStatus' => 'text64',
'dateStarted' => 'epoch?',
'dateCompleted' => 'epoch?',
'buildGeneration' => 'uint32',
// T6203/NULLABILITY
// This should not be nullable.
'name' => 'text255?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_build' => array(
'columns' => array('buildPHID', 'buildStepPHID'),
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
HarbormasterBuildTargetPHIDType::TYPECONST);
}
public function attachBuild(HarbormasterBuild $build) {
$this->build = $build;
return $this;
}
public function getBuild() {
return $this->assertAttached($this->build);
}
public function attachBuildStep(HarbormasterBuildStep $step = null) {
$this->buildStep = $step;
return $this;
}
public function getBuildStep() {
return $this->assertAttached($this->buildStep);
}
public function getDetail($key, $default = null) {
return idx($this->details, $key, $default);
}
public function setDetail($key, $value) {
$this->details[$key] = $value;
return $this;
}
public function getVariables() {
return parent::getVariables() + $this->getBuildTargetVariables();
}
public function getVariable($key, $default = null) {
return idx($this->variables, $key, $default);
}
public function setVariable($key, $value) {
$this->variables[$key] = $value;
return $this;
}
public function getImplementation() {
if ($this->implementation === null) {
$obj = HarbormasterBuildStepImplementation::requireImplementation(
$this->className);
$obj->loadSettings($this);
$this->implementation = $obj;
}
return $this->implementation;
}
public function isAutotarget() {
try {
return (bool)$this->getImplementation()->getBuildStepAutotargetPlanKey();
} catch (Exception $e) {
return false;
}
}
public function getName() {
if (strlen($this->name) && !$this->isAutotarget()) {
return $this->name;
}
try {
return $this->getImplementation()->getName();
} catch (Exception $e) {
return $this->getClassName();
}
}
private function getBuildTargetVariables() {
return array(
'target.phid' => $this->getPHID(),
);
}
public function createArtifact(
PhabricatorUser $actor,
$artifact_key,
$artifact_type,
array $artifact_data) {
$impl = HarbormasterArtifact::getArtifactType($artifact_type);
if (!$impl) {
throw new Exception(
pht(
'There is no implementation available for artifacts of type "%s".',
$artifact_type));
}
$impl->validateArtifactData($artifact_data);
$artifact = HarbormasterBuildArtifact::initializeNewBuildArtifact($this)
->setArtifactKey($artifact_key)
->setArtifactType($artifact_type)
->setArtifactData($artifact_data);
$impl = $artifact->getArtifactImplementation();
$impl->willCreateArtifact($actor);
return $artifact->save();
}
public function loadArtifact($artifact_key) {
$indexes = array();
$indexes[] = HarbormasterBuildArtifact::getArtifactIndex(
$this,
$artifact_key);
$artifact = id(new HarbormasterBuildArtifactQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withArtifactIndexes($indexes)
->executeOne();
if ($artifact === null) {
throw new Exception(
pht(
'Artifact "%s" not found!',
$artifact_key));
}
return $artifact;
}
public function newLog($log_source, $log_type) {
$log_source = id(new PhutilUTF8StringTruncator())
->setMaximumBytes(250)
->truncateString($log_source);
$log = HarbormasterBuildLog::initializeNewBuildLog($this)
->setLogSource($log_source)
- ->setLogType($log_type);
-
- $log->start();
+ ->setLogType($log_type)
+ ->openBuildLog();
return $log;
}
public function getFieldValue($key) {
$field_list = PhabricatorCustomField::getObjectFields(
$this->getBuildStep(),
PhabricatorCustomField::ROLE_VIEW);
$fields = $field_list->getFields();
$full_key = "std:harbormaster:core:{$key}";
$field = idx($fields, $full_key);
if (!$field) {
throw new Exception(
pht(
'Unknown build step field "%s"!',
$key));
}
$field = clone $field;
$field->setValueFromStorage($this->getDetail($key));
return $field->getBuildTargetFieldValue();
}
/* -( Status )------------------------------------------------------------- */
public function isComplete() {
switch ($this->getTargetStatus()) {
case self::STATUS_PASSED:
case self::STATUS_FAILED:
case self::STATUS_ABORTED:
return true;
}
return false;
}
public function isFailed() {
switch ($this->getTargetStatus()) {
case self::STATUS_FAILED:
case self::STATUS_ABORTED:
return true;
}
return false;
}
public function isWaiting() {
switch ($this->getTargetStatus()) {
case self::STATUS_WAITING:
return true;
}
return false;
}
public function isUnderway() {
switch ($this->getTargetStatus()) {
case self::STATUS_PENDING:
case self::STATUS_BUILDING:
return true;
}
return false;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
return $this->getBuild()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getBuild()->hasAutomaticCapability(
$capability,
$viewer);
}
public function describeAutomaticCapability($capability) {
return pht('Users must be able to see a build to view its build targets.');
}
}
diff --git a/src/applications/harbormaster/worker/HarbormasterTargetWorker.php b/src/applications/harbormaster/worker/HarbormasterTargetWorker.php
index ac3014dc29..fa3a01272b 100644
--- a/src/applications/harbormaster/worker/HarbormasterTargetWorker.php
+++ b/src/applications/harbormaster/worker/HarbormasterTargetWorker.php
@@ -1,115 +1,112 @@
<?php
/**
* Execute a build target.
*/
final class HarbormasterTargetWorker extends HarbormasterWorker {
public function getRequiredLeaseTime() {
// This worker performs actual build work, which may involve a long wait
// on external systems.
return phutil_units('24 hours in seconds');
}
public function renderForDisplay(PhabricatorUser $viewer) {
try {
$target = $this->loadBuildTarget();
} catch (Exception $ex) {
return null;
}
return $viewer->renderHandle($target->getPHID());
}
private function loadBuildTarget() {
$data = $this->getTaskData();
$id = idx($data, 'targetID');
$target = id(new HarbormasterBuildTargetQuery())
->withIDs(array($id))
->setViewer($this->getViewer())
->needBuildSteps(true)
->executeOne();
if (!$target) {
throw new PhabricatorWorkerPermanentFailureException(
pht(
'Bad build target ID "%d".',
$id));
}
return $target;
}
protected function doWork() {
$target = $this->loadBuildTarget();
$build = $target->getBuild();
$viewer = $this->getViewer();
$target->setDateStarted(time());
try {
if ($target->getBuildGeneration() !== $build->getBuildGeneration()) {
throw new HarbormasterBuildAbortedException();
}
$status_pending = HarbormasterBuildTarget::STATUS_PENDING;
if ($target->getTargetStatus() == $status_pending) {
$target->setTargetStatus(HarbormasterBuildTarget::STATUS_BUILDING);
$target->save();
}
$implementation = $target->getImplementation();
$implementation->setCurrentWorkerTaskID($this->getCurrentWorkerTaskID());
$implementation->execute($build, $target);
$next_status = HarbormasterBuildTarget::STATUS_PASSED;
if ($implementation->shouldWaitForMessage($target)) {
$next_status = HarbormasterBuildTarget::STATUS_WAITING;
}
$target->setTargetStatus($next_status);
if ($target->isComplete()) {
$target->setDateCompleted(PhabricatorTime::getNow());
}
$target->save();
} catch (PhabricatorWorkerYieldException $ex) {
// If the target wants to yield, let that escape without further
// processing. We'll resume after the task retries.
throw $ex;
} catch (HarbormasterBuildFailureException $ex) {
// A build step wants to fail explicitly.
$target->setTargetStatus(HarbormasterBuildTarget::STATUS_FAILED);
$target->setDateCompleted(PhabricatorTime::getNow());
$target->save();
} catch (HarbormasterBuildAbortedException $ex) {
// A build step is aborting because the build has been restarted.
$target->setTargetStatus(HarbormasterBuildTarget::STATUS_ABORTED);
$target->setDateCompleted(PhabricatorTime::getNow());
$target->save();
} catch (Exception $ex) {
- phlog($ex);
-
try {
- $log = $build->createLog($target, 'core', 'exception');
- $start = $log->start();
- $log->append((string)$ex);
- $log->finalize($start);
+ $log = $target->newLog('core', 'exception')
+ ->append($ex)
+ ->closeBuildLog();
} catch (Exception $log_ex) {
phlog($log_ex);
}
$target->setTargetStatus(HarbormasterBuildTarget::STATUS_FAILED);
$target->setDateCompleted(time());
$target->save();
}
id(new HarbormasterBuildEngine())
->setViewer($viewer)
->setBuild($build)
->continueBuild();
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jun 10, 8:29 AM (21 h, 33 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
140307
Default Alt Text
(38 KB)

Event Timeline