Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
index b5bdec1030..5ed96d8c40 100644
--- a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
+++ b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
@@ -1,236 +1,293 @@
<?php
final class HarbormasterBuildableViewController
extends HarbormasterController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $this->id;
$buildable = id(new HarbormasterBuildableQuery())
->setViewer($viewer)
->withIDs(array($id))
->needBuildableHandles(true)
->needContainerHandles(true)
- ->needBuilds(true)
->executeOne();
if (!$buildable) {
return new Aphront404Response();
}
- $build_list = id(new PHUIObjectItemListView())
- ->setUser($viewer);
- foreach ($buildable->getBuilds() as $build) {
- $view_uri = $this->getApplicationURI('/build/'.$build->getID().'/');
- $item = id(new PHUIObjectItemView())
- ->setObjectName(pht('Build %d', $build->getID()))
- ->setHeader($build->getName())
- ->setHref($view_uri);
-
- switch ($build->getBuildStatus()) {
- case HarbormasterBuild::STATUS_INACTIVE:
- $item->setBarColor('grey');
- $item->addAttribute(pht('Inactive'));
- break;
- case HarbormasterBuild::STATUS_PENDING:
- $item->setBarColor('blue');
- $item->addAttribute(pht('Pending'));
- break;
- case HarbormasterBuild::STATUS_WAITING:
- $item->setBarColor('violet');
- $item->addAttribute(pht('Waiting'));
- break;
- case HarbormasterBuild::STATUS_BUILDING:
- $item->setBarColor('yellow');
- $item->addAttribute(pht('Building'));
- break;
- case HarbormasterBuild::STATUS_PASSED:
- $item->setBarColor('green');
- $item->addAttribute(pht('Passed'));
- break;
- case HarbormasterBuild::STATUS_FAILED:
- $item->setBarColor('red');
- $item->addAttribute(pht('Failed'));
- break;
- case HarbormasterBuild::STATUS_ERROR:
- $item->setBarColor('red');
- $item->addAttribute(pht('Unexpected Error'));
- break;
- case HarbormasterBuild::STATUS_STOPPED:
- $item->setBarColor('black');
- $item->addAttribute(pht('Stopped'));
- break;
- }
-
- if ($build->isRestarting()) {
- $item->addIcon('backward', pht('Restarting'));
- } else if ($build->isStopping()) {
- $item->addIcon('stop', pht('Stopping'));
- } else if ($build->isResuming()) {
- $item->addIcon('play', pht('Resuming'));
- }
-
- $build_id = $build->getID();
-
- $restart_uri = "build/restart/{$build_id}/buildable/";
- $resume_uri = "build/resume/{$build_id}/buildable/";
- $stop_uri = "build/stop/{$build_id}/buildable/";
-
- $item->addAction(
- id(new PHUIListItemView())
- ->setIcon('backward')
- ->setName(pht('Restart'))
- ->setHref($this->getApplicationURI($restart_uri))
- ->setWorkflow(true)
- ->setDisabled(!$build->canRestartBuild()));
+ // Pull builds and build targets.
+ $builds = id(new HarbormasterBuildQuery())
+ ->setViewer($viewer)
+ ->withBuildablePHIDs(array($buildable->getPHID()))
+ ->needBuildTargets(true)
+ ->execute();
- if ($build->canResumeBuild()) {
- $item->addAction(
- id(new PHUIListItemView())
- ->setIcon('play')
- ->setName(pht('Resume'))
- ->setHref($this->getApplicationURI($resume_uri))
- ->setWorkflow(true));
- } else {
- $item->addAction(
- id(new PHUIListItemView())
- ->setIcon('stop')
- ->setName(pht('Stop'))
- ->setHref($this->getApplicationURI($stop_uri))
- ->setWorkflow(true)
- ->setDisabled(!$build->canStopBuild()));
- }
+ $buildable->attachBuilds($builds);
- $build_list->addItem($item);
- }
+ $build_list = $this->buildBuildList($buildable);
$title = pht("Buildable %d", $id);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($viewer)
->setPolicyObject($buildable);
$box = id(new PHUIObjectBoxView())
->setHeader($header);
$actions = $this->buildActionList($buildable);
$this->buildPropertyLists($box, $buildable, $actions);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb("B{$id}");
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$build_list,
),
array(
'title' => $title,
'device' => true,
));
}
private function buildActionList(HarbormasterBuildable $buildable) {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $buildable->getID();
$list = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($buildable)
->setObjectURI($buildable->getMonogram());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$buildable,
PhabricatorPolicyCapability::CAN_EDIT);
$can_restart = false;
$can_resume = false;
$can_stop = false;
foreach ($buildable->getBuilds() as $build) {
if ($build->canRestartBuild()) {
$can_restart = true;
}
if ($build->canResumeBuild()) {
$can_resume = true;
}
if ($build->canStopBuild()) {
$can_stop = true;
}
}
$restart_uri = "buildable/{$id}/restart/";
$stop_uri = "buildable/{$id}/stop/";
$resume_uri = "buildable/{$id}/resume/";
$list->addAction(
id(new PhabricatorActionView())
->setIcon('backward')
->setName(pht('Restart All Builds'))
->setHref($this->getApplicationURI($restart_uri))
->setWorkflow(true)
->setDisabled(!$can_restart || !$can_edit));
$list->addAction(
id(new PhabricatorActionView())
->setIcon('stop')
->setName(pht('Stop All Builds'))
->setHref($this->getApplicationURI($stop_uri))
->setWorkflow(true)
->setDisabled(!$can_stop || !$can_edit));
$list->addAction(
id(new PhabricatorActionView())
->setIcon('play')
->setName(pht('Resume All Builds'))
->setHref($this->getApplicationURI($resume_uri))
->setWorkflow(true)
->setDisabled(!$can_resume || !$can_edit));
return $list;
}
private function buildPropertyLists(
PHUIObjectBoxView $box,
HarbormasterBuildable $buildable,
PhabricatorActionListView $actions) {
$request = $this->getRequest();
$viewer = $request->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($buildable)
->setActionList($actions);
$box->addPropertyList($properties);
$properties->addProperty(
pht('Buildable'),
$buildable->getBuildableHandle()->renderLink());
if ($buildable->getContainerHandle() !== null) {
$properties->addProperty(
pht('Container'),
$buildable->getContainerHandle()->renderLink());
}
$properties->addProperty(
pht('Origin'),
$buildable->getIsManualBuildable()
? pht('Manual Buildable')
: pht('Automatic Buildable'));
}
+ private function buildBuildList(HarbormasterBuildable $buildable) {
+ $viewer = $this->getRequest()->getUser();
+
+ $build_list = id(new PHUIObjectItemListView())
+ ->setUser($viewer);
+ foreach ($buildable->getBuilds() as $build) {
+ $view_uri = $this->getApplicationURI('/build/'.$build->getID().'/');
+ $item = id(new PHUIObjectItemView())
+ ->setObjectName(pht('Build %d', $build->getID()))
+ ->setHeader($build->getName())
+ ->setHref($view_uri);
+
+ switch ($build->getBuildStatus()) {
+ case HarbormasterBuild::STATUS_INACTIVE:
+ $item->setBarColor('grey');
+ $item->addAttribute(pht('Inactive'));
+ break;
+ case HarbormasterBuild::STATUS_PENDING:
+ $item->setBarColor('blue');
+ $item->addAttribute(pht('Pending'));
+ break;
+ case HarbormasterBuild::STATUS_WAITING:
+ $item->setBarColor('violet');
+ $item->addAttribute(pht('Waiting'));
+ break;
+ case HarbormasterBuild::STATUS_BUILDING:
+ $item->setBarColor('yellow');
+ $item->addAttribute(pht('Building'));
+ break;
+ case HarbormasterBuild::STATUS_PASSED:
+ $item->setBarColor('green');
+ $item->addAttribute(pht('Passed'));
+ break;
+ case HarbormasterBuild::STATUS_FAILED:
+ $item->setBarColor('red');
+ $item->addAttribute(pht('Failed'));
+ break;
+ case HarbormasterBuild::STATUS_ERROR:
+ $item->setBarColor('red');
+ $item->addAttribute(pht('Unexpected Error'));
+ break;
+ case HarbormasterBuild::STATUS_STOPPED:
+ $item->setBarColor('black');
+ $item->addAttribute(pht('Stopped'));
+ break;
+ }
+
+ if ($build->isRestarting()) {
+ $item->addIcon('backward', pht('Restarting'));
+ } else if ($build->isStopping()) {
+ $item->addIcon('stop', pht('Stopping'));
+ } else if ($build->isResuming()) {
+ $item->addIcon('play', pht('Resuming'));
+ }
+
+ $build_id = $build->getID();
+
+ $restart_uri = "build/restart/{$build_id}/buildable/";
+ $resume_uri = "build/resume/{$build_id}/buildable/";
+ $stop_uri = "build/stop/{$build_id}/buildable/";
+
+ $item->addAction(
+ id(new PHUIListItemView())
+ ->setIcon('backward')
+ ->setName(pht('Restart'))
+ ->setHref($this->getApplicationURI($restart_uri))
+ ->setWorkflow(true)
+ ->setDisabled(!$build->canRestartBuild()));
+
+ if ($build->canResumeBuild()) {
+ $item->addAction(
+ id(new PHUIListItemView())
+ ->setIcon('play')
+ ->setName(pht('Resume'))
+ ->setHref($this->getApplicationURI($resume_uri))
+ ->setWorkflow(true));
+ } else {
+ $item->addAction(
+ id(new PHUIListItemView())
+ ->setIcon('stop')
+ ->setName(pht('Stop'))
+ ->setHref($this->getApplicationURI($stop_uri))
+ ->setWorkflow(true)
+ ->setDisabled(!$build->canStopBuild()));
+ }
+
+ $targets = $build->getBuildTargets();
+
+ if ($targets) {
+ $target_list = id(new PHUIStatusListView());
+ foreach ($targets as $target) {
+ switch ($target->getTargetStatus()) {
+ case HarbormasterBuildTarget::STATUS_PENDING:
+ $icon = 'time-green';
+ break;
+ case HarbormasterBuildTarget::STATUS_PASSED:
+ $icon = 'accept-green';
+ break;
+ case HarbormasterBuildTarget::STATUS_FAILED:
+ $icon = 'reject-red';
+ break;
+ default:
+ $icon = 'question';
+ break;
+ }
+
+ try {
+ $impl = $target->getImplementation();
+ $name = $impl->getName();
+ } catch (Exception $ex) {
+ $name = $target->getClassName();
+ }
+
+ $target_list->addItem(
+ id(new PHUIStatusItemView())
+ ->setIcon($icon)
+ ->setTarget(pht('Target %d', $target->getID()))
+ ->setNote($name));
+ }
+
+ $target_box = id(new PHUIBoxView())
+ ->addPadding(PHUI::PADDING_SMALL)
+ ->appendChild($target_list);
+
+ $item->appendChild($target_box);
+ }
+
+ $build_list->addItem($item);
+ }
+
+ return $build_list;
+ }
+
}
diff --git a/src/applications/harbormaster/controller/HarbormasterStepAddController.php b/src/applications/harbormaster/controller/HarbormasterStepAddController.php
index 3fe6d2c1ab..0e0eb0538d 100644
--- a/src/applications/harbormaster/controller/HarbormasterStepAddController.php
+++ b/src/applications/harbormaster/controller/HarbormasterStepAddController.php
@@ -1,88 +1,77 @@
<?php
final class HarbormasterStepAddController
extends HarbormasterController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$this->requireApplicationCapability(
HarbormasterCapabilityManagePlans::CAPABILITY);
$id = $this->id;
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
- if ($plan === null) {
- throw new Exception("Build plan not found!");
+ if (!$plan) {
+ return new Aphront404Response();
}
- $implementations =
- HarbormasterBuildStepImplementation::getImplementations();
-
$cancel_uri = $this->getApplicationURI('plan/'.$plan->getID().'/');
if ($request->isDialogFormPost()) {
$class = $request->getStr('step-type');
- if (!in_array($class, $implementations)) {
- return $this->createDialog($implementations, $cancel_uri);
+ if (!HarbormasterBuildStepImplementation::getImplementation($class)) {
+ return $this->createDialog($cancel_uri);
}
$steps = $plan->loadOrderedBuildSteps();
$step = new HarbormasterBuildStep();
$step->setBuildPlanPHID($plan->getPHID());
$step->setClassName($class);
$step->setDetails(array());
$step->setSequence(count($steps) + 1);
$step->save();
$edit_uri = $this->getApplicationURI("step/edit/".$step->getID()."/");
return id(new AphrontRedirectResponse())->setURI($edit_uri);
}
- return $this->createDialog($implementations, $cancel_uri);
+ return $this->createDialog($cancel_uri);
}
- function createDialog(array $implementations, $cancel_uri) {
+ private function createDialog($cancel_uri) {
$request = $this->getRequest();
$viewer = $request->getUser();
$control = id(new AphrontFormRadioButtonControl())
->setName('step-type');
- foreach ($implementations as $implementation_name) {
- $implementation = new $implementation_name();
- $control
- ->addButton(
- $implementation_name,
- $implementation->getName(),
- $implementation->getGenericDescription());
+ $all = HarbormasterBuildStepImplementation::getImplementations();
+ foreach ($all as $class => $implementation) {
+ $control->addButton(
+ $class,
+ $implementation->getName(),
+ $implementation->getGenericDescription());
}
- $dialog = new AphrontDialogView();
- $dialog->setTitle(pht('Add New Step'))
- ->setUser($viewer)
- ->addSubmitButton(pht('Add Build Step'))
- ->addCancelButton($cancel_uri);
- $dialog->appendChild(
- phutil_tag(
- 'p',
- array(),
- pht(
- 'Select what type of build step you want to add: ')));
- $dialog->appendChild($control);
- return id(new AphrontDialogResponse())->setDialog($dialog);
+ return $this->newDialog()
+ ->setTitle(pht('Add New Step'))
+ ->addSubmitButton(pht('Add Build Step'))
+ ->addCancelButton($cancel_uri)
+ ->appendParagraph(pht('Choose a type of build step to add:'))
+ ->appendChild($control);
}
}
diff --git a/src/applications/harbormaster/query/HarbormasterBuildQuery.php b/src/applications/harbormaster/query/HarbormasterBuildQuery.php
index 930cd07b83..1fdab9cdef 100644
--- a/src/applications/harbormaster/query/HarbormasterBuildQuery.php
+++ b/src/applications/harbormaster/query/HarbormasterBuildQuery.php
@@ -1,155 +1,179 @@
<?php
final class HarbormasterBuildQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $buildStatuses;
private $buildablePHIDs;
private $buildPlanPHIDs;
+ private $needBuildTargets;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withBuildStatuses(array $build_statuses) {
$this->buildStatuses = $build_statuses;
return $this;
}
public function withBuildablePHIDs(array $buildable_phids) {
$this->buildablePHIDs = $buildable_phids;
return $this;
}
public function withBuildPlanPHIDs(array $build_plan_phids) {
$this->buildPlanPHIDs = $build_plan_phids;
return $this;
}
+ public function needBuildTargets($need_targets) {
+ $this->needBuildTargets = $need_targets;
+ return $this;
+ }
+
protected function loadPage() {
$table = new HarbormasterBuild();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
protected function willFilterPage(array $page) {
$buildables = array();
$buildable_phids = array_filter(mpull($page, 'getBuildablePHID'));
if ($buildable_phids) {
$buildables = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->withPHIDs($buildable_phids)
->setParentQuery($this)
->execute();
$buildables = mpull($buildables, null, 'getPHID');
}
foreach ($page as $key => $build) {
$buildable_phid = $build->getBuildablePHID();
if (empty($buildables[$buildable_phid])) {
unset($page[$key]);
continue;
}
$build->attachBuildable($buildables[$buildable_phid]);
}
return $page;
}
protected function didFilterPage(array $page) {
$plans = array();
$plan_phids = array_filter(mpull($page, 'getBuildPlanPHID'));
if ($plan_phids) {
$plans = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->withPHIDs($plan_phids)
->setParentQuery($this)
->execute();
$plans = mpull($plans, null, 'getPHID');
}
foreach ($page as $key => $build) {
$plan_phid = $build->getBuildPlanPHID();
$build->attachBuildPlan(idx($plans, $plan_phid));
}
$build_phids = mpull($page, 'getPHID');
$commands = id(new HarbormasterBuildCommand())->loadAllWhere(
'targetPHID IN (%Ls) ORDER BY id ASC',
$build_phids);
$commands = mgroup($commands, 'getTargetPHID');
foreach ($page as $build) {
$unprocessed_commands = idx($commands, $build->getPHID(), array());
$build->attachUnprocessedCommands($unprocessed_commands);
}
+ if ($this->needBuildTargets) {
+ $targets = id(new HarbormasterBuildTargetQuery())
+ ->setViewer($this->getViewer())
+ ->setParentQuery($this)
+ ->withBuildPHIDs($build_phids)
+ ->execute();
+
+ // TODO: Some day, when targets have dependencies, we should toposort
+ // these. For now, just put them into chronological order.
+ $targets = array_reverse($targets);
+
+ $targets = mgroup($targets, 'getBuildPHID');
+ foreach ($page as $build) {
+ $build_targets = idx($targets, $build->getPHID(), array());
+ $build->attachBuildTargets($build_targets);
+ }
+ }
+
return $page;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid in (%Ls)',
$this->phids);
}
if ($this->buildStatuses) {
$where[] = qsprintf(
$conn_r,
'buildStatus in (%Ls)',
$this->buildStatuses);
}
if ($this->buildablePHIDs) {
$where[] = qsprintf(
$conn_r,
'buildablePHID IN (%Ls)',
$this->buildablePHIDs);
}
if ($this->buildPlanPHIDs) {
$where[] = qsprintf(
$conn_r,
'buildPlanPHID IN (%Ls)',
$this->buildPlanPHIDs);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
public function getQueryApplicationClass() {
return 'PhabricatorApplicationHarbormaster';
}
}
diff --git a/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php
index 5d47aee406..4ba72c09b7 100644
--- a/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php
@@ -1,166 +1,199 @@
<?php
abstract class HarbormasterBuildStepImplementation {
public static function getImplementations() {
- $symbols = id(new PhutilSymbolLoader())
+ return id(new PhutilSymbolLoader())
->setAncestorClass('HarbormasterBuildStepImplementation')
- ->setConcreteOnly(true)
- ->selectAndLoadSymbols();
- return ipull($symbols, 'name');
+ ->loadObjects();
+ }
+
+ public static function getImplementation($class) {
+ $base = idx(self::getImplementations(), $class);
+
+ if ($base) {
+ return (clone $base);
+ }
+
+ return null;
+ }
+
+ public static function requireImplementation($class) {
+ if (!$class) {
+ throw new Exception(pht('No implementation is specified!'));
+ }
+
+ $implementation = self::getImplementation($class);
+ if (!$implementation) {
+ throw new Exception(pht('No such implementation "%s" exists!', $class));
+ }
+
+ return $implementation;
}
/**
* The name of the implementation.
*/
abstract public function getName();
/**
* The generic description of the implementation.
*/
public function getGenericDescription() {
return '';
}
/**
* The description of the implementation, based on the current settings.
*/
public function getDescription() {
return $this->getGenericDescription();
}
/**
* Run the build target against the specified build.
*/
abstract public function execute(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target);
/**
* Gets the settings for this build step.
*/
public function getSettings() {
return $this->settings;
}
public function getSetting($key, $default = null) {
return idx($this->settings, $key, $default);
}
/**
* Loads the settings for this build step implementation from a build
* step or target.
*/
public final function loadSettings($build_object) {
$this->settings = $build_object->getDetails();
return $this;
}
/**
* Return the name of artifacts produced by this command.
*
* Something like:
*
* return array(
* 'some_name_input_by_user' => 'host');
*
* Future steps will calculate all available artifact mappings
* before them and filter on the type.
*
* @return array The mappings of artifact names to their types.
*/
public function getArtifactInputs() {
return array();
}
public function getArtifactOutputs() {
return array();
}
/**
* Returns a list of all artifacts made available by previous build steps.
*/
public static function loadAvailableArtifacts(
HarbormasterBuildPlan $build_plan,
HarbormasterBuildStep $current_build_step,
$artifact_type) {
$build_steps = $build_plan->loadOrderedBuildSteps();
return self::getAvailableArtifacts(
$build_plan,
$build_steps,
$current_build_step,
$artifact_type);
}
/**
* Returns a list of all artifacts made available by previous build steps.
*/
public static function getAvailableArtifacts(
HarbormasterBuildPlan $build_plan,
array $build_steps,
HarbormasterBuildStep $current_build_step,
$artifact_type) {
$previous_implementations = array();
foreach ($build_steps as $build_step) {
if ($build_step->getPHID() === $current_build_step->getPHID()) {
break;
}
$previous_implementations[] = $build_step->getStepImplementation();
}
$artifact_arrays = mpull($previous_implementations, 'getArtifactOutputs');
$artifacts = array();
foreach ($artifact_arrays as $array) {
$array = ipull($array, 'type', 'key');
foreach ($array as $name => $type) {
if ($type !== $artifact_type && $artifact_type !== null) {
continue;
}
$artifacts[$name] = $type;
}
}
return $artifacts;
}
/**
* Convert a user-provided string with variables in it, like:
*
* ls ${dirname}
*
* ...into a string with variables merged into it safely:
*
* ls 'dir with spaces'
*
* @param string Name of a `vxsprintf` function, like @{function:vcsprintf}.
* @param string User-provided pattern string containing `${variables}`.
* @param dict List of available replacement variables.
* @return string String with variables replaced safely into it.
*/
protected function mergeVariables($function, $pattern, array $variables) {
$regexp = '/\\$\\{(?P<name>[a-z\\.]+)\\}/';
$matches = null;
preg_match_all($regexp, $pattern, $matches);
$argv = array();
foreach ($matches['name'] as $name) {
if (!array_key_exists($name, $variables)) {
throw new Exception(pht("No such variable '%s'!", $name));
}
$argv[] = $variables[$name];
}
$pattern = str_replace('%', '%%', $pattern);
$pattern = preg_replace($regexp, '%s', $pattern);
return call_user_func($function, $pattern, $argv);
}
public function getFieldSpecifications() {
return array();
}
+ protected function formatSettingForDescription($key, $default = null) {
+ return $this->formatValueForDescription($this->getSetting($key, $default));
+ }
+
+ protected function formatValueForDescription($value) {
+ if (strlen($value)) {
+ return phutil_tag('strong', array(), $value);
+ } else {
+ return phutil_tag('em', array(), pht('(null)'));
+ }
+ }
+
}
diff --git a/src/applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php
index d21ba09a1e..a0d481b63b 100644
--- a/src/applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php
@@ -1,102 +1,100 @@
<?php
final class HarbormasterCommandBuildStepImplementation
extends HarbormasterBuildStepImplementation {
public function getName() {
return pht('Run Command');
}
public function getGenericDescription() {
return pht('Run a command on Drydock host.');
}
public function getDescription() {
- $settings = $this->getSettings();
-
return pht(
- 'Run \'%s\' on \'%s\'.',
- $settings['command'],
- $settings['hostartifact']);
+ 'Run command %s on host %s.',
+ $this->formatSettingForDescription('command'),
+ $this->formatSettingForDescription('hostartifact'));
}
public function execute(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
$settings = $this->getSettings();
$variables = $build_target->getVariables();
$command = $this->mergeVariables(
'vcsprintf',
$settings['command'],
$variables);
$artifact = $build->loadArtifact($settings['hostartifact']);
$lease = $artifact->loadDrydockLease();
$interface = $lease->getInterface('command');
$future = $interface->getExecFuture('%C', $command);
$log_stdout = $build->createLog($build_target, "remote", "stdout");
$log_stderr = $build->createLog($build_target, "remote", "stderr");
$start_stdout = $log_stdout->start();
$start_stderr = $log_stderr->start();
// Read the next amount of available output every second.
while (!$future->isReady()) {
list($stdout, $stderr) = $future->read();
$log_stdout->append($stdout);
$log_stderr->append($stderr);
$future->discardBuffers();
// Wait one second before querying for more data.
sleep(1);
}
// Get the return value so we can log that as well.
list($err) = $future->resolve();
// Retrieve the last few bits of information.
list($stdout, $stderr) = $future->read();
$log_stdout->append($stdout);
$log_stderr->append($stderr);
$future->discardBuffers();
$log_stdout->finalize($start_stdout);
$log_stderr->finalize($start_stderr);
if ($err) {
throw new Exception(pht('Command failed with error %d.', $err));
}
}
public function getArtifactInputs() {
return array(
array(
'name' => pht('Run on Host'),
'key' => $this->getSetting('hostartifact'),
'type' => HarbormasterBuildArtifact::TYPE_HOST,
),
);
}
public function getFieldSpecifications() {
return array(
'command' => array(
'name' => pht('Command'),
'type' => 'text',
'required' => true,
),
'hostartifact' => array(
'name' => pht('Host'),
'type' => 'text',
'required' => true,
),
);
}
}
diff --git a/src/applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php
index b34ea35608..5af731d81f 100644
--- a/src/applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php
@@ -1,67 +1,72 @@
<?php
final class HarbormasterHTTPRequestBuildStepImplementation
extends HarbormasterBuildStepImplementation {
public function getName() {
return pht('Make HTTP Request');
}
public function getGenericDescription() {
return pht('Make an HTTP request.');
}
public function getDescription() {
- $settings = $this->getSettings();
+ $domain = null;
+ $uri = $this->getSetting('uri');
+ if ($uri) {
+ $domain = id(new PhutilURI($uri))->getDomain();
+ }
- $uri = new PhutilURI($settings['uri']);
- $domain = $uri->getDomain();
- return pht('Make an HTTP %s request to %s', $settings['method'], $domain);
+ return pht(
+ 'Make an HTTP %s request to %s.',
+ $this->formatSettingForDescription('method', 'POST'),
+ $this->formatValueForDescription($domain));
}
public function execute(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
$settings = $this->getSettings();
$variables = $build_target->getVariables();
$uri = $this->mergeVariables(
'vurisprintf',
$settings['uri'],
$variables);
$log_body = $build->createLog($build_target, $uri, 'http-body');
$start = $log_body->start();
$method = nonempty(idx($settings, 'method'), 'POST');
list($status, $body, $headers) = id(new HTTPSFuture($uri))
->setMethod($method)
->setTimeout(60)
->resolve();
$log_body->append($body);
$log_body->finalize($start);
if ($status->getStatusCode() != 200) {
$build->setBuildStatus(HarbormasterBuild::STATUS_FAILED);
}
}
public function getFieldSpecifications() {
return array(
'uri' => array(
'name' => pht('URI'),
'type' => 'text',
'required' => true,
),
'method' => array(
'name' => pht('HTTP Method'),
'type' => 'select',
'options' => array_fuse(array('POST', 'GET', 'PUT', 'DELETE')),
),
);
}
}
diff --git a/src/applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php
index a42f3ae079..90efde27ec 100644
--- a/src/applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php
@@ -1,79 +1,69 @@
<?php
final class HarbormasterLeaseHostBuildStepImplementation
extends HarbormasterBuildStepImplementation {
public function getName() {
return pht('Lease Host');
}
public function getGenericDescription() {
return pht('Obtain a lease on a Drydock host for performing builds.');
}
- public function getDescription() {
- $settings = $this->getSettings();
-
- return pht(
- 'Obtain a lease on a Drydock host whose platform is \'%s\' and store '.
- 'the resulting lease in a host artifact called \'%s\'.',
- $settings['platform'],
- $settings['name']);
- }
-
public function execute(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
$settings = $this->getSettings();
// Create the lease.
$lease = id(new DrydockLease())
->setResourceType('host')
->setAttributes(
array(
'platform' => $settings['platform'],
))
->queueForActivation();
// Wait until the lease is fulfilled.
// TODO: This will throw an exception if the lease can't be fulfilled;
// we should treat that as build failure not build error.
$lease->waitUntilActive();
// Create the associated artifact.
$artifact = $build->createArtifact(
$build_target,
$settings['name'],
HarbormasterBuildArtifact::TYPE_HOST);
$artifact->setArtifactData(array(
'drydock-lease' => $lease->getID()));
$artifact->save();
}
public function getArtifactOutputs() {
return array(
array(
'name' => pht('Leased Host'),
'key' => $this->getSetting('name'),
'type' => HarbormasterBuildArtifact::TYPE_HOST,
),
);
}
public function getFieldSpecifications() {
return array(
'name' => array(
'name' => pht('Artifact Name'),
'type' => 'text',
'required' => true,
),
'platform' => array(
'name' => pht('Platform'),
'type' => 'text',
'required' => true,
),
);
}
}
diff --git a/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php
index e1dda2bff8..be007cad0f 100644
--- a/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php
@@ -1,85 +1,83 @@
<?php
final class HarbormasterPublishFragmentBuildStepImplementation
extends HarbormasterBuildStepImplementation {
public function getName() {
return pht('Publish Fragment');
}
public function getGenericDescription() {
return pht('Publish a fragment based on a file artifact.');
}
public function getDescription() {
- $settings = $this->getSettings();
-
return pht(
- 'Publish file artifact \'%s\' to the fragment path \'%s\'.',
- $settings['artifact'],
- $settings['path']);
+ 'Publish file artifact %s as fragment %s.',
+ $this->formatSettingForDescription('artifact'),
+ $this->formatSettingForDescription('path'));
}
public function execute(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
$settings = $this->getSettings();
$variables = $build_target->getVariables();
$path = $this->mergeVariables(
'vsprintf',
$settings['path'],
$variables);
$artifact = $build->loadArtifact($settings['artifact']);
$file = $artifact->loadPhabricatorFile();
$fragment = id(new PhragmentFragmentQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPaths(array($path))
->executeOne();
if ($fragment === null) {
PhragmentFragment::createFromFile(
PhabricatorUser::getOmnipotentUser(),
$file,
$path,
PhabricatorPolicies::getMostOpenPolicy(),
PhabricatorPolicies::POLICY_USER);
} else {
if ($file->getMimeType() === "application/zip") {
$fragment->updateFromZIP(PhabricatorUser::getOmnipotentUser(), $file);
} else {
$fragment->updateFromFile(PhabricatorUser::getOmnipotentUser(), $file);
}
}
}
public function getArtifactInputs() {
return array(
array(
'name' => pht('Publishes File'),
'key' => $this->getSetting('artifact'),
'type' => HarbormasterBuildArtifact::TYPE_FILE,
),
);
}
public function getFieldSpecifications() {
return array(
'path' => array(
'name' => pht('Path'),
'type' => 'text',
'required' => true,
),
'artifact' => array(
'name' => pht('File Artifact'),
'type' => 'text',
'required' => true,
),
);
}
}
diff --git a/src/applications/harbormaster/step/HarbormasterSleepBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterSleepBuildStepImplementation.php
index 35571f216f..bca222c17a 100644
--- a/src/applications/harbormaster/step/HarbormasterSleepBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterSleepBuildStepImplementation.php
@@ -1,40 +1,40 @@
<?php
final class HarbormasterSleepBuildStepImplementation
extends HarbormasterBuildStepImplementation {
public function getName() {
return pht('Sleep');
}
public function getGenericDescription() {
return pht('Sleep for a specified number of seconds.');
}
public function getDescription() {
- $settings = $this->getSettings();
-
- return pht('Sleep for %s seconds.', $settings['seconds']);
+ return pht(
+ 'Sleep for %s seconds.',
+ $this->formatSettingForDescription('seconds'));
}
public function execute(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
$settings = $this->getSettings();
sleep($settings['seconds']);
}
public function getFieldSpecifications() {
return array(
'seconds' => array(
'name' => pht('Seconds'),
'type' => 'int',
'required' => true,
'caption' => pht('The number of seconds to sleep for.'),
),
);
}
}
diff --git a/src/applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php b/src/applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php
index 0d3985f292..402a03ba20 100644
--- a/src/applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php
+++ b/src/applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php
@@ -1,25 +1,21 @@
<?php
final class HarbormasterThrowExceptionBuildStep
extends HarbormasterBuildStepImplementation {
public function getName() {
return pht('Throw Exception');
}
public function getGenericDescription() {
return pht('Throw an exception.');
}
- public function getDescription() {
- return pht('Throw an exception.');
- }
-
public function execute(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
throw new Exception(pht('(This is an explicit exception.)'));
}
}
diff --git a/src/applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php
index ee50f18188..1e76e65819 100644
--- a/src/applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php
@@ -1,94 +1,92 @@
<?php
final class HarbormasterUploadArtifactBuildStepImplementation
extends HarbormasterBuildStepImplementation {
public function getName() {
- return pht('Upload Artifact');
+ return pht('Upload File');
}
public function getGenericDescription() {
- return pht('Upload an artifact from a Drydock host to Phabricator.');
+ return pht('Upload a file from a host to Phabricator.');
}
public function getDescription() {
- $settings = $this->getSettings();
-
return pht(
- 'Upload artifact located at \'%s\' on \'%s\'.',
- $settings['path'],
- $settings['hostartifact']);
+ 'Upload %s from %s.',
+ $this->formatSettingForDescription('path'),
+ $this->formatSettingForDescription('hostartifact'));
}
public function execute(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
$settings = $this->getSettings();
$variables = $build_target->getVariables();
$path = $this->mergeVariables(
'vsprintf',
$settings['path'],
$variables);
$artifact = $build->loadArtifact($settings['hostartifact']);
$lease = $artifact->loadDrydockLease();
$interface = $lease->getInterface('filesystem');
// TODO: Handle exceptions.
$file = $interface->saveFile($path, $settings['name']);
// Insert the artifact record.
$artifact = $build->createArtifact(
$build_target,
$settings['name'],
HarbormasterBuildArtifact::TYPE_FILE);
$artifact->setArtifactData(array(
'filePHID' => $file->getPHID()));
$artifact->save();
}
public function getArtifactInputs() {
return array(
array(
'name' => pht('Upload From Host'),
'key' => $this->getSetting('hostartifact'),
'type' => HarbormasterBuildArtifact::TYPE_HOST,
),
);
}
public function getArtifactOutputs() {
return array(
array(
'name' => pht('Uploaded File'),
'key' => $this->getSetting('name'),
'type' => HarbormasterBuildArtifact::TYPE_FILE,
),
);
}
public function getFieldSpecifications() {
return array(
'path' => array(
'name' => pht('Path'),
'type' => 'text',
'required' => true,
),
'name' => array(
'name' => pht('Local Name'),
'type' => 'text',
'required' => true,
),
'hostartifact' => array(
'name' => pht('Host Artifact'),
'type' => 'text',
'required' => true,
),
);
}
}
diff --git a/src/applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php
index 1c802f0b85..f1d7481f3e 100644
--- a/src/applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php
@@ -1,110 +1,104 @@
<?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 getDescription() {
- return pht(
- 'Wait for previous commits to finish building the current plan '.
- 'before continuing.');
- }
-
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();
$log = null;
$log_start = null;
$blockers = $this->getBlockers($object, $plan, $build);
while (count($blockers) > 0) {
if ($log === null) {
$log = $build->createLog($build_target, "waiting", "blockers");
$log_start = $log->start();
}
$log->append("Blocked by: ".implode(",", $blockers)."\n");
// TODO: This should fail temporarily instead after setting the target to
// waiting, and thereby push the build into a waiting status.
sleep(1);
$blockers = $this->getBlockers($object, $plan, $build);
}
if ($log !== null) {
$log->finalize($log_start);
}
}
private function getBlockers(
PhabricatorRepositoryCommit $commit,
HarbormasterBuildPlan $plan,
HarbormasterBuild $source) {
$call = new ConduitCall(
'diffusion.commitparentsquery',
array(
'commit' => $commit->getCommitIdentifier(),
'callsign' => $commit->getRepository()->getCallsign()
));
$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();
}
}
$buildables = id(new HarbormasterBuildableQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withBuildablePHIDs($build_objects)
->withManualBuildables(false)
->execute();
$buildable_phids = mpull($buildables, 'getPHID');
$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 fe24542853..be26c7238d 100644
--- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php
@@ -1,334 +1,344 @@
<?php
final class HarbormasterBuild extends HarbormasterDAO
implements PhabricatorPolicyInterface {
protected $buildablePHID;
protected $buildPlanPHID;
protected $buildStatus;
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';
/**
* Waiting for a resource to be allocated (not yet relevant).
*/
const STATUS_WAITING = 'waiting';
/**
* 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 encountered an unexpected error.
*/
const STATUS_ERROR = 'error';
/**
* The build has been stopped.
*/
const STATUS_STOPPED = 'stopped';
public static function initializeNewBuild(PhabricatorUser $actor) {
return id(new HarbormasterBuild())
->setBuildStatus(self::STATUS_INACTIVE);
}
public function delete() {
$this->openTransaction();
$this->deleteUnprocessedCommands();
$result = parent::delete();
$this->saveTransaction();
return $result;
}
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
HarbormasterPHIDTypeBuild::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_WAITING ||
$this->getBuildStatus() === self::STATUS_BUILDING;
}
public function createLog(
HarbormasterBuildTarget $build_target,
$log_source,
$log_type) {
$log_source = phutil_utf8_shorten($log_source, 250);
$log = HarbormasterBuildLog::initializeNewBuildLog($build_target)
->setLogSource($log_source)
->setLogType($log_type)
->save();
return $log;
}
public function createArtifact(
HarbormasterBuildTarget $build_target,
$artifact_key,
$artifact_type) {
$artifact =
HarbormasterBuildArtifact::initializeNewBuildArtifact($build_target);
$artifact->setArtifactKey($this->getPHID(), $artifact_key);
$artifact->setArtifactType($artifact_type);
$artifact->save();
return $artifact;
}
public function loadArtifact($name) {
$artifact = id(new HarbormasterBuildArtifactQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withArtifactKeys(
$this->getPHID(),
array($name))
->executeOne();
if ($artifact === null) {
throw new Exception("Artifact not found!");
}
return $artifact;
}
public function retrieveVariablesFromBuild() {
$results = array(
'buildable.diff' => null,
'buildable.revision' => null,
'buildable.commit' => null,
'repository.callsign' => null,
'repository.vcs' => null,
'repository.uri' => null,
'step.timestamp' => null,
'build.id' => null);
$buildable = $this->getBuildable();
$object = $buildable->getBuildableObject();
$repo = null;
if ($object instanceof DifferentialDiff) {
$results['buildable.diff'] = $object->getID();
$revision = $object->getRevision();
$results['buildable.revision'] = $revision->getID();
$repo = $revision->getRepository();
} else if ($object instanceof PhabricatorRepositoryCommit) {
$results['buildable.commit'] = $object->getCommitIdentifier();
$repo = $object->getRepository();
}
if ($repo) {
$results['repository.callsign'] = $repo->getCallsign();
$results['repository.vcs'] = $repo->getVersionControlSystem();
$results['repository.uri'] = $repo->getPublicCloneURI();
}
$results['step.timestamp'] = time();
$results['build.id'] = $this->getID();
return $results;
}
public static function getAvailableBuildVariables() {
return array(
'buildable.diff' =>
pht('The differential diff ID, if applicable.'),
'buildable.revision' =>
pht('The differential revision ID, if applicable.'),
'buildable.commit' => pht('The commit identifier, if applicable.'),
'repository.callsign' =>
pht('The callsign of the repository in Phabricator.'),
'repository.vcs' =>
pht('The version control system, either "svn", "hg" or "git".'),
'repository.uri' =>
pht('The URI to clone or checkout the repository from.'),
'step.timestamp' => pht('The current UNIX timestamp.'),
'build.id' => pht('The ID of the current build.'));
}
public function isComplete() {
switch ($this->getBuildStatus()) {
case self::STATUS_PASSED:
case self::STATUS_FAILED:
case self::STATUS_ERROR:
case self::STATUS_STOPPED:
return true;
}
return false;
}
public function isStopped() {
return ($this->getBuildStatus() == self::STATUS_STOPPED);
}
/* -( Build Commands )----------------------------------------------------- */
private function getUnprocessedCommands() {
return $this->assertAttached($this->unprocessedCommands);
}
public function attachUnprocessedCommands(array $commands) {
$this->unprocessedCommands = $commands;
return $this;
}
public function canRestartBuild() {
return !$this->isRestarting();
}
public function canStopBuild() {
return !$this->isComplete() &&
!$this->isStopped() &&
!$this->isStopping();
}
public function canResumeBuild() {
return $this->isStopped() &&
!$this->isResuming();
}
public function isStopping() {
$is_stopping = false;
foreach ($this->getUnprocessedCommands() as $command_object) {
$command = $command_object->getCommand();
switch ($command) {
case HarbormasterBuildCommand::COMMAND_STOP:
$is_stopping = true;
break;
case HarbormasterBuildCommand::COMMAND_RESUME:
case HarbormasterBuildCommand::COMMAND_RESTART:
$is_stopping = false;
break;
}
}
return $is_stopping;
}
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_STOP:
$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 deleteUnprocessedCommands() {
foreach ($this->getUnprocessedCommands() as $key => $command_object) {
$command_object->delete();
unset($this->unprocessedCommands[$key]);
}
return $this;
}
/* -( 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/HarbormasterBuildTarget.php b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
index 14cbc267da..a6a94d2abd 100644
--- a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
@@ -1,154 +1,144 @@
<?php
final class HarbormasterBuildTarget extends HarbormasterDAO
implements PhabricatorPolicyInterface {
protected $buildPHID;
protected $buildStepPHID;
protected $className;
protected $details;
protected $variables;
protected $targetStatus;
const STATUS_PENDING = 'target/pending';
const STATUS_PASSED = 'target/passed';
const STATUS_FAILED = 'target/failed';
private $build = self::ATTACHABLE;
private $buildStep = self::ATTACHABLE;
+ private $implementation;
public static function initializeNewBuildTarget(
HarbormasterBuild $build,
HarbormasterBuildStep $build_step,
array $variables) {
return id(new HarbormasterBuildTarget())
->setBuildPHID($build->getPHID())
->setBuildStepPHID($build_step->getPHID())
->setClassName($build_step->getClassName())
->setDetails($build_step->getDetails())
->setTargetStatus(self::STATUS_PENDING)
->setVariables($variables);
}
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'details' => self::SERIALIZATION_JSON,
'variables' => self::SERIALIZATION_JSON,
)
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
HarbormasterPHIDTypeBuildTarget::TYPECONST);
}
public function attachBuild(HarbormasterBuild $build) {
$this->build = $build;
return $this;
}
public function getBuild() {
return $this->assertAttached($this->build);
}
public function attachBuildStep(HarbormasterBuildStep $step) {
$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 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->className === null) {
- throw new Exception("No implementation set for the given target.");
+ if ($this->implementation === null) {
+ $obj = HarbormasterBuildStepImplementation::requireImplementation(
+ $this->className);
+ $obj->loadSettings($this);
+ $this->implementation = $obj;
}
- static $implementations = null;
- if ($implementations === null) {
- $implementations =
- HarbormasterBuildStepImplementation::getImplementations();
- }
-
- $class = $this->className;
- if (!in_array($class, $implementations)) {
- throw new Exception(
- "Class name '".$class."' does not extend BuildStepImplementation.");
- }
- $implementation = newv($class, array());
- $implementation->loadSettings($this);
- return $implementation;
+ return $this->implementation;
}
/* -( Status )------------------------------------------------------------- */
public function isComplete() {
switch ($this->getTargetStatus()) {
case self::STATUS_PASSED:
case self::STATUS_FAILED:
return true;
}
return false;
}
public function isFailed() {
switch ($this->getTargetStatus()) {
case self::STATUS_FAILED:
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.');
+ return pht('Users must be able to see a build to view its build targets.');
}
}
diff --git a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php
index 0798b31b10..f76dc67327 100644
--- a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php
+++ b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php
@@ -1,111 +1,102 @@
<?php
final class HarbormasterBuildStep extends HarbormasterDAO
implements
PhabricatorPolicyInterface,
PhabricatorCustomFieldInterface {
protected $buildPlanPHID;
protected $className;
protected $details = array();
protected $sequence;
private $buildPlan = self::ATTACHABLE;
private $customFields = self::ATTACHABLE;
+ private $implementation;
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'details' => self::SERIALIZATION_JSON,
)
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
HarbormasterPHIDTypeBuildStep::TYPECONST);
}
public function attachBuildPlan(HarbormasterBuildPlan $plan) {
$this->buildPlan = $plan;
return $this;
}
public function getBuildPlan() {
return $this->assertAttached($this->buildPlan);
}
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 getStepImplementation() {
- if ($this->className === null) {
- throw new Exception("No implementation set for the given step.");
+ if ($this->implementation === null) {
+ $obj = HarbormasterBuildStepImplementation::requireImplementation(
+ $this->className);
+ $obj->loadSettings($this);
+ $this->implementation = $obj;
}
- static $implementations = null;
- if ($implementations === null) {
- $implementations =
- HarbormasterBuildStepImplementation::getImplementations();
- }
-
- $class = $this->className;
- if (!in_array($class, $implementations)) {
- throw new Exception(
- "Class name '".$class."' does not extend BuildStepImplementation.");
- }
- $implementation = newv($class, array());
- $implementation->loadSettings($this);
- return $implementation;
+ return $this->implementation;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
return $this->getBuildPlan()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getBuildPlan()->hasAutomaticCapability($capability, $viewer);
}
public function describeAutomaticCapability($capability) {
return pht('A build step has the same policies as its build plan.');
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
public function getCustomFieldSpecificationForRole($role) {
return array();
}
public function getCustomFieldBaseClass() {
return 'HarbormasterBuildStepCustomField';
}
public function getCustomFields() {
return $this->assertAttached($this->customFields);
}
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
$this->customFields = $fields;
return $this;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 15, 7:31 AM (15 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
337702
Default Alt Text
(63 KB)

Event Timeline