Page MenuHomePhabricator

D9806.id23555.diff
No OneTemporary

D9806.id23555.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2315,6 +2315,7 @@
'PhabricatorTypeaheadMonogramDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadMonogramDatasource.php',
'PhabricatorTypeaheadResult' => 'applications/typeahead/storage/PhabricatorTypeaheadResult.php',
'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadRuntimeCompositeDatasource.php',
+ 'PhabricatorTypeaheadStepDependenciesDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadStepDependenciesDatasourceController.php',
'PhabricatorUIConfigOptions' => 'applications/config/option/PhabricatorUIConfigOptions.php',
'PhabricatorUIExample' => 'applications/uiexample/examples/PhabricatorUIExample.php',
'PhabricatorUIExampleRenderController' => 'applications/uiexample/controller/PhabricatorUIExampleRenderController.php',
@@ -5211,6 +5212,7 @@
'PhabricatorTypeaheadModularDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
'PhabricatorTypeaheadMonogramDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
+ 'PhabricatorTypeaheadStepDependenciesDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
'PhabricatorUIConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorUIExampleRenderController' => 'PhabricatorController',
'PhabricatorUIListFilterExample' => 'PhabricatorUIExample',
diff --git a/src/applications/harbormaster/controller/HarbormasterPlanViewController.php b/src/applications/harbormaster/controller/HarbormasterPlanViewController.php
--- a/src/applications/harbormaster/controller/HarbormasterPlanViewController.php
+++ b/src/applications/harbormaster/controller/HarbormasterPlanViewController.php
@@ -158,17 +158,23 @@
->setHref(
$this->getApplicationURI('step/delete/'.$step->getID().'/')));
+ $depends = $step->getStepImplementation()->getDependencies($step);
$inputs = $step->getStepImplementation()->getArtifactInputs();
$outputs = $step->getStepImplementation()->getArtifactOutputs();
$has_conflicts = false;
- if ($inputs || $outputs) {
+ if ($depends || $inputs || $outputs) {
$available_artifacts =
HarbormasterBuildStepImplementation::loadAvailableArtifacts(
$plan,
$step,
null);
+ list($depends_ui, $has_conflicts) = $this->buildDependsOnList(
+ $depends,
+ pht('Depends On'),
+ $steps);
+
list($inputs_ui, $has_conflicts) = $this->buildArtifactList(
$inputs,
'in',
@@ -188,6 +194,7 @@
'class' => 'harbormaster-artifact-io',
),
array(
+ $depends_ui,
$inputs_ui,
$outputs_ui,
)));
@@ -292,7 +299,6 @@
return array(null, $has_conflicts);
}
-
$this->requireResource('harbormaster-css');
$header = phutil_tag(
@@ -385,4 +391,71 @@
return array($ui, $has_conflicts);
}
+ private function buildDependsOnList(
+ array $artifacts,
+ $name,
+ array $steps) {
+ $has_conflicts = false;
+
+ $this->requireResource('harbormaster-css');
+
+ $steps = mpull($steps, null, 'getPHID');
+
+ $header = phutil_tag(
+ 'div',
+ array(
+ 'class' => 'harbormaster-artifact-summary-header',
+ ),
+ $name);
+
+ $list = new PHUIStatusListView();
+ foreach ($artifacts as $artifact) {
+ $error = null;
+
+ $type = idx($artifact, 'type');
+ $key = idx($artifact, 'key');
+ if ($type !== HarbormasterBuildArtifact::TYPE_BUILD_STATE) {
+ $icon = PHUIStatusItemView::ICON_WARNING;
+ $color = 'red';
+ $icon_label = pht('Invalid Dependency');
+ $has_conflicts = true;
+ $error = pht(
+ 'This dependency is specified, but is not another build step.');
+ } else if (idx($steps, $key) === null) {
+ $icon = PHUIStatusItemView::ICON_WARNING;
+ $color = 'red';
+ $icon_label = pht('Missing Dependency');
+ $has_conflicts = true;
+ $error = pht(
+ 'This dependency specifies a build step which doesn\'t exist.');
+ } else {
+ $bound = phutil_tag('strong', array(), idx($steps, $key)->getName());
+ $icon = PHUIStatusItemView::ICON_ACCEPT;
+ $color = 'green';
+ $icon_label = pht('Valid Input');
+ }
+
+ if ($error) {
+ $note = array(
+ phutil_tag('strong', array(), pht('ERROR:')),
+ ' ',
+ $error);
+ } else {
+ $note = $bound;
+ }
+
+ $list->addItem(
+ id(new PHUIStatusItemView())
+ ->setIcon($icon, $color, $icon_label)
+ ->setTarget(pht('Build Step'))
+ ->setNote($note));
+ }
+
+ $ui = array(
+ $header,
+ $list,
+ );
+
+ return array($ui, $has_conflicts);
+ }
}
diff --git a/src/applications/harbormaster/controller/HarbormasterStepEditController.php b/src/applications/harbormaster/controller/HarbormasterStepEditController.php
--- a/src/applications/harbormaster/controller/HarbormasterStepEditController.php
+++ b/src/applications/harbormaster/controller/HarbormasterStepEditController.php
@@ -66,12 +66,34 @@
$e_name = true;
$v_name = $step->getName();
+ $e_depends_on = true;
+ $raw_depends_on = $step->getDetail('depends_on', array());
+
+ $steps = id(new HarbormasterBuildStepQuery())
+ ->setViewer($viewer)
+ ->withBuildPlanPHIDs(array($plan->getPHID()))
+ ->execute();
+ $steps = mpull($steps, null, 'getPHID');
+
+ // TODO: This is pretty horrible!
+ $v_depends_on = array();
+ foreach ($raw_depends_on as $key) {
+ $ref_step = idx($steps, $key);
+ if ($ref_step !== null) {
+ $v_depends_on[] = id(new PhabricatorObjectHandle())
+ ->setName($ref_step->getName())
+ ->setURI('/')
+ ->setPHID($key);
+ }
+ }
$errors = array();
$validation_exception = null;
if ($request->isFormPost()) {
$e_name = null;
$v_name = $request->getStr('name');
+ $e_depends_on = null;
+ $v_depends_on = $request->getArr('dependsOn');
$xactions = $field_list->buildFieldTransactionsFromRequest(
new HarbormasterBuildStepTransaction(),
@@ -87,6 +109,12 @@
->setNewValue($v_name);
array_unshift($xactions, $name_xaction);
+ $depends_on_xaction = id(new HarbormasterBuildStepTransaction())
+ ->setTransactionType(
+ HarbormasterBuildStepTransaction::TYPE_DEPENDS_ON)
+ ->setNewValue($v_depends_on);
+ array_unshift($xactions, $depends_on_xaction);
+
if ($is_new) {
// This is okay, but a little iffy. We should move it inside the editor
// if we create plans elsewhere.
@@ -109,6 +137,12 @@
}
}
+ $datasource_uri = new PhutilURI('/typeahead/stepdependencies/');
+ $datasource_uri->setQueryParam('planPHID', $plan->getPHID());
+ if (!$is_new) {
+ $datasource_uri->setQueryParam('stepPHID', $step->getPHID());
+ }
+
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
@@ -118,6 +152,28 @@
->setError($e_name)
->setValue($v_name));
+ // Check if there are any build steps that we can use.
+ $step_ref = null;
+ if (!$is_new) {
+ $step_ref = $step;
+ }
+ $previous_artifacts =
+ HarbormasterBuildStepImplementation::loadAvailableArtifacts(
+ $plan,
+ $step_ref,
+ HarbormasterBuildArtifact::TYPE_BUILD_STATE);
+
+ if (count($previous_artifacts) > 0) {
+ $form
+ ->appendChild(
+ id(new AphrontFormTokenizerControl())
+ ->setDatasource((string)$datasource_uri)
+ ->setName('dependsOn')
+ ->setLabel(pht('Depends On'))
+ ->setError($e_depends_on)
+ ->setValue($v_depends_on));
+ }
+
$field_list->appendFieldsToForm($form);
if ($is_new) {
diff --git a/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php
--- a/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php
+++ b/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php
@@ -8,6 +8,7 @@
$types[] = HarbormasterBuildStepTransaction::TYPE_CREATE;
$types[] = HarbormasterBuildStepTransaction::TYPE_NAME;
+ $types[] = HarbormasterBuildStepTransaction::TYPE_DEPENDS_ON;
return $types;
}
@@ -24,6 +25,11 @@
return null;
}
return $object->getName();
+ case HarbormasterBuildStepTransaction::TYPE_DEPENDS_ON:
+ if ($this->getIsNewObject()) {
+ return null;
+ }
+ return $object->getDetail('depends_on', array());
}
return parent::getCustomTransactionOldValue($object, $xaction);
@@ -37,6 +43,7 @@
case HarbormasterBuildStepTransaction::TYPE_CREATE:
return true;
case HarbormasterBuildStepTransaction::TYPE_NAME:
+ case HarbormasterBuildStepTransaction::TYPE_DEPENDS_ON:
return $xaction->getNewValue();
}
@@ -52,6 +59,8 @@
return;
case HarbormasterBuildStepTransaction::TYPE_NAME:
return $object->setName($xaction->getNewValue());
+ case HarbormasterBuildStepTransaction::TYPE_DEPENDS_ON:
+ return $object->setDetail('depends_on', $xaction->getNewValue());
}
return parent::applyCustomInternalTransaction($object, $xaction);
@@ -64,6 +73,7 @@
switch ($xaction->getTransactionType()) {
case HarbormasterBuildStepTransaction::TYPE_CREATE:
case HarbormasterBuildStepTransaction::TYPE_NAME:
+ case HarbormasterBuildStepTransaction::TYPE_DEPENDS_ON:
return;
}
diff --git a/src/applications/harbormaster/engine/HarbormasterBuildEngine.php b/src/applications/harbormaster/engine/HarbormasterBuildEngine.php
--- a/src/applications/harbormaster/engine/HarbormasterBuildEngine.php
+++ b/src/applications/harbormaster/engine/HarbormasterBuildEngine.php
@@ -246,22 +246,14 @@
// Identify all the steps which are ready to run (because all their
// depdendencies are complete).
- $previous_step = null;
$runnable = array();
foreach ($steps as $step) {
- // TODO: For now, we're hard coding sequential dependencies into build
- // steps. In the future, we can be smart about this instead.
-
- if ($previous_step) {
- $dependencies = array($previous_step);
- } else {
- $dependencies = array();
- }
+ $dependencies = $step->getDetail('depends_on');
if (isset($queued[$step->getPHID()])) {
$can_run = true;
foreach ($dependencies as $dependency) {
- if (empty($complete[$dependency->getPHID()])) {
+ if (empty($complete[$dependency])) {
$can_run = false;
break;
}
@@ -271,13 +263,12 @@
$runnable[] = $step;
}
}
-
- $previous_step = $step;
}
if (!$runnable && !$waiting && !$underway) {
- // TODO: This means the build is deadlocked, probably? It should not
- // normally be possible yet, but we should communicate it more clearly.
+ // This means the build is deadlocked, and the user has configured
+ // circular dependencies. It should not normally be possible yet,
+ // but we should communicate it more clearly.
$build->setBuildStatus(HarbormasterBuild::STATUS_FAILED);
$build->save();
return;
diff --git a/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php
--- a/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php
@@ -98,6 +98,36 @@
return array();
}
+ public function getDependencies(HarbormasterBuildStep $build_step) {
+ $depends_on = $build_step->getDetail('depends_on', array());
+ $artifacts = array();
+ foreach ($depends_on as $dependency) {
+ $artifacts[] = array(
+ 'name' => pht('Build State'),
+ 'key' => $dependency,
+ 'type' => HarbormasterBuildArtifact::TYPE_BUILD_STATE,
+ );
+ }
+ return $artifacts;
+ }
+
+ public function getFullArtifactInputs(HarbormasterBuildStep $build_step) {
+ $artifacts = $this->getArtifactInputs();
+ $artifacts += $this->getDependencies($build_step);
+ return $artifacts;
+ }
+
+ public function getFullArtifactOutputs(HarbormasterBuildStep $build_step) {
+ $artifacts = $this->getArtifactOutputs();
+ $artifacts[] = array(
+ 'name' => pht('Build State'),
+ 'key' => $build_step->getPHID(),
+ 'type' => HarbormasterBuildArtifact::TYPE_BUILD_STATE,
+ );
+
+ return $artifacts;
+ }
+
/**
* Returns a list of all artifacts made available by previous build steps.
*/
@@ -121,18 +151,20 @@
public static function getAvailableArtifacts(
HarbormasterBuildPlan $build_plan,
array $build_steps,
- HarbormasterBuildStep $current_build_step,
+ $current_build_step,
$artifact_type) {
- $previous_implementations = array();
+ $artifact_arrays = array();
foreach ($build_steps as $build_step) {
- if ($build_step->getPHID() === $current_build_step->getPHID()) {
+ if ($current_build_step !== null &&
+ $build_step->getPHID() === $current_build_step->getPHID()) {
break;
}
- $previous_implementations[] = $build_step->getStepImplementation();
+
+ $implementation = $build_step->getStepImplementation();
+ $artifact_arrays[] = $implementation->getFullArtifactOutputs($build_step);
}
- $artifact_arrays = mpull($previous_implementations, 'getArtifactOutputs');
$artifacts = array();
foreach ($artifact_arrays as $array) {
$array = ipull($array, 'type', 'key');
diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildArtifact.php b/src/applications/harbormaster/storage/build/HarbormasterBuildArtifact.php
--- a/src/applications/harbormaster/storage/build/HarbormasterBuildArtifact.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuildArtifact.php
@@ -13,6 +13,7 @@
const TYPE_FILE = 'file';
const TYPE_HOST = 'host';
+ const TYPE_BUILD_STATE = 'buildstate';
public static function initializeNewBuildArtifact(
HarbormasterBuildTarget $build_target) {
diff --git a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStepTransaction.php b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStepTransaction.php
--- a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStepTransaction.php
+++ b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStepTransaction.php
@@ -5,6 +5,7 @@
const TYPE_CREATE = 'harbormaster:step:create';
const TYPE_NAME = 'harbormaster:step:name';
+ const TYPE_DEPENDS_ON = 'harbormaster:step:depends';
public function getApplicationName() {
return 'harbormaster';
diff --git a/src/applications/typeahead/application/PhabricatorApplicationTypeahead.php b/src/applications/typeahead/application/PhabricatorApplicationTypeahead.php
--- a/src/applications/typeahead/application/PhabricatorApplicationTypeahead.php
+++ b/src/applications/typeahead/application/PhabricatorApplicationTypeahead.php
@@ -7,6 +7,8 @@
'/typeahead/' => array(
'common/(?P<type>\w+)/'
=> 'PhabricatorTypeaheadCommonDatasourceController',
+ 'stepdependencies/'
+ => 'PhabricatorTypeaheadStepDependenciesDatasourceController',
'class/(?:(?P<class>\w+)/)?'
=> 'PhabricatorTypeaheadModularDatasourceController',
),
diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadStepDependenciesDatasourceController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadStepDependenciesDatasourceController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/typeahead/controller/PhabricatorTypeaheadStepDependenciesDatasourceController.php
@@ -0,0 +1,87 @@
+<?php
+
+final class PhabricatorTypeaheadStepDependenciesDatasourceController
+ extends PhabricatorTypeaheadDatasourceController {
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ $plan_phid = $request->getStr('planPHID');
+ $step_phid = $request->getStr('stepPHID');
+
+ $plan = id(new HarbormasterBuildPlanQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($plan_phid))
+ ->executeOne();
+
+ $steps = id(new HarbormasterBuildStepQuery())
+ ->setViewer($viewer)
+ ->withBuildPlanPHIDs(array($plan_phid))
+ ->execute();
+ $steps = mpull($steps, null, 'getPHID');
+
+ $current_step = idx($steps, $step_phid);
+
+ $artifacts = HarbormasterBuildStepImplementation::loadAvailableArtifacts(
+ $plan,
+ $current_step,
+ HarbormasterBuildArtifact::TYPE_BUILD_STATE);
+
+ $results = array();
+ foreach ($artifacts as $key => $name) {
+ $ref_step = idx($steps, $key);
+ if ($ref_step !== null) {
+ $results[] = id(new PhabricatorTypeaheadResult())
+ ->setName($ref_step->getName())
+ ->setURI('/')
+ ->setPHID($key);
+ }
+ }
+
+ $content = mpull($results, 'getWireFormat');
+
+ if ($request->isAjax()) {
+ return id(new AphrontAjaxResponse())->setContent($content);
+ }
+
+ // If there's a non-Ajax request to this endpoint, show results in a tabular
+ // format to make it easier to debug typeahead output.
+
+ $rows = array();
+ foreach ($results as $result) {
+ $wire = $result->getWireFormat();
+ $rows[] = $wire;
+ }
+
+ $table = new AphrontTableView($rows);
+ $table->setHeaders(
+ array(
+ 'Name',
+ 'URI',
+ 'PHID',
+ 'Priority',
+ 'Display Name',
+ 'Display Type',
+ 'Image URI',
+ 'Priority Type',
+ 'Sprite Class',
+ ));
+
+ $panel = new AphrontPanelView();
+ $panel->setHeader('Typeahead Results');
+ $panel->appendChild($table);
+
+ return $this->buildStandardPageResponse(
+ $panel,
+ array(
+ 'title' => pht('Typeahead Results'),
+ 'device' => true
+ ));
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Fri, Mar 21, 2:22 PM (6 h, 40 m ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7715553
Default Alt Text
D9806.id23555.diff (18 KB)

Event Timeline