2013-11-08 18:09:03 -08:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class HarbormasterBuildViewController
|
|
|
|
extends HarbormasterController {
|
|
|
|
|
Add `harbormaster.createartifact`
Summary:
Ref T8659. In the general case, this eventually allows build processes to do things like:
- Upload build results (like a ".app" or ".exe" or other binary).
- Pass complex results between build steps (e.g., build step A does something hard and build step B uses it to do something else).
Today, we're a long way away from having the infrastructure for that. However, it is useful to let third party build processes (like Jenkins) upload URIs that link back to the external build results.
This adds `harbormaster.createartifact` so they can do that. The only useful thing to do with this method today is have your Jenkins build do this:
params = array(
"uri": "https://jenkins.mycompany.com/build/23923/details/",
"name": "View Build Results in Jenkins",
"ui.external": true,
);
harbormaster.createartifact(target, 'uri', params);
Then (after the next diff) we'll show a link in Differential and a prominent link in Harbormaster. I didn't actually do the UI stuff in this diff since it's already pretty big.
This change moves a lot of code around, too:
- Adds PHIDs to artifacts.
- It modularizes build artifact types (currently "file", "host" and "URI").
- It formalizes build artifact parameters and construction:
- This lets me generate usable documentation about how to create artifacts.
- This prevents users from doing dangerous or policy-violating things.
- It does some other general modernization.
Test Plan:
{F715633}
{F715634}
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T8659
Differential Revision: https://secure.phabricator.com/D13900
2015-08-15 07:28:56 -07:00
|
|
|
public function handleRequest(AphrontRequest $request) {
|
2013-11-08 18:09:03 -08:00
|
|
|
$request = $this->getRequest();
|
|
|
|
$viewer = $request->getUser();
|
|
|
|
|
Add `harbormaster.createartifact`
Summary:
Ref T8659. In the general case, this eventually allows build processes to do things like:
- Upload build results (like a ".app" or ".exe" or other binary).
- Pass complex results between build steps (e.g., build step A does something hard and build step B uses it to do something else).
Today, we're a long way away from having the infrastructure for that. However, it is useful to let third party build processes (like Jenkins) upload URIs that link back to the external build results.
This adds `harbormaster.createartifact` so they can do that. The only useful thing to do with this method today is have your Jenkins build do this:
params = array(
"uri": "https://jenkins.mycompany.com/build/23923/details/",
"name": "View Build Results in Jenkins",
"ui.external": true,
);
harbormaster.createartifact(target, 'uri', params);
Then (after the next diff) we'll show a link in Differential and a prominent link in Harbormaster. I didn't actually do the UI stuff in this diff since it's already pretty big.
This change moves a lot of code around, too:
- Adds PHIDs to artifacts.
- It modularizes build artifact types (currently "file", "host" and "URI").
- It formalizes build artifact parameters and construction:
- This lets me generate usable documentation about how to create artifacts.
- This prevents users from doing dangerous or policy-violating things.
- It does some other general modernization.
Test Plan:
{F715633}
{F715634}
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T8659
Differential Revision: https://secure.phabricator.com/D13900
2015-08-15 07:28:56 -07:00
|
|
|
$id = $request->getURIData('id');
|
2014-08-21 22:55:24 +10:00
|
|
|
$generation = $request->getInt('g');
|
2013-11-08 18:09:03 -08:00
|
|
|
|
|
|
|
$build = id(new HarbormasterBuildQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withIDs(array($id))
|
|
|
|
->executeOne();
|
|
|
|
if (!$build) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
2014-08-06 10:34:39 +10:00
|
|
|
require_celerity_resource('harbormaster-css');
|
|
|
|
|
2014-06-09 11:36:49 -07:00
|
|
|
$title = pht('Build %d', $id);
|
2013-11-08 18:09:03 -08:00
|
|
|
|
|
|
|
$header = id(new PHUIHeaderView())
|
|
|
|
->setHeader($title)
|
|
|
|
->setUser($viewer)
|
|
|
|
->setPolicyObject($build);
|
|
|
|
|
2014-01-06 14:12:05 -08:00
|
|
|
if ($build->isRestarting()) {
|
2014-05-18 16:10:54 -07:00
|
|
|
$header->setStatus('fa-exclamation-triangle', 'red', pht('Restarting'));
|
2015-09-21 12:07:24 -07:00
|
|
|
} else if ($build->isPausing()) {
|
2014-08-08 08:25:04 +10:00
|
|
|
$header->setStatus('fa-exclamation-triangle', 'red', pht('Pausing'));
|
2014-01-06 14:12:05 -08:00
|
|
|
} else if ($build->isResuming()) {
|
2014-05-18 16:10:54 -07:00
|
|
|
$header->setStatus('fa-exclamation-triangle', 'red', pht('Resuming'));
|
2015-09-21 12:07:24 -07:00
|
|
|
} else if ($build->isAborting()) {
|
|
|
|
$header->setStatus('fa-exclamation-triangle', 'red', pht('Aborting'));
|
2014-01-06 14:12:05 -08:00
|
|
|
}
|
|
|
|
|
2013-11-08 18:09:03 -08:00
|
|
|
$box = id(new PHUIObjectBoxView())
|
|
|
|
->setHeader($header);
|
|
|
|
|
|
|
|
$actions = $this->buildActionList($build);
|
|
|
|
$this->buildPropertyLists($box, $build, $actions);
|
|
|
|
|
|
|
|
$crumbs = $this->buildApplicationCrumbs();
|
2015-06-21 13:45:38 -07:00
|
|
|
$this->addBuildableCrumb($crumbs, $build->getBuildable());
|
2013-12-18 17:47:34 -08:00
|
|
|
$crumbs->addTextCrumb($title);
|
2013-11-08 18:09:03 -08:00
|
|
|
|
2014-08-21 22:55:24 +10:00
|
|
|
if ($generation === null || $generation > $build->getBuildGeneration() ||
|
|
|
|
$generation < 0) {
|
|
|
|
$generation = $build->getBuildGeneration();
|
|
|
|
}
|
|
|
|
|
2013-12-05 12:01:12 +11:00
|
|
|
$build_targets = id(new HarbormasterBuildTargetQuery())
|
|
|
|
->setViewer($viewer)
|
2014-08-01 08:09:15 +10:00
|
|
|
->needBuildSteps(true)
|
2013-12-05 12:01:12 +11:00
|
|
|
->withBuildPHIDs(array($build->getPHID()))
|
2014-08-21 22:55:24 +10:00
|
|
|
->withBuildGenerations(array($generation))
|
2013-12-05 12:01:12 +11:00
|
|
|
->execute();
|
|
|
|
|
2014-03-25 16:11:28 -07:00
|
|
|
if ($build_targets) {
|
|
|
|
$messages = id(new HarbormasterBuildMessageQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withBuildTargetPHIDs(mpull($build_targets, 'getPHID'))
|
|
|
|
->execute();
|
|
|
|
$messages = mgroup($messages, 'getBuildTargetPHID');
|
|
|
|
} else {
|
|
|
|
$messages = array();
|
|
|
|
}
|
|
|
|
|
2015-08-15 07:29:26 -07:00
|
|
|
if ($build_targets) {
|
|
|
|
$artifacts = id(new HarbormasterBuildArtifactQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withBuildTargetPHIDs(mpull($build_targets, 'getPHID'))
|
|
|
|
->execute();
|
|
|
|
$artifacts = msort($artifacts, 'getArtifactKey');
|
|
|
|
$artifacts = mgroup($artifacts, 'getBuildTargetPHID');
|
|
|
|
} else {
|
|
|
|
$artifacts = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-12-05 12:01:12 +11:00
|
|
|
$targets = array();
|
|
|
|
foreach ($build_targets as $build_target) {
|
|
|
|
$header = id(new PHUIHeaderView())
|
2014-08-01 08:09:15 +10:00
|
|
|
->setHeader($build_target->getName())
|
2013-12-05 12:01:12 +11:00
|
|
|
->setUser($viewer);
|
2014-08-01 08:09:15 +10:00
|
|
|
|
|
|
|
$target_box = id(new PHUIObjectBoxView())
|
|
|
|
->setHeader($header);
|
|
|
|
|
2013-12-05 12:01:12 +11:00
|
|
|
$properties = new PHUIPropertyListView();
|
2015-08-15 07:29:26 -07:00
|
|
|
|
|
|
|
$target_artifacts = idx($artifacts, $build_target->getPHID(), array());
|
|
|
|
|
|
|
|
$links = array();
|
|
|
|
$type_uri = HarbormasterURIArtifact::ARTIFACTCONST;
|
|
|
|
foreach ($target_artifacts as $artifact) {
|
|
|
|
if ($artifact->getArtifactType() == $type_uri) {
|
|
|
|
$impl = $artifact->getArtifactImplementation();
|
|
|
|
if ($impl->isExternalLink()) {
|
|
|
|
$links[] = $impl->renderLink();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($links) {
|
|
|
|
$links = phutil_implode_html(phutil_tag('br'), $links);
|
|
|
|
$properties->addProperty(
|
|
|
|
pht('External Link'),
|
|
|
|
$links);
|
|
|
|
}
|
|
|
|
|
2014-08-01 08:09:15 +10:00
|
|
|
$status_view = new PHUIStatusListView();
|
|
|
|
|
|
|
|
$item = new PHUIStatusItemView();
|
|
|
|
|
|
|
|
$status = $build_target->getTargetStatus();
|
|
|
|
$status_name =
|
|
|
|
HarbormasterBuildTarget::getBuildTargetStatusName($status);
|
|
|
|
$icon = HarbormasterBuildTarget::getBuildTargetStatusIcon($status);
|
|
|
|
$color = HarbormasterBuildTarget::getBuildTargetStatusColor($status);
|
|
|
|
|
|
|
|
$item->setTarget($status_name);
|
|
|
|
$item->setIcon($icon, $color);
|
|
|
|
$status_view->addItem($item);
|
|
|
|
|
2015-06-21 13:45:38 -07:00
|
|
|
$when = array();
|
|
|
|
$started = $build_target->getDateStarted();
|
|
|
|
$now = PhabricatorTime::getNow();
|
|
|
|
if ($started) {
|
|
|
|
$ended = $build_target->getDateCompleted();
|
|
|
|
if ($ended) {
|
|
|
|
$when[] = pht(
|
|
|
|
'Completed at %s',
|
|
|
|
phabricator_datetime($started, $viewer));
|
|
|
|
|
|
|
|
$duration = ($ended - $started);
|
|
|
|
if ($duration) {
|
|
|
|
$when[] = pht(
|
|
|
|
'Built for %s',
|
|
|
|
phutil_format_relative_time_detailed($duration));
|
|
|
|
} else {
|
|
|
|
$when[] = pht('Built instantly');
|
|
|
|
}
|
2014-08-12 08:34:43 +10:00
|
|
|
} else {
|
2015-06-21 13:45:38 -07:00
|
|
|
$when[] = pht(
|
|
|
|
'Started at %s',
|
|
|
|
phabricator_datetime($started, $viewer));
|
|
|
|
$duration = ($now - $started);
|
|
|
|
if ($duration) {
|
|
|
|
$when[] = pht(
|
|
|
|
'Running for %s',
|
|
|
|
phutil_format_relative_time_detailed($duration));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$created = $build_target->getDateCreated();
|
|
|
|
$when[] = pht(
|
|
|
|
'Queued at %s',
|
|
|
|
phabricator_datetime($started, $viewer));
|
|
|
|
$duration = ($now - $created);
|
|
|
|
if ($duration) {
|
|
|
|
$when[] = pht(
|
|
|
|
'Waiting for %s',
|
|
|
|
phutil_format_relative_time_detailed($duration));
|
2014-08-12 08:34:43 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-21 13:45:38 -07:00
|
|
|
$properties->addProperty(
|
|
|
|
pht('When'),
|
|
|
|
phutil_implode_html(" \xC2\xB7 ", $when));
|
|
|
|
|
2014-08-01 08:09:15 +10:00
|
|
|
$properties->addProperty(pht('Status'), $status_view);
|
|
|
|
|
|
|
|
$target_box->addPropertyList($properties, pht('Overview'));
|
|
|
|
|
2014-08-28 08:20:11 +10:00
|
|
|
$step = $build_target->getBuildStep();
|
|
|
|
|
|
|
|
if ($step) {
|
|
|
|
$description = $step->getDescription();
|
|
|
|
if ($description) {
|
|
|
|
$rendered = PhabricatorMarkupEngine::renderOneObject(
|
|
|
|
id(new PhabricatorMarkupOneOff())
|
|
|
|
->setContent($description)
|
|
|
|
->setPreserveLinebreaks(true),
|
|
|
|
'default',
|
|
|
|
$viewer);
|
|
|
|
|
2015-09-19 11:29:01 -07:00
|
|
|
$properties->addSectionHeader(
|
|
|
|
pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
|
2014-08-28 08:20:11 +10:00
|
|
|
$properties->addTextContent($rendered);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$target_box->setFormErrors(
|
|
|
|
array(
|
|
|
|
pht(
|
|
|
|
'This build step has since been deleted on the build plan. '.
|
|
|
|
'Some information may be omitted.'),
|
|
|
|
));
|
2014-08-01 08:09:15 +10:00
|
|
|
}
|
2013-12-05 12:01:12 +11:00
|
|
|
|
|
|
|
$details = $build_target->getDetails();
|
2015-08-10 14:15:43 -07:00
|
|
|
$properties = new PHUIPropertyListView();
|
|
|
|
foreach ($details as $key => $value) {
|
|
|
|
$properties->addProperty($key, $value);
|
2013-12-05 12:01:12 +11:00
|
|
|
}
|
2015-08-10 14:15:43 -07:00
|
|
|
$target_box->addPropertyList($properties, pht('Configuration'));
|
2013-12-05 12:01:12 +11:00
|
|
|
|
|
|
|
$variables = $build_target->getVariables();
|
2015-08-10 14:15:43 -07:00
|
|
|
$properties = new PHUIPropertyListView();
|
|
|
|
$properties->addRawContent($this->buildProperties($variables));
|
|
|
|
$target_box->addPropertyList($properties, pht('Variables'));
|
2013-12-05 12:01:12 +11:00
|
|
|
|
2015-08-15 07:29:26 -07:00
|
|
|
$artifacts_tab = $this->buildArtifacts($build_target, $target_artifacts);
|
2015-08-10 14:15:43 -07:00
|
|
|
$properties = new PHUIPropertyListView();
|
2015-08-15 07:29:26 -07:00
|
|
|
$properties->addRawContent($artifacts_tab);
|
2015-08-10 14:15:43 -07:00
|
|
|
$target_box->addPropertyList($properties, pht('Artifacts'));
|
2014-08-06 10:34:39 +10:00
|
|
|
|
|
|
|
$build_messages = idx($messages, $build_target->getPHID(), array());
|
2015-08-10 14:15:43 -07:00
|
|
|
$properties = new PHUIPropertyListView();
|
|
|
|
$properties->addRawContent($this->buildMessages($build_messages));
|
|
|
|
$target_box->addPropertyList($properties, pht('Messages'));
|
2014-08-06 10:34:39 +10:00
|
|
|
|
2014-08-01 08:09:15 +10:00
|
|
|
$properties = new PHUIPropertyListView();
|
2015-06-21 13:45:38 -07:00
|
|
|
$properties->addProperty(
|
|
|
|
pht('Build Target ID'),
|
|
|
|
$build_target->getID());
|
|
|
|
$properties->addProperty(
|
|
|
|
pht('Build Target PHID'),
|
|
|
|
$build_target->getPHID());
|
2014-08-01 08:09:15 +10:00
|
|
|
$target_box->addPropertyList($properties, pht('Metadata'));
|
|
|
|
|
|
|
|
$targets[] = $target_box;
|
2013-12-05 12:01:12 +11:00
|
|
|
|
|
|
|
$targets[] = $this->buildLog($build, $build_target);
|
|
|
|
}
|
|
|
|
|
Transactions - deploy buildTransactionTimeline to remaining applications
Summary:
Ref T4712. Specifically...
- Differential
- needed getApplicationTransactionViewObject() implemented
- Audit
- needed getApplicationTransactionViewObject() implemented
- Repository
- one object needed PhabricatorApplicationTransactionInterface implemented
- setShouldTerminate(true)
- Ponder
- BONUS BUG FIX - leaving a comment on an answer had a bad redirect URI
- both PonderQuestion and PonderAnswer needed PhabricatorApplicationTransactionInterface implemented
- setShouldTerminate(true) on both "history" controllers
- left a "TODO" on buildAnswers on the question view controller, which is non-standard and should be re-written eventually
- Phortune
- BONUS BUG FIX - fix new user "createNewAccount" code to not fatal
- PhortuneAccount, PhortuneMerchant, and PhortuneCart needed PhabricatorApplicationTransactionInterface implemented
- setShouldTerminate(true) on Account view, merchant view, and cart view controller
- Fund
- Legalpad
- Nuance
- NuanceSource needed PhabricatorApplicationTransactionInterface implemented
- Releeph (this product is kind of a mess...)
- HACKQUEST - had to manually create an arcanist project to even be able to make a "product" and get started...!
- BONUS BUG FIX - make sure to "setName" on product edit
- ReleephProject (should be ReleepProduct...?), ReleephBranch, and ReleepRequest needed PhabricatorApplicationTransactionInterface implemented
- Harbormaster
- HarbormasterBuildable, HarbormasterBuild, HarbormasterBuildPlan, and HarbormasterBuildStep all needed PhabricatorApplicationTransactionInterface implemented
- setShouldTerminate(true) all over the place
Test Plan: foreach application, viewed the timeline(s) and made sure they still rendered
Reviewers: epriestley
Reviewed By: epriestley
Subscribers: Korvin, epriestley
Maniphest Tasks: T4712
Differential Revision: https://secure.phabricator.com/D10925
2014-12-03 15:35:47 -08:00
|
|
|
$timeline = $this->buildTransactionTimeline(
|
|
|
|
$build,
|
|
|
|
new HarbormasterBuildTransactionQuery());
|
|
|
|
$timeline->setShouldTerminate(true);
|
2014-05-15 07:02:30 -07:00
|
|
|
|
2013-11-08 18:09:03 -08:00
|
|
|
return $this->buildApplicationPage(
|
|
|
|
array(
|
|
|
|
$crumbs,
|
|
|
|
$box,
|
2014-05-15 07:02:30 -07:00
|
|
|
$targets,
|
|
|
|
$timeline,
|
2013-11-08 18:09:03 -08:00
|
|
|
),
|
|
|
|
array(
|
|
|
|
'title' => $title,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2015-08-15 07:29:26 -07:00
|
|
|
private function buildArtifacts(
|
|
|
|
HarbormasterBuildTarget $build_target,
|
|
|
|
array $artifacts) {
|
Add `harbormaster.createartifact`
Summary:
Ref T8659. In the general case, this eventually allows build processes to do things like:
- Upload build results (like a ".app" or ".exe" or other binary).
- Pass complex results between build steps (e.g., build step A does something hard and build step B uses it to do something else).
Today, we're a long way away from having the infrastructure for that. However, it is useful to let third party build processes (like Jenkins) upload URIs that link back to the external build results.
This adds `harbormaster.createartifact` so they can do that. The only useful thing to do with this method today is have your Jenkins build do this:
params = array(
"uri": "https://jenkins.mycompany.com/build/23923/details/",
"name": "View Build Results in Jenkins",
"ui.external": true,
);
harbormaster.createartifact(target, 'uri', params);
Then (after the next diff) we'll show a link in Differential and a prominent link in Harbormaster. I didn't actually do the UI stuff in this diff since it's already pretty big.
This change moves a lot of code around, too:
- Adds PHIDs to artifacts.
- It modularizes build artifact types (currently "file", "host" and "URI").
- It formalizes build artifact parameters and construction:
- This lets me generate usable documentation about how to create artifacts.
- This prevents users from doing dangerous or policy-violating things.
- It does some other general modernization.
Test Plan:
{F715633}
{F715634}
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T8659
Differential Revision: https://secure.phabricator.com/D13900
2015-08-15 07:28:56 -07:00
|
|
|
$viewer = $this->getViewer();
|
2013-12-05 14:06:22 +11:00
|
|
|
|
Add `harbormaster.createartifact`
Summary:
Ref T8659. In the general case, this eventually allows build processes to do things like:
- Upload build results (like a ".app" or ".exe" or other binary).
- Pass complex results between build steps (e.g., build step A does something hard and build step B uses it to do something else).
Today, we're a long way away from having the infrastructure for that. However, it is useful to let third party build processes (like Jenkins) upload URIs that link back to the external build results.
This adds `harbormaster.createartifact` so they can do that. The only useful thing to do with this method today is have your Jenkins build do this:
params = array(
"uri": "https://jenkins.mycompany.com/build/23923/details/",
"name": "View Build Results in Jenkins",
"ui.external": true,
);
harbormaster.createartifact(target, 'uri', params);
Then (after the next diff) we'll show a link in Differential and a prominent link in Harbormaster. I didn't actually do the UI stuff in this diff since it's already pretty big.
This change moves a lot of code around, too:
- Adds PHIDs to artifacts.
- It modularizes build artifact types (currently "file", "host" and "URI").
- It formalizes build artifact parameters and construction:
- This lets me generate usable documentation about how to create artifacts.
- This prevents users from doing dangerous or policy-violating things.
- It does some other general modernization.
Test Plan:
{F715633}
{F715634}
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T8659
Differential Revision: https://secure.phabricator.com/D13900
2015-08-15 07:28:56 -07:00
|
|
|
$rows = array();
|
2013-12-05 14:06:22 +11:00
|
|
|
foreach ($artifacts as $artifact) {
|
Add `harbormaster.createartifact`
Summary:
Ref T8659. In the general case, this eventually allows build processes to do things like:
- Upload build results (like a ".app" or ".exe" or other binary).
- Pass complex results between build steps (e.g., build step A does something hard and build step B uses it to do something else).
Today, we're a long way away from having the infrastructure for that. However, it is useful to let third party build processes (like Jenkins) upload URIs that link back to the external build results.
This adds `harbormaster.createartifact` so they can do that. The only useful thing to do with this method today is have your Jenkins build do this:
params = array(
"uri": "https://jenkins.mycompany.com/build/23923/details/",
"name": "View Build Results in Jenkins",
"ui.external": true,
);
harbormaster.createartifact(target, 'uri', params);
Then (after the next diff) we'll show a link in Differential and a prominent link in Harbormaster. I didn't actually do the UI stuff in this diff since it's already pretty big.
This change moves a lot of code around, too:
- Adds PHIDs to artifacts.
- It modularizes build artifact types (currently "file", "host" and "URI").
- It formalizes build artifact parameters and construction:
- This lets me generate usable documentation about how to create artifacts.
- This prevents users from doing dangerous or policy-violating things.
- It does some other general modernization.
Test Plan:
{F715633}
{F715634}
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T8659
Differential Revision: https://secure.phabricator.com/D13900
2015-08-15 07:28:56 -07:00
|
|
|
$impl = $artifact->getArtifactImplementation();
|
|
|
|
|
|
|
|
if ($impl) {
|
|
|
|
$summary = $impl->renderArtifactSummary($viewer);
|
|
|
|
$type_name = $impl->getArtifactTypeName();
|
|
|
|
} else {
|
|
|
|
$summary = pht('<Unknown Artifact Type>');
|
|
|
|
$type_name = $artifact->getType();
|
2014-08-12 08:34:43 +10:00
|
|
|
}
|
Add `harbormaster.createartifact`
Summary:
Ref T8659. In the general case, this eventually allows build processes to do things like:
- Upload build results (like a ".app" or ".exe" or other binary).
- Pass complex results between build steps (e.g., build step A does something hard and build step B uses it to do something else).
Today, we're a long way away from having the infrastructure for that. However, it is useful to let third party build processes (like Jenkins) upload URIs that link back to the external build results.
This adds `harbormaster.createartifact` so they can do that. The only useful thing to do with this method today is have your Jenkins build do this:
params = array(
"uri": "https://jenkins.mycompany.com/build/23923/details/",
"name": "View Build Results in Jenkins",
"ui.external": true,
);
harbormaster.createartifact(target, 'uri', params);
Then (after the next diff) we'll show a link in Differential and a prominent link in Harbormaster. I didn't actually do the UI stuff in this diff since it's already pretty big.
This change moves a lot of code around, too:
- Adds PHIDs to artifacts.
- It modularizes build artifact types (currently "file", "host" and "URI").
- It formalizes build artifact parameters and construction:
- This lets me generate usable documentation about how to create artifacts.
- This prevents users from doing dangerous or policy-violating things.
- It does some other general modernization.
Test Plan:
{F715633}
{F715634}
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T8659
Differential Revision: https://secure.phabricator.com/D13900
2015-08-15 07:28:56 -07:00
|
|
|
|
|
|
|
$rows[] = array(
|
|
|
|
$artifact->getArtifactKey(),
|
|
|
|
$type_name,
|
|
|
|
$summary,
|
|
|
|
);
|
2013-12-05 14:06:22 +11:00
|
|
|
}
|
|
|
|
|
Add `harbormaster.createartifact`
Summary:
Ref T8659. In the general case, this eventually allows build processes to do things like:
- Upload build results (like a ".app" or ".exe" or other binary).
- Pass complex results between build steps (e.g., build step A does something hard and build step B uses it to do something else).
Today, we're a long way away from having the infrastructure for that. However, it is useful to let third party build processes (like Jenkins) upload URIs that link back to the external build results.
This adds `harbormaster.createartifact` so they can do that. The only useful thing to do with this method today is have your Jenkins build do this:
params = array(
"uri": "https://jenkins.mycompany.com/build/23923/details/",
"name": "View Build Results in Jenkins",
"ui.external": true,
);
harbormaster.createartifact(target, 'uri', params);
Then (after the next diff) we'll show a link in Differential and a prominent link in Harbormaster. I didn't actually do the UI stuff in this diff since it's already pretty big.
This change moves a lot of code around, too:
- Adds PHIDs to artifacts.
- It modularizes build artifact types (currently "file", "host" and "URI").
- It formalizes build artifact parameters and construction:
- This lets me generate usable documentation about how to create artifacts.
- This prevents users from doing dangerous or policy-violating things.
- It does some other general modernization.
Test Plan:
{F715633}
{F715634}
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T8659
Differential Revision: https://secure.phabricator.com/D13900
2015-08-15 07:28:56 -07:00
|
|
|
$table = id(new AphrontTableView($rows))
|
|
|
|
->setNoDataString(pht('This target has no associated artifacts.'))
|
|
|
|
->setHeaders(
|
|
|
|
array(
|
|
|
|
pht('Key'),
|
|
|
|
pht('Type'),
|
|
|
|
pht('Summary'),
|
|
|
|
))
|
|
|
|
->setColumnClasses(
|
|
|
|
array(
|
|
|
|
'pri',
|
|
|
|
'',
|
|
|
|
'wide',
|
|
|
|
));
|
|
|
|
|
|
|
|
return $table;
|
2013-12-05 14:06:22 +11:00
|
|
|
}
|
|
|
|
|
2013-12-05 12:01:12 +11:00
|
|
|
private function buildLog(
|
|
|
|
HarbormasterBuild $build,
|
|
|
|
HarbormasterBuildTarget $build_target) {
|
|
|
|
|
2013-11-08 18:09:03 -08:00
|
|
|
$request = $this->getRequest();
|
|
|
|
$viewer = $request->getUser();
|
|
|
|
$limit = $request->getInt('l', 25);
|
|
|
|
|
|
|
|
$logs = id(new HarbormasterBuildLogQuery())
|
|
|
|
->setViewer($viewer)
|
2013-12-05 12:01:12 +11:00
|
|
|
->withBuildTargetPHIDs(array($build_target->getPHID()))
|
2013-11-08 18:09:03 -08:00
|
|
|
->execute();
|
|
|
|
|
2014-08-06 10:28:13 +10:00
|
|
|
$empty_logs = array();
|
|
|
|
|
2013-11-08 18:09:03 -08:00
|
|
|
$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;
|
|
|
|
}
|
|
|
|
}
|
2014-08-06 10:28:13 +10:00
|
|
|
|
|
|
|
$id = null;
|
|
|
|
$is_empty = false;
|
|
|
|
if (count($lines) === 1 && trim($lines[0]) === '') {
|
|
|
|
// Prevent Harbormaster from showing empty build logs.
|
|
|
|
$id = celerity_generate_unique_node_id();
|
|
|
|
$empty_logs[] = $id;
|
|
|
|
$is_empty = true;
|
|
|
|
}
|
|
|
|
|
2013-11-08 18:09:03 -08:00
|
|
|
$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);
|
|
|
|
|
2014-08-06 10:28:13 +10:00
|
|
|
$log_box = id(new PHUIObjectBoxView())
|
2013-11-08 18:09:03 -08:00
|
|
|
->setHeader($header)
|
|
|
|
->setForm($log_view);
|
2014-08-06 10:28:13 +10:00
|
|
|
|
|
|
|
if ($is_empty) {
|
|
|
|
$log_box = phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'style' => 'display: none',
|
2014-10-08 00:01:04 +11:00
|
|
|
'id' => $id,
|
|
|
|
),
|
2014-08-06 10:28:13 +10:00
|
|
|
$log_box);
|
|
|
|
}
|
|
|
|
|
|
|
|
$log_boxes[] = $log_box;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($empty_logs) {
|
|
|
|
$hide_id = celerity_generate_unique_node_id();
|
|
|
|
|
|
|
|
Javelin::initBehavior('phabricator-reveal-content');
|
|
|
|
|
|
|
|
$expand = phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'id' => $hide_id,
|
|
|
|
'class' => 'harbormaster-empty-logs-are-hidden mlr mlt mll',
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
pht(
|
|
|
|
'%s empty logs are hidden.',
|
2015-11-03 06:54:38 +11:00
|
|
|
phutil_count($empty_logs)),
|
2014-08-06 10:28:13 +10:00
|
|
|
' ',
|
|
|
|
javelin_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '#',
|
|
|
|
'sigil' => 'reveal-content',
|
|
|
|
'meta' => array(
|
|
|
|
'showIDs' => $empty_logs,
|
|
|
|
'hideIDs' => array($hide_id),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
pht('Show all logs.')),
|
|
|
|
));
|
|
|
|
|
|
|
|
array_unshift($log_boxes, $expand);
|
2013-11-08 18:09:03 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2014-10-08 00:01:04 +11:00
|
|
|
' Lines',
|
|
|
|
));
|
2013-11-08 18:09:03 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
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}");
|
|
|
|
|
Replace "Cancel Build" with "Stop", "Resume" and "Restart"
Summary:
Ref T1049. Currently you can cancel a build, but now that we're tracking a lot more state we can stop, resume, and restart builds.
When the user issues a command against a build, I'm writing it into an auxiliary queue (`HarbormasterBuildCommand`) and then reading them out in the worker. This is mostly to avoid race messes where we try to `save()` the object in multiple places: basically, the BuildEngine is the //only// thing that writes to Build objects, and it holds a lock while it does it.
Test Plan:
- Created a plan which runs "sleep 2" a bunch of times in a row.
- Stopped, resumed, and restarted it.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, chad
Maniphest Tasks: T1049
Differential Revision: https://secure.phabricator.com/D7892
2014-01-06 12:32:20 -08:00
|
|
|
$can_restart = $build->canRestartBuild();
|
2015-09-21 12:07:24 -07:00
|
|
|
$can_pause = $build->canPauseBuild();
|
Replace "Cancel Build" with "Stop", "Resume" and "Restart"
Summary:
Ref T1049. Currently you can cancel a build, but now that we're tracking a lot more state we can stop, resume, and restart builds.
When the user issues a command against a build, I'm writing it into an auxiliary queue (`HarbormasterBuildCommand`) and then reading them out in the worker. This is mostly to avoid race messes where we try to `save()` the object in multiple places: basically, the BuildEngine is the //only// thing that writes to Build objects, and it holds a lock while it does it.
Test Plan:
- Created a plan which runs "sleep 2" a bunch of times in a row.
- Stopped, resumed, and restarted it.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, chad
Maniphest Tasks: T1049
Differential Revision: https://secure.phabricator.com/D7892
2014-01-06 12:32:20 -08:00
|
|
|
$can_resume = $build->canResumeBuild();
|
2015-09-21 12:07:24 -07:00
|
|
|
$can_abort = $build->canAbortBuild();
|
Replace "Cancel Build" with "Stop", "Resume" and "Restart"
Summary:
Ref T1049. Currently you can cancel a build, but now that we're tracking a lot more state we can stop, resume, and restart builds.
When the user issues a command against a build, I'm writing it into an auxiliary queue (`HarbormasterBuildCommand`) and then reading them out in the worker. This is mostly to avoid race messes where we try to `save()` the object in multiple places: basically, the BuildEngine is the //only// thing that writes to Build objects, and it holds a lock while it does it.
Test Plan:
- Created a plan which runs "sleep 2" a bunch of times in a row.
- Stopped, resumed, and restarted it.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, chad
Maniphest Tasks: T1049
Differential Revision: https://secure.phabricator.com/D7892
2014-01-06 12:32:20 -08:00
|
|
|
|
|
|
|
$list->addAction(
|
2013-11-08 18:09:03 -08:00
|
|
|
id(new PhabricatorActionView())
|
Replace "Cancel Build" with "Stop", "Resume" and "Restart"
Summary:
Ref T1049. Currently you can cancel a build, but now that we're tracking a lot more state we can stop, resume, and restart builds.
When the user issues a command against a build, I'm writing it into an auxiliary queue (`HarbormasterBuildCommand`) and then reading them out in the worker. This is mostly to avoid race messes where we try to `save()` the object in multiple places: basically, the BuildEngine is the //only// thing that writes to Build objects, and it holds a lock while it does it.
Test Plan:
- Created a plan which runs "sleep 2" a bunch of times in a row.
- Stopped, resumed, and restarted it.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, chad
Maniphest Tasks: T1049
Differential Revision: https://secure.phabricator.com/D7892
2014-01-06 12:32:20 -08:00
|
|
|
->setName(pht('Restart Build'))
|
2014-08-08 08:25:04 +10:00
|
|
|
->setIcon('fa-repeat')
|
Replace "Cancel Build" with "Stop", "Resume" and "Restart"
Summary:
Ref T1049. Currently you can cancel a build, but now that we're tracking a lot more state we can stop, resume, and restart builds.
When the user issues a command against a build, I'm writing it into an auxiliary queue (`HarbormasterBuildCommand`) and then reading them out in the worker. This is mostly to avoid race messes where we try to `save()` the object in multiple places: basically, the BuildEngine is the //only// thing that writes to Build objects, and it holds a lock while it does it.
Test Plan:
- Created a plan which runs "sleep 2" a bunch of times in a row.
- Stopped, resumed, and restarted it.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, chad
Maniphest Tasks: T1049
Differential Revision: https://secure.phabricator.com/D7892
2014-01-06 12:32:20 -08:00
|
|
|
->setHref($this->getApplicationURI('/build/restart/'.$id.'/'))
|
|
|
|
->setDisabled(!$can_restart)
|
|
|
|
->setWorkflow(true));
|
|
|
|
|
2014-08-08 08:25:04 +10:00
|
|
|
if ($build->canResumeBuild()) {
|
|
|
|
$list->addAction(
|
|
|
|
id(new PhabricatorActionView())
|
|
|
|
->setName(pht('Resume Build'))
|
|
|
|
->setIcon('fa-play')
|
|
|
|
->setHref($this->getApplicationURI('/build/resume/'.$id.'/'))
|
|
|
|
->setDisabled(!$can_resume)
|
|
|
|
->setWorkflow(true));
|
|
|
|
} else {
|
|
|
|
$list->addAction(
|
|
|
|
id(new PhabricatorActionView())
|
|
|
|
->setName(pht('Pause Build'))
|
|
|
|
->setIcon('fa-pause')
|
2015-09-21 12:07:24 -07:00
|
|
|
->setHref($this->getApplicationURI('/build/pause/'.$id.'/'))
|
|
|
|
->setDisabled(!$can_pause)
|
2014-08-08 08:25:04 +10:00
|
|
|
->setWorkflow(true));
|
|
|
|
}
|
2013-11-08 18:09:03 -08:00
|
|
|
|
2015-09-21 12:07:24 -07:00
|
|
|
$list->addAction(
|
|
|
|
id(new PhabricatorActionView())
|
|
|
|
->setName(pht('Abort Build'))
|
|
|
|
->setIcon('fa-exclamation-triangle')
|
|
|
|
->setHref($this->getApplicationURI('/build/abort/'.$id.'/'))
|
|
|
|
->setDisabled(!$can_abort)
|
|
|
|
->setWorkflow(true));
|
|
|
|
|
2013-11-08 18:09:03 -08:00
|
|
|
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);
|
|
|
|
|
2013-11-09 15:02:07 -08:00
|
|
|
$handles = id(new PhabricatorHandleQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withPHIDs(array(
|
|
|
|
$build->getBuildablePHID(),
|
2014-10-08 00:01:04 +11:00
|
|
|
$build->getBuildPlanPHID(),
|
|
|
|
))
|
2013-11-09 15:02:07 -08:00
|
|
|
->execute();
|
|
|
|
|
|
|
|
$properties->addProperty(
|
|
|
|
pht('Buildable'),
|
|
|
|
$handles[$build->getBuildablePHID()]->renderLink());
|
|
|
|
|
|
|
|
$properties->addProperty(
|
|
|
|
pht('Build Plan'),
|
|
|
|
$handles[$build->getBuildPlanPHID()]->renderLink());
|
|
|
|
|
2014-08-21 22:55:24 +10:00
|
|
|
$properties->addProperty(
|
|
|
|
pht('Restarts'),
|
|
|
|
$build->getBuildGeneration());
|
|
|
|
|
2014-08-01 08:10:09 +10:00
|
|
|
$properties->addProperty(
|
|
|
|
pht('Status'),
|
|
|
|
$this->getStatus($build));
|
2013-11-08 18:09:03 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
private function getStatus(HarbormasterBuild $build) {
|
2014-08-01 08:09:32 +10:00
|
|
|
$status_view = new PHUIStatusListView();
|
|
|
|
|
|
|
|
$item = new PHUIStatusItemView();
|
|
|
|
|
2015-09-21 12:07:24 -07:00
|
|
|
if ($build->isPausing()) {
|
2014-08-08 08:25:04 +10:00
|
|
|
$status_name = pht('Pausing');
|
2014-08-01 08:09:32 +10:00
|
|
|
$icon = PHUIStatusItemView::ICON_RIGHT;
|
|
|
|
$color = 'dark';
|
|
|
|
} else {
|
|
|
|
$status = $build->getBuildStatus();
|
|
|
|
$status_name =
|
|
|
|
HarbormasterBuild::getBuildStatusName($status);
|
|
|
|
$icon = HarbormasterBuild::getBuildStatusIcon($status);
|
|
|
|
$color = HarbormasterBuild::getBuildStatusColor($status);
|
2013-11-08 18:09:03 -08:00
|
|
|
}
|
2014-04-17 16:00:58 -07:00
|
|
|
|
2014-08-01 08:09:32 +10:00
|
|
|
$item->setTarget($status_name);
|
|
|
|
$item->setIcon($icon, $color);
|
|
|
|
$status_view->addItem($item);
|
|
|
|
|
|
|
|
return $status_view;
|
2013-11-08 18:09:03 -08:00
|
|
|
}
|
|
|
|
|
2014-03-25 16:11:28 -07:00
|
|
|
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',
|
|
|
|
));
|
|
|
|
|
2014-08-06 10:34:39 +10:00
|
|
|
return $table;
|
2014-03-25 16:11:28 -07:00
|
|
|
}
|
|
|
|
|
2015-06-21 13:45:38 -07:00
|
|
|
private function buildProperties(array $properties) {
|
|
|
|
ksort($properties);
|
|
|
|
|
|
|
|
$rows = array();
|
|
|
|
foreach ($properties as $key => $value) {
|
|
|
|
$rows[] = array(
|
|
|
|
$key,
|
|
|
|
$value,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$table = id(new AphrontTableView($rows))
|
|
|
|
->setHeaders(
|
|
|
|
array(
|
|
|
|
pht('Key'),
|
|
|
|
pht('Value'),
|
|
|
|
))
|
|
|
|
->setColumnClasses(
|
|
|
|
array(
|
|
|
|
'pri right',
|
|
|
|
'wide',
|
|
|
|
));
|
|
|
|
|
|
|
|
return $table;
|
|
|
|
}
|
|
|
|
|
2013-11-08 18:09:03 -08:00
|
|
|
}
|