Page MenuHomestyx hydra

No OneTemporary

diff --git a/resources/sql/autopatches/20140704.harbormasterstep.1.sql b/resources/sql/autopatches/20140704.harbormasterstep.1.sql
new file mode 100644
index 0000000000..318765a024
--- /dev/null
+++ b/resources/sql/autopatches/20140704.harbormasterstep.1.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildstep
+ ADD name VARCHAR(255) COLLATE utf8_bin;
diff --git a/resources/sql/autopatches/20140704.harbormasterstep.2.sql b/resources/sql/autopatches/20140704.harbormasterstep.2.sql
new file mode 100644
index 0000000000..517d29baac
--- /dev/null
+++ b/resources/sql/autopatches/20140704.harbormasterstep.2.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildtarget
+ ADD name VARCHAR(255) COLLATE utf8_bin;
diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php
index 6ae9639999..58419976d8 100644
--- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php
+++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php
@@ -1,375 +1,375 @@
<?php
final class HarbormasterBuildViewController
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;
$build = id(new HarbormasterBuildQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$build) {
return new Aphront404Response();
}
$title = pht('Build %d', $id);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($viewer)
->setPolicyObject($build);
if ($build->isRestarting()) {
$header->setStatus('fa-exclamation-triangle', 'red', pht('Restarting'));
} else if ($build->isStopping()) {
$header->setStatus('fa-exclamation-triangle', 'red', pht('Stopping'));
} else if ($build->isResuming()) {
$header->setStatus('fa-exclamation-triangle', 'red', pht('Resuming'));
}
$box = id(new PHUIObjectBoxView())
->setHeader($header);
$actions = $this->buildActionList($build);
$this->buildPropertyLists($box, $build, $actions);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
$build->getBuildable()->getMonogram(),
'/'.$build->getBuildable()->getMonogram());
$crumbs->addTextCrumb($title);
$build_targets = id(new HarbormasterBuildTargetQuery())
->setViewer($viewer)
->withBuildPHIDs(array($build->getPHID()))
->execute();
if ($build_targets) {
$messages = id(new HarbormasterBuildMessageQuery())
->setViewer($viewer)
->withBuildTargetPHIDs(mpull($build_targets, 'getPHID'))
->execute();
$messages = mgroup($messages, 'getBuildTargetPHID');
} else {
$messages = array();
}
$targets = array();
foreach ($build_targets as $build_target) {
$header = id(new PHUIHeaderView())
->setHeader(pht(
'Build Target %d (%s)',
$build_target->getID(),
- $build_target->getImplementation()->getName()))
+ $build_target->getName()))
->setUser($viewer);
$properties = new PHUIPropertyListView();
$details = $build_target->getDetails();
if ($details) {
$properties->addSectionHeader(pht('Configuration Details'));
foreach ($details as $key => $value) {
$properties->addProperty($key, $value);
}
}
$variables = $build_target->getVariables();
if ($variables) {
$properties->addSectionHeader(pht('Variables'));
foreach ($variables as $key => $value) {
$properties->addProperty($key, $value);
}
}
$targets[] = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$build_messages = idx($messages, $build_target->getPHID(), array());
$targets[] = $this->buildMessages($build_messages);
$targets[] = $this->buildArtifacts($build_target);
$targets[] = $this->buildLog($build, $build_target);
}
$xactions = id(new HarbormasterBuildTransactionQuery())
->setViewer($viewer)
->withObjectPHIDs(array($build->getPHID()))
->execute();
$timeline = id(new PhabricatorApplicationTransactionView())
->setUser($viewer)
->setObjectPHID($build->getPHID())
->setTransactions($xactions);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$targets,
$timeline,
),
array(
'title' => $title,
));
}
private function buildArtifacts(HarbormasterBuildTarget $build_target) {
$request = $this->getRequest();
$viewer = $request->getUser();
$artifacts = id(new HarbormasterBuildArtifactQuery())
->setViewer($viewer)
->withBuildTargetPHIDs(array($build_target->getPHID()))
->execute();
if (count($artifacts) === 0) {
return null;
}
$list = new PHUIObjectItemListView();
foreach ($artifacts as $artifact) {
$list->addItem($artifact->getObjectItemView($viewer));
}
$header = id(new PHUIHeaderView())
->setHeader(pht('Build Artifacts'))
->setUser($viewer);
$box = id(new PHUIObjectBoxView())
->setHeader($header);
return array($box, $list);
}
private function buildLog(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
$request = $this->getRequest();
$viewer = $request->getUser();
$limit = $request->getInt('l', 25);
$logs = id(new HarbormasterBuildLogQuery())
->setViewer($viewer)
->withBuildTargetPHIDs(array($build_target->getPHID()))
->execute();
$log_boxes = array();
foreach ($logs as $log) {
$start = 1;
$lines = preg_split("/\r\n|\r|\n/", $log->getLogText());
if ($limit !== 0) {
$start = count($lines) - $limit;
if ($start >= 1) {
$lines = array_slice($lines, -$limit, $limit);
} else {
$start = 1;
}
}
$log_view = new ShellLogView();
$log_view->setLines($lines);
$log_view->setStart($start);
$header = id(new PHUIHeaderView())
->setHeader(pht(
'Build Log %d (%s - %s)',
$log->getID(),
$log->getLogSource(),
$log->getLogType()))
->setSubheader($this->createLogHeader($build, $log))
->setUser($viewer);
$log_boxes[] = id(new PHUIObjectBoxView())
->setHeader($header)
->setForm($log_view);
}
return $log_boxes;
}
private function createLogHeader($build, $log) {
$request = $this->getRequest();
$limit = $request->getInt('l', 25);
$lines_25 = $this->getApplicationURI('/build/'.$build->getID().'/?l=25');
$lines_50 = $this->getApplicationURI('/build/'.$build->getID().'/?l=50');
$lines_100 =
$this->getApplicationURI('/build/'.$build->getID().'/?l=100');
$lines_0 = $this->getApplicationURI('/build/'.$build->getID().'/?l=0');
$link_25 = phutil_tag('a', array('href' => $lines_25), pht('25'));
$link_50 = phutil_tag('a', array('href' => $lines_50), pht('50'));
$link_100 = phutil_tag('a', array('href' => $lines_100), pht('100'));
$link_0 = phutil_tag('a', array('href' => $lines_0), pht('Unlimited'));
if ($limit === 25) {
$link_25 = phutil_tag('strong', array(), $link_25);
} else if ($limit === 50) {
$link_50 = phutil_tag('strong', array(), $link_50);
} else if ($limit === 100) {
$link_100 = phutil_tag('strong', array(), $link_100);
} else if ($limit === 0) {
$link_0 = phutil_tag('strong', array(), $link_0);
}
return phutil_tag(
'span',
array(),
array(
$link_25,
' - ',
$link_50,
' - ',
$link_100,
' - ',
$link_0,
' Lines'));
}
private function buildActionList(HarbormasterBuild $build) {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $build->getID();
$list = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($build)
->setObjectURI("/build/{$id}");
$can_restart = $build->canRestartBuild();
$can_stop = $build->canStopBuild();
$can_resume = $build->canResumeBuild();
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Restart Build'))
->setIcon('fa-backward')
->setHref($this->getApplicationURI('/build/restart/'.$id.'/'))
->setDisabled(!$can_restart)
->setWorkflow(true));
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Stop Build'))
->setIcon('fa-stop')
->setHref($this->getApplicationURI('/build/stop/'.$id.'/'))
->setDisabled(!$can_stop)
->setWorkflow(true));
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Resume Build'))
->setIcon('fa-play')
->setHref($this->getApplicationURI('/build/resume/'.$id.'/'))
->setDisabled(!$can_resume)
->setWorkflow(true));
return $list;
}
private function buildPropertyLists(
PHUIObjectBoxView $box,
HarbormasterBuild $build,
PhabricatorActionListView $actions) {
$request = $this->getRequest();
$viewer = $request->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($build)
->setActionList($actions);
$box->addPropertyList($properties);
$properties->addProperty(
pht('Status'),
$this->getStatus($build));
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs(array(
$build->getBuildablePHID(),
$build->getBuildPlanPHID()))
->execute();
$properties->addProperty(
pht('Buildable'),
$handles[$build->getBuildablePHID()]->renderLink());
$properties->addProperty(
pht('Build Plan'),
$handles[$build->getBuildPlanPHID()]->renderLink());
}
private function getStatus(HarbormasterBuild $build) {
if ($build->isStopping()) {
return pht('Stopping');
}
return HarbormasterBuild::getBuildStatusName($build->getBuildStatus());
}
private function buildMessages(array $messages) {
$viewer = $this->getRequest()->getUser();
if ($messages) {
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs(mpull($messages, 'getAuthorPHID'))
->execute();
} else {
$handles = array();
}
$rows = array();
foreach ($messages as $message) {
$rows[] = array(
$message->getID(),
$handles[$message->getAuthorPHID()]->renderLink(),
$message->getType(),
$message->getIsConsumed() ? pht('Consumed') : null,
phabricator_datetime($message->getDateCreated(), $viewer),
);
}
$table = new AphrontTableView($rows);
$table->setNoDataString(pht('No messages for this build target.'));
$table->setHeaders(
array(
pht('ID'),
pht('From'),
pht('Type'),
pht('Consumed'),
pht('Received'),
));
$table->setColumnClasses(
array(
'',
'',
'wide',
'',
'date',
));
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Build Target Messages'))
->appendChild($table);
return $box;
}
}
diff --git a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
index 8585b832c3..e8a41f13f6 100644
--- a/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
+++ b/src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
@@ -1,312 +1,307 @@
<?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)
->executeOne();
if (!$buildable) {
return new Aphront404Response();
}
// Pull builds and build targets.
$builds = id(new HarbormasterBuildQuery())
->setViewer($viewer)
->withBuildablePHIDs(array($buildable->getPHID()))
->needBuildTargets(true)
->execute();
$buildable->attachBuilds($builds);
$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);
$xactions = id(new HarbormasterBuildableTransactionQuery())
->setViewer($viewer)
->withObjectPHIDs(array($buildable->getPHID()))
->execute();
$timeline = id(new PhabricatorApplicationTransactionView())
->setUser($viewer)
->setObjectPHID($buildable->getPHID())
->setTransactions($xactions);
$actions = $this->buildActionList($buildable);
$this->buildPropertyLists($box, $buildable, $actions);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb("B{$id}");
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$build_list,
$timeline,
),
array(
'title' => $title,
));
}
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('fa-backward')
->setName(pht('Restart All Builds'))
->setHref($this->getApplicationURI($restart_uri))
->setWorkflow(true)
->setDisabled(!$can_restart || !$can_edit));
$list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-stop')
->setName(pht('Stop All Builds'))
->setHref($this->getApplicationURI($stop_uri))
->setWorkflow(true)
->setDisabled(!$can_stop || !$can_edit));
$list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-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);
$status = $build->getBuildStatus();
switch ($status) {
case HarbormasterBuild::STATUS_INACTIVE:
$item->setBarColor('grey');
break;
case HarbormasterBuild::STATUS_PENDING:
$item->setBarColor('blue');
break;
case HarbormasterBuild::STATUS_BUILDING:
$item->setBarColor('yellow');
break;
case HarbormasterBuild::STATUS_PASSED:
$item->setBarColor('green');
break;
case HarbormasterBuild::STATUS_FAILED:
$item->setBarColor('red');
break;
case HarbormasterBuild::STATUS_ERROR:
$item->setBarColor('red');
break;
case HarbormasterBuild::STATUS_STOPPED:
$item->setBarColor('black');
break;
}
$item->addAttribute(HarbormasterBuild::getBuildStatusName($status));
if ($build->isRestarting()) {
$item->addIcon('fa-backward', pht('Restarting'));
} else if ($build->isStopping()) {
$item->addIcon('fa-stop', pht('Stopping'));
} else if ($build->isResuming()) {
$item->addIcon('fa-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('fa-backward')
->setName(pht('Restart'))
->setHref($this->getApplicationURI($restart_uri))
->setWorkflow(true)
->setDisabled(!$build->canRestartBuild()));
if ($build->canResumeBuild()) {
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-play')
->setName(pht('Resume'))
->setHref($this->getApplicationURI($resume_uri))
->setWorkflow(true));
} else {
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-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 = PHUIStatusItemView::ICON_CLOCK;
$color = 'green';
$status_name = pht('Pending');
break;
case HarbormasterBuildTarget::STATUS_BUILDING:
$icon = PHUIStatusItemView::ICON_RIGHT;
$color = 'green';
$status_name = pht('Building');
break;
case HarbormasterBuildTarget::STATUS_WAITING:
$icon = PHUIStatusItemView::ICON_CLOCK;
$color = 'orange';
$status_name = pht('Waiting');
break;
case HarbormasterBuildTarget::STATUS_PASSED:
$icon = PHUIStatusItemView::ICON_ACCEPT;
$color = 'green';
$status_name = pht('Passed');
break;
case HarbormasterBuildTarget::STATUS_FAILED:
$icon = PHUIStatusItemView::ICON_REJECT;
$color = 'red';
$status_name = pht('Failed');
break;
default:
$icon = PHUIStatusItemView::ICON_QUESTION;
$color = 'bluegrey';
$status_name = pht('Unknown');
break;
}
- try {
- $impl = $target->getImplementation();
- $name = $impl->getName();
- } catch (Exception $ex) {
- $name = $target->getClassName();
- }
+ $name = $target->getName();
$target_list->addItem(
id(new PHUIStatusItemView())
->setIcon($icon, $color, $status_name)
->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/HarbormasterPlanViewController.php b/src/applications/harbormaster/controller/HarbormasterPlanViewController.php
index 8bfe298fdd..45dad6f344 100644
--- a/src/applications/harbormaster/controller/HarbormasterPlanViewController.php
+++ b/src/applications/harbormaster/controller/HarbormasterPlanViewController.php
@@ -1,388 +1,388 @@
<?php
final class HarbormasterPlanViewController
extends HarbormasterPlanController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $this->id;
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$plan) {
return new Aphront404Response();
}
$xactions = id(new HarbormasterBuildPlanTransactionQuery())
->setViewer($viewer)
->withObjectPHIDs(array($plan->getPHID()))
->execute();
$xaction_view = id(new PhabricatorApplicationTransactionView())
->setUser($viewer)
->setObjectPHID($plan->getPHID())
->setTransactions($xactions)
->setShouldTerminate(true);
$title = pht('Plan %d', $id);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($viewer)
->setPolicyObject($plan);
$box = id(new PHUIObjectBoxView())
->setHeader($header);
$actions = $this->buildActionList($plan);
$this->buildPropertyLists($box, $plan, $actions);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Plan %d', $id));
list($step_list, $has_any_conflicts) = $this->buildStepList($plan);
if ($has_any_conflicts) {
$box->setFormErrors(
array(
pht(
'This build plan has conflicts in one or more build steps. '.
'Examine the step list and resolve the listed errors.'),
));
}
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$step_list,
$xaction_view,
),
array(
'title' => $title,
));
}
private function buildStepList(HarbormasterBuildPlan $plan) {
$request = $this->getRequest();
$viewer = $request->getUser();
$list_id = celerity_generate_unique_node_id();
$steps = id(new HarbormasterBuildStepQuery())
->setViewer($viewer)
->withBuildPlanPHIDs(array($plan->getPHID()))
->execute();
$can_edit = $this->hasApplicationCapability(
HarbormasterCapabilityManagePlans::CAPABILITY);
$i = 1;
$step_list = id(new PHUIObjectItemListView())
->setUser($viewer)
->setNoDataString(
pht('This build plan does not have any build steps yet.'))
->setID($list_id);
Javelin::initBehavior(
'harbormaster-reorder-steps',
array(
'listID' => $list_id,
'orderURI' => '/harbormaster/plan/order/'.$plan->getID().'/',
));
$has_any_conflicts = false;
foreach ($steps as $step) {
$implementation = null;
try {
$implementation = $step->getStepImplementation();
} catch (Exception $ex) {
// We can't initialize the implementation. This might be because
// it's been renamed or no longer exists.
$item = id(new PHUIObjectItemView())
->setObjectName(pht('Step %d', $i++))
->setHeader(pht('Unknown Implementation'))
->setBarColor('red')
->addAttribute(pht(
'This step has an invalid implementation (%s).',
$step->getClassName()))
->addAction(
id(new PHUIListItemView())
->setIcon('fa-times')
->addSigil('harbormaster-build-step-delete')
->setWorkflow(true)
->setRenderNameAsTooltip(true)
->setName(pht('Delete'))
->setHref(
$this->getApplicationURI('step/delete/'.$step->getID().'/')));
$step_list->addItem($item);
continue;
}
$item = id(new PHUIObjectItemView())
->setObjectName('Step '.$i++)
- ->setHeader($implementation->getName());
+ ->setHeader($step->getName());
$item->addAttribute($implementation->getDescription());
$step_id = $step->getID();
$edit_uri = $this->getApplicationURI("step/edit/{$step_id}/");
$delete_uri = $this->getApplicationURI("step/delete/{$step_id}/");
if ($can_edit) {
$item->setHref($edit_uri);
$item->setGrippable(true);
$item->addSigil('build-step');
$item->setMetadata(
array(
'stepID' => $step->getID(),
));
}
$item
->setHref($edit_uri)
->addAction(
id(new PHUIListItemView())
->setIcon('fa-times')
->addSigil('harbormaster-build-step-delete')
->setWorkflow(true)
->setDisabled(!$can_edit)
->setHref(
$this->getApplicationURI('step/delete/'.$step->getID().'/')));
$inputs = $step->getStepImplementation()->getArtifactInputs();
$outputs = $step->getStepImplementation()->getArtifactOutputs();
$has_conflicts = false;
if ($inputs || $outputs) {
$available_artifacts =
HarbormasterBuildStepImplementation::loadAvailableArtifacts(
$plan,
$step,
null);
list($inputs_ui, $has_conflicts) = $this->buildArtifactList(
$inputs,
'in',
pht('Input Artifacts'),
$available_artifacts);
list($outputs_ui) = $this->buildArtifactList(
$outputs,
'out',
pht('Output Artifacts'),
array());
$item->appendChild(
phutil_tag(
'div',
array(
'class' => 'harbormaster-artifact-io',
),
array(
$inputs_ui,
$outputs_ui,
)));
}
if ($has_conflicts) {
$has_any_conflicts = true;
$item->setBarColor('red');
}
$step_list->addItem($item);
}
return array($step_list, $has_any_conflicts);
}
private function buildActionList(HarbormasterBuildPlan $plan) {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $plan->getID();
$list = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($plan)
->setObjectURI($this->getApplicationURI("plan/{$id}/"));
$can_edit = $this->hasApplicationCapability(
HarbormasterCapabilityManagePlans::CAPABILITY);
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Plan'))
->setHref($this->getApplicationURI("plan/edit/{$id}/"))
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit)
->setIcon('fa-pencil'));
if ($plan->isDisabled()) {
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Enable Plan'))
->setHref($this->getApplicationURI("plan/disable/{$id}/"))
->setWorkflow(true)
->setDisabled(!$can_edit)
->setIcon('fa-check'));
} else {
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Disable Plan'))
->setHref($this->getApplicationURI("plan/disable/{$id}/"))
->setWorkflow(true)
->setDisabled(!$can_edit)
->setIcon('fa-ban'));
}
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Add Build Step'))
->setHref($this->getApplicationURI("step/add/{$id}/"))
->setWorkflow(true)
->setDisabled(!$can_edit)
->setIcon('fa-plus'));
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Run Plan Manually'))
->setHref($this->getApplicationURI("plan/run/{$id}/"))
->setWorkflow(true)
->setDisabled(!$can_edit)
->setIcon('fa-play-circle'));
return $list;
}
private function buildPropertyLists(
PHUIObjectBoxView $box,
HarbormasterBuildPlan $plan,
PhabricatorActionListView $actions) {
$request = $this->getRequest();
$viewer = $request->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($plan)
->setActionList($actions);
$box->addPropertyList($properties);
$properties->addProperty(
pht('Created'),
phabricator_datetime($plan->getDateCreated(), $viewer));
}
private function buildArtifactList(
array $artifacts,
$kind,
$name,
array $available_artifacts) {
$has_conflicts = false;
if (!$artifacts) {
return array(null, $has_conflicts);
}
$this->requireResource('harbormaster-css');
$header = phutil_tag(
'div',
array(
'class' => 'harbormaster-artifact-summary-header',
),
$name);
$is_input = ($kind == 'in');
$list = new PHUIStatusListView();
foreach ($artifacts as $artifact) {
$error = null;
$key = idx($artifact, 'key');
if (!strlen($key)) {
$bound = phutil_tag('em', array(), pht('(null)'));
if ($is_input) {
// This is an unbound input. For now, all inputs are always required.
$icon = PHUIStatusItemView::ICON_WARNING;
$color = 'red';
$icon_label = pht('Required Input');
$has_conflicts = true;
$error = pht('This input is required, but not configured.');
} else {
// This is an unnamed output. Outputs do not necessarily need to be
// named.
$icon = PHUIStatusItemView::ICON_OPEN;
$color = 'bluegrey';
$icon_label = pht('Unused Output');
}
} else {
$bound = phutil_tag('strong', array(), $key);
if ($is_input) {
if (isset($available_artifacts[$key])) {
if ($available_artifacts[$key] == idx($artifact, 'type')) {
$icon = PHUIStatusItemView::ICON_ACCEPT;
$color = 'green';
$icon_label = pht('Valid Input');
} else {
$icon = PHUIStatusItemView::ICON_WARNING;
$color = 'red';
$icon_label = pht('Bad Input Type');
$has_conflicts = true;
$error = pht(
'This input is bound to the wrong artifact type. It is bound '.
'to a "%s" artifact, but should be bound to a "%s" artifact.',
$available_artifacts[$key],
idx($artifact, 'type'));
}
} else {
$icon = PHUIStatusItemView::ICON_QUESTION;
$color = 'red';
$icon_label = pht('Unknown Input');
$has_conflicts = true;
$error = pht(
'This input is bound to an artifact ("%s") which does not exist '.
'at this stage in the build process.',
$key);
}
} else {
$icon = PHUIStatusItemView::ICON_DOWN;
$color = 'green';
$icon_label = pht('Valid Output');
}
}
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($artifact['name'])
->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
index ca43de75e5..09da4df9c9 100644
--- a/src/applications/harbormaster/controller/HarbormasterStepEditController.php
+++ b/src/applications/harbormaster/controller/HarbormasterStepEditController.php
@@ -1,187 +1,204 @@
<?php
final class HarbormasterStepEditController
extends HarbormasterController {
private $id;
private $planID;
private $className;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
$this->planID = idx($data, 'plan');
$this->className = idx($data, 'class');
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$this->requireApplicationCapability(
HarbormasterCapabilityManagePlans::CAPABILITY);
if ($this->id) {
$step = id(new HarbormasterBuildStepQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->executeOne();
if (!$step) {
return new Aphront404Response();
}
$plan = $step->getBuildPlan();
$is_new = false;
} else {
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withIDs(array($this->planID))
->executeOne();
if (!$plan) {
return new Aphront404Response();
}
$impl = HarbormasterBuildStepImplementation::getImplementation(
$this->className);
if (!$impl) {
return new Aphront404Response();
}
$step = HarbormasterBuildStep::initializeNewStep($viewer)
->setBuildPlanPHID($plan->getPHID())
->setClassName($this->className);
$is_new = true;
}
$plan_uri = $this->getApplicationURI('plan/'.$plan->getID().'/');
$implementation = $step->getStepImplementation();
$field_list = PhabricatorCustomField::getObjectFields(
$step,
PhabricatorCustomField::ROLE_EDIT);
$field_list
->setViewer($viewer)
->readFieldsFromStorage($step);
+ $e_name = true;
+ $v_name = $step->getName();
+
$errors = array();
$validation_exception = null;
if ($request->isFormPost()) {
+ $e_name = null;
+ $v_name = $request->getStr('name');
+
$xactions = $field_list->buildFieldTransactionsFromRequest(
new HarbormasterBuildStepTransaction(),
$request);
$editor = id(new HarbormasterBuildStepEditor())
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request);
+ $name_xaction = id(new HarbormasterBuildStepTransaction())
+ ->setTransactionType(HarbormasterBuildStepTransaction::TYPE_NAME)
+ ->setNewValue($v_name);
+ array_unshift($xactions, $name_xaction);
+
if ($is_new) {
// This is okay, but a little iffy. We should move it inside the editor
// if we create plans elsewhere.
$steps = $plan->loadOrderedBuildSteps();
$step->setSequence(count($steps) + 1);
// When creating a new step, make sure we have a create transaction
// so we'll apply the transactions even if the step has no
// configurable options.
$create_xaction = id(new HarbormasterBuildStepTransaction())
->setTransactionType(HarbormasterBuildStepTransaction::TYPE_CREATE);
array_unshift($xactions, $create_xaction);
}
try {
$editor->applyTransactions($step, $xactions);
return id(new AphrontRedirectResponse())->setURI($plan_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
}
}
$form = id(new AphrontFormView())
- ->setUser($viewer);
+ ->setUser($viewer)
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName('name')
+ ->setLabel(pht('Name'))
+ ->setError($e_name)
+ ->setValue($v_name));
$field_list->appendFieldsToForm($form);
if ($is_new) {
$submit = pht('Create Build Step');
$header = pht('New Step: %s', $implementation->getName());
$crumb = pht('Add Step');
} else {
$submit = pht('Save Build Step');
$header = pht('Edit Step: %s', $implementation->getName());
$crumb = pht('Edit Step');
}
$form->appendChild(
id(new AphrontFormSubmitControl())
->setValue($submit)
->addCancelButton($plan_uri));
$box = id(new PHUIObjectBoxView())
->setHeaderText($header)
->setValidationException($validation_exception)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
$id = $plan->getID();
$crumbs->addTextCrumb(pht('Plan %d', $id), $plan_uri);
$crumbs->addTextCrumb($crumb);
$variables = $this->renderBuildVariablesTable();
if ($is_new) {
$xaction_view = null;
} else {
$xactions = id(new HarbormasterBuildStepTransactionQuery())
->setViewer($viewer)
->withObjectPHIDs(array($step->getPHID()))
->execute();
$xaction_view = id(new PhabricatorApplicationTransactionView())
->setUser($viewer)
->setObjectPHID($step->getPHID())
->setTransactions($xactions)
->setShouldTerminate(true);
}
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$variables,
$xaction_view,
),
array(
'title' => $implementation->getName(),
));
}
private function renderBuildVariablesTable() {
$viewer = $this->getRequest()->getUser();
$variables = HarbormasterBuild::getAvailableBuildVariables();
ksort($variables);
$rows = array();
$rows[] = pht(
'The following variables can be used in most fields. To reference '.
'a variable, use `${name}` in a field.');
$rows[] = pht('| Variable | Description |');
$rows[] = '|---|---|';
foreach ($variables as $name => $description) {
$rows[] = '| `'.$name.'` | '.$description.' |';
}
$rows = implode("\n", $rows);
$form = id(new AphrontFormView())
->setUser($viewer)
->appendRemarkupInstructions($rows);
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Build Variables'))
->appendChild($form);
}
}
diff --git a/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php b/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php
index 8c5ff83bbe..cc7124f5da 100644
--- a/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php
+++ b/src/applications/harbormaster/editor/HarbormasterBuildStepEditor.php
@@ -1,62 +1,73 @@
<?php
final class HarbormasterBuildStepEditor
extends PhabricatorApplicationTransactionEditor {
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = HarbormasterBuildStepTransaction::TYPE_CREATE;
+ $types[] = HarbormasterBuildStepTransaction::TYPE_NAME;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case HarbormasterBuildStepTransaction::TYPE_CREATE:
return null;
+ case HarbormasterBuildStepTransaction::TYPE_NAME:
+ if ($this->getIsNewObject()) {
+ return null;
+ }
+ return $object->getName();
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case HarbormasterBuildStepTransaction::TYPE_CREATE:
return true;
+ case HarbormasterBuildStepTransaction::TYPE_NAME:
+ return $xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case HarbormasterBuildStepTransaction::TYPE_CREATE:
return;
+ case HarbormasterBuildStepTransaction::TYPE_NAME:
+ return $object->setName($xaction->getNewValue());
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case HarbormasterBuildStepTransaction::TYPE_CREATE:
+ case HarbormasterBuildStepTransaction::TYPE_NAME:
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
}
diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
index c545bcaf03..0c994701e1 100644
--- a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
@@ -1,176 +1,190 @@
<?php
final class HarbormasterBuildTarget extends HarbormasterDAO
implements PhabricatorPolicyInterface {
+ protected $name;
protected $buildPHID;
protected $buildStepPHID;
protected $className;
protected $details;
protected $variables;
protected $targetStatus;
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';
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())
+ ->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);
}
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 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 getName() {
+ if (strlen($this->name)) {
+ return $this->name;
+ }
+
+ try {
+ return $this->getImplementation()->getName();
+ } catch (Exception $e) {
+ return $this->getClassName();
+ }
+ }
+
private function getBuildTargetVariables() {
return array(
'target.phid' => $this->getPHID(),
);
}
/* -( 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;
}
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/storage/configuration/HarbormasterBuildStep.php b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php
index 37b9377c40..c412e09840 100644
--- a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php
+++ b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php
@@ -1,108 +1,117 @@
<?php
final class HarbormasterBuildStep extends HarbormasterDAO
implements
PhabricatorPolicyInterface,
PhabricatorCustomFieldInterface {
+ protected $name;
protected $buildPlanPHID;
protected $className;
protected $details = array();
protected $sequence;
private $buildPlan = self::ATTACHABLE;
private $customFields = self::ATTACHABLE;
private $implementation;
public static function initializeNewStep(PhabricatorUser $actor) {
return id(new HarbormasterBuildStep());
}
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 getName() {
+ if (strlen($this->name)) {
+ return $this->name;
+ }
+
+ return $this->getStepImplementation()->getName();
+ }
+
public function getStepImplementation() {
if ($this->implementation === null) {
$obj = HarbormasterBuildStepImplementation::requireImplementation(
$this->className);
$obj->loadSettings($this);
$this->implementation = $obj;
}
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;
}
}
diff --git a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStepTransaction.php b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStepTransaction.php
index ca04a9818b..f1e75d1fb3 100644
--- a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStepTransaction.php
+++ b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStepTransaction.php
@@ -1,60 +1,61 @@
<?php
final class HarbormasterBuildStepTransaction
extends PhabricatorApplicationTransaction {
const TYPE_CREATE = 'harbormaster:step:create';
+ const TYPE_NAME = 'harbormaster:step:name';
public function getApplicationName() {
return 'harbormaster';
}
public function getApplicationTransactionType() {
return HarbormasterPHIDTypeBuildStep::TYPECONST;
}
public function getTitle() {
$author_phid = $this->getAuthorPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_CREATE:
return pht(
'%s created this build step.',
$this->renderHandleLink($author_phid));
}
return parent::getTitle();
}
public function getIcon() {
$author_phid = $this->getAuthorPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_CREATE:
return 'fa-plus';
}
return parent::getIcon();
}
public function getColor() {
$author_phid = $this->getAuthorPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_CREATE:
return 'green';
}
return parent::getColor();
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jul 29, 5:46 AM (2 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
188076
Default Alt Text
(52 KB)

Event Timeline