Page MenuHomePhabricator

D13900.id33564.diff
No OneTemporary

D13900.id33564.diff

diff --git a/resources/sql/autopatches/20150814.harbormater.artifact.phid.sql b/resources/sql/autopatches/20150814.harbormater.artifact.phid.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150814.harbormater.artifact.phid.sql
@@ -0,0 +1,5 @@
+ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildartifact
+ ADD phid VARBINARY(64) NOT NULL AFTER id;
+
+UPDATE {$NAMESPACE}_harbormaster.harbormaster_buildartifact
+ SET phid = CONCAT('PHID-HMBA-', id) WHERE phid = '';
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
@@ -915,12 +915,14 @@
'FundSchemaSpec' => 'applications/fund/storage/FundSchemaSpec.php',
'HarbormasterArcLintBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcLintBuildStepImplementation.php',
'HarbormasterArcUnitBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcUnitBuildStepImplementation.php',
+ 'HarbormasterArtifact' => 'applications/harbormaster/artifact/HarbormasterArtifact.php',
'HarbormasterAutotargetsTestCase' => 'applications/harbormaster/__tests__/HarbormasterAutotargetsTestCase.php',
'HarbormasterBuild' => 'applications/harbormaster/storage/build/HarbormasterBuild.php',
'HarbormasterBuildAbortedException' => 'applications/harbormaster/exception/HarbormasterBuildAbortedException.php',
'HarbormasterBuildActionController' => 'applications/harbormaster/controller/HarbormasterBuildActionController.php',
'HarbormasterBuildArcanistAutoplan' => 'applications/harbormaster/autoplan/HarbormasterBuildArcanistAutoplan.php',
'HarbormasterBuildArtifact' => 'applications/harbormaster/storage/build/HarbormasterBuildArtifact.php',
+ 'HarbormasterBuildArtifactPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildArtifactPHIDType.php',
'HarbormasterBuildArtifactQuery' => 'applications/harbormaster/query/HarbormasterBuildArtifactQuery.php',
'HarbormasterBuildAutoplan' => 'applications/harbormaster/autoplan/HarbormasterBuildAutoplan.php',
'HarbormasterBuildCommand' => 'applications/harbormaster/storage/HarbormasterBuildCommand.php',
@@ -980,9 +982,12 @@
'HarbormasterCommandBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php',
'HarbormasterConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterConduitAPIMethod.php',
'HarbormasterController' => 'applications/harbormaster/controller/HarbormasterController.php',
+ 'HarbormasterCreateArtifactConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterCreateArtifactConduitAPIMethod.php',
'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php',
'HarbormasterExternalBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterExternalBuildStepGroup.php',
+ 'HarbormasterFileArtifact' => 'applications/harbormaster/artifact/HarbormasterFileArtifact.php',
'HarbormasterHTTPRequestBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterHTTPRequestBuildStepImplementation.php',
+ 'HarbormasterHostArtifact' => 'applications/harbormaster/artifact/HarbormasterHostArtifact.php',
'HarbormasterLeaseHostBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php',
'HarbormasterLintMessagesController' => 'applications/harbormaster/controller/HarbormasterLintMessagesController.php',
'HarbormasterLintPropertyView' => 'applications/harbormaster/view/HarbormasterLintPropertyView.php',
@@ -1018,6 +1023,7 @@
'HarbormasterTestBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterTestBuildStepGroup.php',
'HarbormasterThrowExceptionBuildStep' => 'applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php',
'HarbormasterUIEventListener' => 'applications/harbormaster/event/HarbormasterUIEventListener.php',
+ 'HarbormasterURIArtifact' => 'applications/harbormaster/artifact/HarbormasterURIArtifact.php',
'HarbormasterUnitMessagesController' => 'applications/harbormaster/controller/HarbormasterUnitMessagesController.php',
'HarbormasterUnitPropertyView' => 'applications/harbormaster/view/HarbormasterUnitPropertyView.php',
'HarbormasterUploadArtifactBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php',
@@ -4603,6 +4609,7 @@
'FundSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'HarbormasterArcLintBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterArcUnitBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
+ 'HarbormasterArtifact' => 'Phobject',
'HarbormasterAutotargetsTestCase' => 'PhabricatorTestCase',
'HarbormasterBuild' => array(
'HarbormasterDAO',
@@ -4616,6 +4623,7 @@
'HarbormasterDAO',
'PhabricatorPolicyInterface',
),
+ 'HarbormasterBuildArtifactPHIDType' => 'PhabricatorPHIDType',
'HarbormasterBuildArtifactQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'HarbormasterBuildAutoplan' => 'Phobject',
'HarbormasterBuildCommand' => 'HarbormasterDAO',
@@ -4700,9 +4708,12 @@
'HarbormasterCommandBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterConduitAPIMethod' => 'ConduitAPIMethod',
'HarbormasterController' => 'PhabricatorController',
+ 'HarbormasterCreateArtifactConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
'HarbormasterDAO' => 'PhabricatorLiskDAO',
'HarbormasterExternalBuildStepGroup' => 'HarbormasterBuildStepGroup',
+ 'HarbormasterFileArtifact' => 'HarbormasterArtifact',
'HarbormasterHTTPRequestBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
+ 'HarbormasterHostArtifact' => 'HarbormasterArtifact',
'HarbormasterLeaseHostBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterLintMessagesController' => 'HarbormasterController',
'HarbormasterLintPropertyView' => 'AphrontView',
@@ -4738,6 +4749,7 @@
'HarbormasterTestBuildStepGroup' => 'HarbormasterBuildStepGroup',
'HarbormasterThrowExceptionBuildStep' => 'HarbormasterBuildStepImplementation',
'HarbormasterUIEventListener' => 'PhabricatorEventListener',
+ 'HarbormasterURIArtifact' => 'HarbormasterArtifact',
'HarbormasterUnitMessagesController' => 'HarbormasterController',
'HarbormasterUnitPropertyView' => 'AphrontView',
'HarbormasterUploadArtifactBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
diff --git a/src/applications/drydock/query/DrydockLeaseQuery.php b/src/applications/drydock/query/DrydockLeaseQuery.php
--- a/src/applications/drydock/query/DrydockLeaseQuery.php
+++ b/src/applications/drydock/query/DrydockLeaseQuery.php
@@ -27,19 +27,12 @@
return $this;
}
+ public function newResultObject() {
+ return new DrydockLease();
+ }
+
protected function loadPage() {
- $table = new DrydockLease();
- $conn_r = $table->establishConnection('r');
-
- $data = queryfx_all(
- $conn_r,
- 'SELECT lease.* FROM %T lease %Q %Q %Q',
- $table->getTableName(),
- $this->buildWhereClause($conn_r),
- $this->buildOrderClause($conn_r),
- $this->buildLimitClause($conn_r));
-
- return $table->loadAllFromArray($data);
+ return $this->loadStandardPage($this->newResultObject());
}
protected function willFilterPage(array $leases) {
@@ -69,40 +62,38 @@
return $leases;
}
- protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
- $where = array();
+ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
+ $where = parent::buildWhereClauseParts($conn);
- if ($this->resourceIDs) {
+ if ($this->resourceIDs !== null) {
$where[] = qsprintf(
- $conn_r,
+ $conn,
'resourceID IN (%Ld)',
$this->resourceIDs);
}
- if ($this->ids) {
+ if ($this->ids !== null) {
$where[] = qsprintf(
- $conn_r,
+ $conn,
'id IN (%Ld)',
$this->ids);
}
- if ($this->phids) {
+ if ($this->phids !== null) {
$where[] = qsprintf(
- $conn_r,
+ $conn,
'phid IN (%Ls)',
$this->phids);
}
- if ($this->statuses) {
+ if ($this->statuses !== null) {
$where[] = qsprintf(
- $conn_r,
+ $conn,
'status IN (%Ld)',
$this->statuses);
}
- $where[] = $this->buildPagingClause($conn_r);
-
- return $this->formatWhereClause($where);
+ return $where;
}
}
diff --git a/src/applications/harbormaster/artifact/HarbormasterArtifact.php b/src/applications/harbormaster/artifact/HarbormasterArtifact.php
new file mode 100644
--- /dev/null
+++ b/src/applications/harbormaster/artifact/HarbormasterArtifact.php
@@ -0,0 +1,88 @@
+<?php
+
+
+abstract class HarbormasterArtifact extends Phobject {
+
+ private $buildArtifact;
+
+ abstract public function getArtifactTypeName();
+
+ public function getArtifactTypeSummary() {
+ return $this->getArtifactTypeDescription();
+ }
+
+ abstract public function getArtifactTypeDescription();
+ abstract public function getArtifactParameterSpecification();
+ abstract public function getArtifactParameterDescriptions();
+ abstract public function willCreateArtifact(PhabricatorUser $actor);
+
+ public function validateArtifactData(array $artifact_data) {
+ $artifact_spec = $this->getArtifactParameterSpecification();
+ PhutilTypeSpec::checkMap($artifact_data, $artifact_spec);
+ }
+
+ public function renderArtifactSummary(PhabricatorUser $viewer) {
+ return null;
+ }
+
+ public function releaseArtifact(PhabricatorUser $actor) {
+ return;
+ }
+
+ public function getArtifactDataExample() {
+ return null;
+ }
+
+ public function setBuildArtifact(HarbormasterBuildArtifact $build_artifact) {
+ $this->buildArtifact = $build_artifact;
+ return $this;
+ }
+
+ public function getBuildArtifact() {
+ return $this->buildArtifact;
+ }
+
+ final public function getArtifactConstant() {
+ $class = new ReflectionClass($this);
+
+ $const = $class->getConstant('ARTIFACTCONST');
+ if ($const === false) {
+ throw new Exception(
+ pht(
+ '"%s" class "%s" must define a "%s" property.',
+ __CLASS__,
+ get_class($this),
+ 'ARTIFACTCONST'));
+ }
+
+ $limit = self::getArtifactConstantByteLimit();
+ if (!is_string($const) || (strlen($const) > $limit)) {
+ throw new Exception(
+ pht(
+ '"%s" class "%s" has an invalid "%s" property. Action constants '.
+ 'must be strings and no more than %s bytes in length.',
+ __CLASS__,
+ get_class($this),
+ 'ARTIFACTCONST',
+ new PhutilNumber($limit)));
+ }
+
+ return $const;
+ }
+
+ final public static function getArtifactConstantByteLimit() {
+ return 32;
+ }
+
+ final public static function getAllArtifactTypes() {
+ return id(new PhutilClassMapQuery())
+ ->setAncestorClass(__CLASS__)
+ ->setUniqueMethod('getArtifactConstant')
+ ->execute();
+ }
+
+ final public static function getArtifactType($type) {
+ return idx(self::getAllArtifactTypes(), $type);
+ }
+
+}
diff --git a/src/applications/harbormaster/artifact/HarbormasterFileArtifact.php b/src/applications/harbormaster/artifact/HarbormasterFileArtifact.php
new file mode 100644
--- /dev/null
+++ b/src/applications/harbormaster/artifact/HarbormasterFileArtifact.php
@@ -0,0 +1,66 @@
+<?php
+
+final class HarbormasterFileArtifact extends HarbormasterArtifact {
+
+ const ARTIFACTCONST = 'file';
+
+ public function getArtifactTypeName() {
+ return pht('File');
+ }
+
+ public function getArtifactTypeDescription() {
+ return pht(
+ 'Stores a reference to file data which has been uploaded to '.
+ 'Phabricator.');
+ }
+
+ public function getArtifactParameterSpecification() {
+ return array(
+ 'filePHID' => 'string',
+ );
+ }
+
+ public function getArtifactParameterDescriptions() {
+ return array(
+ 'filePHID' => pht('File to create an artifact from.'),
+ );
+ }
+
+ public function getArtifactDataExample() {
+ return array(
+ 'filePHID' => 'PHID-FILE-abcdefghijklmnopqrst',
+ );
+ }
+
+ public function renderArtifactSummary(PhabricatorUser $viewer) {
+ $artifact = $this->getBuildArtifact();
+ $file_phid = $artifact->getProperty('filePHID');
+ return $viewer->renderHandle($file_phid);
+ }
+
+ public function willCreateArtifact(PhabricatorUser $actor) {
+ // NOTE: This is primarily making sure the actor has permission to view the
+ // file. We don't want to let you run builds using files you don't have
+ // permission to see, since this could let you violate permissions.
+ $this->loadArtifactFile($actor);
+ }
+
+ public function loadArtifactFile(PhabricatorUser $viewer) {
+ $artifact = $this->getBuildArtifact();
+ $file_phid = $artifact->getProperty('filePHID');
+
+ $file = id(new PhabricatorFileQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($file_phid))
+ ->executeOne();
+ if (!$file) {
+ throw new Exception(
+ pht(
+ 'File PHID "%s" does not correspond to a valid file.',
+ $file_phid));
+ }
+
+ return $file;
+ }
+
+}
diff --git a/src/applications/harbormaster/artifact/HarbormasterHostArtifact.php b/src/applications/harbormaster/artifact/HarbormasterHostArtifact.php
new file mode 100644
--- /dev/null
+++ b/src/applications/harbormaster/artifact/HarbormasterHostArtifact.php
@@ -0,0 +1,74 @@
+<?php
+
+final class HarbormasterHostArtifact extends HarbormasterArtifact {
+
+ const ARTIFACTCONST = 'host';
+
+ public function getArtifactTypeName() {
+ return pht('Drydock Host');
+ }
+
+ public function getArtifactTypeDescription() {
+ return pht('References a host lease from Drydock.');
+ }
+
+
+ public function getArtifactParameterSpecification() {
+ return array(
+ 'drydockLeasePHID' => 'string',
+ );
+ }
+
+ public function getArtifactParameterDescriptions() {
+ return array(
+ 'drydockLeasePHID' => pht(
+ 'Drydock host lease to create an artifact from.'),
+ );
+ }
+
+ public function getArtifactDataExample() {
+ return array(
+ 'drydockLeasePHID' => 'PHID-DRYL-abcdefghijklmnopqrst',
+ );
+ }
+
+ public function renderArtifactSummary(PhabricatorUser $viewer) {
+ $artifact = $this->getBuildArtifact();
+ $file_phid = $artifact->getProperty('drydockLeasePHID');
+ return $viewer->renderHandle($file_phid);
+ }
+
+ public function willCreateArtifact(PhabricatorUser $actor) {
+ $this->loadArtifactLease($actor);
+ }
+
+ public function loadArtifactLease(PhabricatorUser $viewer) {
+ $artifact = $this->getBuildArtifact();
+ $lease_phid = $artifact->getProperty('drydockLeasePHID');
+
+ $lease = id(new DrydockLeaseQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($lease_phid))
+ ->executeOne();
+ if (!$lease) {
+ throw new Exception(
+ pht(
+ 'Drydock lease PHID "%s" does not correspond to a valid lease.',
+ $lease_phid));
+ }
+
+ return $lease;
+ }
+
+ public function releaseArtifact(PhabricatorUser $actor) {
+ $lease = $this->loadArtifactLease($actor);
+ $resource = $lease->getResource();
+ $blueprint = $resource->getBlueprint();
+
+ if ($lease->isActive()) {
+ $blueprint->releaseLease($resource, $lease);
+ }
+ }
+
+
+}
diff --git a/src/applications/harbormaster/artifact/HarbormasterURIArtifact.php b/src/applications/harbormaster/artifact/HarbormasterURIArtifact.php
new file mode 100644
--- /dev/null
+++ b/src/applications/harbormaster/artifact/HarbormasterURIArtifact.php
@@ -0,0 +1,100 @@
+<?php
+
+final class HarbormasterURIArtifact extends HarbormasterArtifact {
+
+ const ARTIFACTCONST = 'uri';
+
+ public function getArtifactTypeName() {
+ return pht('URI');
+ }
+
+ public function getArtifactTypeSummary() {
+ return pht('Stores a URI.');
+ }
+
+ public function getArtifactTypeDescription() {
+ return pht(
+ "Stores a URI.\n\n".
+ "With `ui.external`, you can use this artifact type to add links to ".
+ "build results in an external build system.");
+ }
+
+ public function getArtifactParameterSpecification() {
+ return array(
+ 'uri' => 'string',
+ 'name' => 'optional string',
+ 'ui.external' => 'optional bool',
+ );
+ }
+
+ public function getArtifactParameterDescriptions() {
+ return array(
+ 'uri' => pht('The URI to store.'),
+ 'name' => pht('Optional label for this URI.'),
+ 'ui.external' => pht(
+ 'If true, display this URI in the UI as an link to '.
+ 'additional build details in an external build system.'),
+ );
+ }
+
+ public function getArtifactDataExample() {
+ return array(
+ 'uri' => 'https://buildserver.mycompany.com/build/123/',
+ 'name' => pht('View External Build Results'),
+ 'ui.external' => true,
+ );
+ }
+
+ public function renderArtifactSummary(PhabricatorUser $viewer) {
+ $artifact = $this->getBuildArtifact();
+ $uri = $artifact->getProperty('uri');
+
+ try {
+ $this->validateURI($uri);
+ } catch (Exception $ex) {
+ return pht('<Invalid URI>');
+ }
+
+ $name = $artifact->getProperty('name', $uri);
+
+ return phutil_tag(
+ 'a',
+ array(
+ 'href' => $uri,
+ 'target' => '_blank',
+ ),
+ $name);
+ }
+
+ public function willCreateArtifact(PhabricatorUser $actor) {
+ $artifact = $this->getBuildArtifact();
+ $uri = $artifact->getProperty('uri');
+ $this->validateURI($uri);
+ }
+
+ private function validateURI($raw_uri) {
+ $uri = new PhutilURI($raw_uri);
+
+ $protocol = $uri->getProtocol();
+ if (!strlen($protocol)) {
+ throw new Exception(
+ pht(
+ 'Unable to identify the protocol for URI "%s". URIs must be '.
+ 'fully qualified and have an identifiable protocol.',
+ $raw_uri));
+ }
+
+ $protocol_key = 'uri.allowed-protocols';
+ $protocols = PhabricatorEnv::getEnvConfig($protocol_key);
+ if (empty($protocols[$protocol])) {
+ throw new Exception(
+ pht(
+ 'URI "%s" does not have an allowable protocol. Configure '.
+ 'protocols in `%s`. Allowed protocols are: %s.',
+ $raw_uri,
+ $protocol_key,
+ implode(', ', array_keys($protocols))));
+ }
+ }
+
+}
diff --git a/src/applications/harbormaster/conduit/HarbormasterConduitAPIMethod.php b/src/applications/harbormaster/conduit/HarbormasterConduitAPIMethod.php
--- a/src/applications/harbormaster/conduit/HarbormasterConduitAPIMethod.php
+++ b/src/applications/harbormaster/conduit/HarbormasterConduitAPIMethod.php
@@ -15,4 +15,16 @@
return pht('All Harbormaster APIs are new and subject to change.');
}
+ protected function returnArtifactList(array $artifacts) {
+ $list = array();
+
+ foreach ($artifacts as $artifact) {
+ $list[] = array(
+ 'phid' => $artifact->getPHID(),
+ );
+ }
+
+ return $list;
+ }
+
}
diff --git a/src/applications/harbormaster/conduit/HarbormasterCreateArtifactConduitAPIMethod.php b/src/applications/harbormaster/conduit/HarbormasterCreateArtifactConduitAPIMethod.php
new file mode 100644
--- /dev/null
+++ b/src/applications/harbormaster/conduit/HarbormasterCreateArtifactConduitAPIMethod.php
@@ -0,0 +1,129 @@
+<?php
+
+final class HarbormasterCreateArtifactConduitAPIMethod
+ extends HarbormasterConduitAPIMethod {
+
+ public function getAPIMethodName() {
+ return 'harbormaster.createartifact';
+ }
+
+ public function getMethodSummary() {
+ return pht('Create a build artifact.');
+ }
+
+ public function getMethodDescription() {
+ $types = HarbormasterArtifact::getAllArtifactTypes();
+ $types = msort($types, 'getArtifactTypeName');
+
+ $head_key = pht('Key');
+ $head_type = pht('Type');
+ $head_desc = pht('Description');
+ $head_atype = pht('Artifact Type');
+ $head_name = pht('Name');
+ $head_summary = pht('Summary');
+
+ $out = array();
+ $out[] = pht(
+ 'Use this method to attach artifacts to build targets while running '.
+ 'builds. Artifacts can be used to carry data through a complex build '.
+ 'workflow, provide extra information to users, or store build results.');
+ $out[] = null;
+ $out[] = pht(
+ 'When creating an artifact, you will choose an `artifactType` from '.
+ 'this table. These types of artifacts are supported:');
+
+ $out[] = "| {$head_atype} | {$head_name} | {$head_summary} |";
+ $out[] = '|-------------|--------------|--------------|';
+ foreach ($types as $type) {
+ $type_name = $type->getArtifactTypeName();
+ $type_const = $type->getArtifactConstant();
+ $type_summary = $type->getArtifactTypeSummary();
+ $out[] = "| `{$type_const}` | **{$type_name}** | {$type_summary} |";
+ }
+
+ $out[] = null;
+ $out[] = pht(
+ 'Each artifact also needs an `artifactKey`, which names the artifact. '.
+ 'Finally, you will provide some `artifactData` to fill in the content '.
+ 'of the artifact. The data you provide depends on what type of artifact '.
+ 'you are creating.');
+
+ foreach ($types as $type) {
+ $type_name = $type->getArtifactTypeName();
+ $type_const = $type->getArtifactConstant();
+
+ $out[] = $type_name;
+ $out[] = '--------------------------';
+ $out[] = null;
+ $out[] = $type->getArtifactTypeDescription();
+ $out[] = null;
+ $out[] = pht(
+ 'Create an artifact of this type by passing `%s` as the '.
+ '`artifactType`. When creating an artifact of this type, provide '.
+ 'these parameters as a dictionary to `artifactData`:',
+ $type_const);
+
+ $spec = $type->getArtifactParameterSpecification();
+ $desc = $type->getArtifactParameterDescriptions();
+ $out[] = "| {$head_key} | {$head_type} | {$head_desc} |";
+ $out[] = '|-------------|--------------|--------------|';
+ foreach ($spec as $key => $key_type) {
+ $key_desc = idx($desc, $key);
+ $out[] = "| `{$key}` | //{$key_type}// | {$key_desc} |";
+ }
+
+ $example = $type->getArtifactDataExample();
+ if ($example !== null) {
+ $json = new PhutilJSON();
+ $rendered = $json->encodeFormatted($example);
+
+ $out[] = pht('For example:');
+ $out[] = '```lang=json';
+ $out[] = $rendered;
+ $out[] = '```';
+ }
+ }
+
+ return implode("\n", $out);
+ }
+
+ protected function defineParamTypes() {
+ return array(
+ 'buildTargetPHID' => 'phid',
+ 'artifactKey' => 'string',
+ 'artifactType' => 'string',
+ 'artifactData' => 'map<string, wild>',
+ );
+ }
+
+ protected function defineReturnType() {
+ return 'wild';
+ }
+
+ protected function execute(ConduitAPIRequest $request) {
+ $viewer = $request->getUser();
+
+ $build_target_phid = $request->getValue('buildTargetPHID');
+ $build_target = id(new HarbormasterBuildTargetQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($build_target_phid))
+ ->executeOne();
+ if (!$build_target) {
+ throw new Exception(
+ pht(
+ 'No such build target "%s"!',
+ $build_target_phid));
+ }
+
+ $artifact = $build_target->createArtifact(
+ $viewer,
+ $request->getValue('artifactKey'),
+ $request->getValue('artifactType'),
+ $request->getValue('artifactData'));
+
+ return array(
+ 'data' => $this->returnArtifactList(array($artifact)),
+ );
+ }
+
+}
diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php
--- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php
+++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php
@@ -3,17 +3,11 @@
final class HarbormasterBuildViewController
extends HarbormasterController {
- private $id;
-
- public function willProcessRequest(array $data) {
- $this->id = $data['id'];
- }
-
- public function processRequest() {
+ public function handleRequest(AphrontRequest $request) {
$request = $this->getRequest();
$viewer = $request->getUser();
- $id = $this->id;
+ $id = $request->getURIData('id');
$generation = $request->getInt('g');
$build = id(new HarbormasterBuildQuery())
@@ -224,29 +218,51 @@
));
}
- private function buildArtifacts(
- HarbormasterBuildTarget $build_target) {
-
- $request = $this->getRequest();
- $viewer = $request->getUser();
+ private function buildArtifacts(HarbormasterBuildTarget $build_target) {
+ $viewer = $this->getViewer();
$artifacts = id(new HarbormasterBuildArtifactQuery())
->setViewer($viewer)
->withBuildTargetPHIDs(array($build_target->getPHID()))
->execute();
- $list = id(new PHUIObjectItemListView())
- ->setNoDataString(pht('This target has no associated artifacts.'))
- ->setFlush(true);
+ $artifacts = msort($artifacts, 'getArtifactKey');
+ $rows = array();
foreach ($artifacts as $artifact) {
- $item = $artifact->getObjectItemView($viewer);
- if ($item !== null) {
- $list->addItem($item);
+ $impl = $artifact->getArtifactImplementation();
+
+ if ($impl) {
+ $summary = $impl->renderArtifactSummary($viewer);
+ $type_name = $impl->getArtifactTypeName();
+ } else {
+ $summary = pht('<Unknown Artifact Type>');
+ $type_name = $artifact->getType();
}
+
+ $rows[] = array(
+ $artifact->getArtifactKey(),
+ $type_name,
+ $summary,
+ );
}
- return $list;
+ $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;
}
private function buildLog(
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
@@ -483,7 +483,7 @@
->execute();
foreach ($artifacts as $artifact) {
- $artifact->release();
+ $artifact->releaseArtifact();
}
}
diff --git a/src/applications/harbormaster/phid/HarbormasterBuildArtifactPHIDType.php b/src/applications/harbormaster/phid/HarbormasterBuildArtifactPHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/harbormaster/phid/HarbormasterBuildArtifactPHIDType.php
@@ -0,0 +1,35 @@
+<?php
+
+final class HarbormasterBuildArtifactPHIDType extends PhabricatorPHIDType {
+
+ const TYPECONST = 'HMBA';
+
+ public function getTypeName() {
+ return pht('Build Artifact');
+ }
+
+ public function newObject() {
+ return new HarbormasterBuildArtifact();
+ }
+
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ return id(new HarbormasterBuildArtifactQuery())
+ ->withPHIDs($phids);
+ }
+
+ public function loadHandles(
+ PhabricatorHandleQuery $query,
+ array $handles,
+ array $objects) {
+
+ foreach ($handles as $phid => $handle) {
+ $artifact = $objects[$phid];
+ $artifact_id = $artifact->getID();
+ $handle->setName(pht('Build Artifact %d', $artifact_id));
+ }
+ }
+
+}
diff --git a/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php b/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php
--- a/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php
+++ b/src/applications/harbormaster/query/HarbormasterBuildArtifactQuery.php
@@ -6,7 +6,7 @@
private $ids;
private $buildTargetPHIDs;
private $artifactTypes;
- private $artifactKeys;
+ private $artifactIndexes;
private $keyBuildPHID;
private $keyBuildGeneration;
@@ -25,29 +25,17 @@
return $this;
}
- public function withArtifactKeys(
- $build_phid,
- $build_gen,
- array $artifact_keys) {
- $this->keyBuildPHID = $build_phid;
- $this->keyBuildGeneration = $build_gen;
- $this->artifactKeys = $artifact_keys;
+ public function withArtifactIndexes(array $artifact_indexes) {
+ $this->artifactIndexes = $artifact_indexes;
return $this;
}
+ public function newResultObject() {
+ return new HarbormasterBuildArtifact();
+ }
+
protected function loadPage() {
- $table = new HarbormasterBuildArtifact();
- $conn_r = $table->establishConnection('r');
-
- $data = queryfx_all(
- $conn_r,
- 'SELECT * FROM %T %Q %Q %Q',
- $table->getTableName(),
- $this->buildWhereClause($conn_r),
- $this->buildOrderClause($conn_r),
- $this->buildLimitClause($conn_r));
-
- return $table->loadAllFromArray($data);
+ return $this->loadStandardPage($this->newResultObject());
}
protected function willFilterPage(array $page) {
@@ -75,46 +63,38 @@
return $page;
}
- protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
- $where = array();
+ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
+ $where = parent::buildWhereClauseParts($conn);
- if ($this->ids) {
+ if ($this->ids !== null) {
$where[] = qsprintf(
- $conn_r,
+ $conn,
'id IN (%Ld)',
$this->ids);
}
- if ($this->buildTargetPHIDs) {
+ if ($this->buildTargetPHIDs !== null) {
$where[] = qsprintf(
- $conn_r,
+ $conn,
'buildTargetPHID IN (%Ls)',
$this->buildTargetPHIDs);
}
- if ($this->artifactTypes) {
+ if ($this->artifactTypes !== null) {
$where[] = qsprintf(
- $conn_r,
+ $conn,
'artifactType in (%Ls)',
$this->artifactTypes);
}
- if ($this->artifactKeys) {
- $indexes = array();
- foreach ($this->artifactKeys as $key) {
- $indexes[] = PhabricatorHash::digestForIndex(
- $this->keyBuildPHID.$this->keyBuildGeneration.$key);
- }
-
+ if ($this->artifactIndexes !== null) {
$where[] = qsprintf(
- $conn_r,
+ $conn,
'artifactIndex IN (%Ls)',
- $indexes);
+ $this->artifactIndexes);
}
- $where[] = $this->buildPagingClause($conn_r);
-
- return $this->formatWhereClause($where);
+ return $where;
}
public function getQueryApplicationClass() {
diff --git a/src/applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php
--- a/src/applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterCommandBuildStepImplementation.php
@@ -43,9 +43,9 @@
$settings = $this->getSettings();
$variables = $build_target->getVariables();
- $artifact = $build->loadArtifact($settings['hostartifact']);
-
- $lease = $artifact->loadDrydockLease();
+ $artifact = $build_target->loadArtifact($settings['hostartifact']);
+ $impl = $artifact->getArtifactImplementation();
+ $lease = $impl->loadArtifactLease();
$this->platform = $lease->getAttribute('platform');
@@ -120,9 +120,9 @@
public function getArtifactInputs() {
return array(
array(
- 'name' => pht('Run on Host'),
- 'key' => $this->getSetting('hostartifact'),
- 'type' => HarbormasterBuildArtifact::TYPE_HOST,
+ 'name' => pht('Run on Host'),
+ 'key' => $this->getSetting('hostartifact'),
+ 'type' => HarbormasterHostArtifact::ARTIFACTCONST,
),
);
}
diff --git a/src/applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php
--- a/src/applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterLeaseHostBuildStepImplementation.php
@@ -36,14 +36,13 @@
$lease->waitUntilActive();
// Create the associated artifact.
- $artifact = $build->createArtifact(
- $build_target,
+ $artifact = $build_target->createArtifact(
+ PhabricatorUser::getOmnipotentUser(),
$settings['name'],
- HarbormasterBuildArtifact::TYPE_HOST);
- $artifact->setArtifactData(array(
- 'drydock-lease' => $lease->getID(),
- ));
- $artifact->save();
+ HarbormasterHostArtifact::ARTIFACTCONST,
+ array(
+ 'drydockLeasePHID' => $lease->getPHID(),
+ ));
}
public function getArtifactOutputs() {
@@ -51,7 +50,7 @@
array(
'name' => pht('Leased Host'),
'key' => $this->getSetting('name'),
- 'type' => HarbormasterBuildArtifact::TYPE_HOST,
+ 'type' => HarbormasterHostArtifact::ARTIFACTCONST,
),
);
}
diff --git a/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php
--- a/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php
@@ -29,33 +29,34 @@
$settings = $this->getSettings();
$variables = $build_target->getVariables();
+ $viewer = PhabricatorUser::getOmnipotentUser();
$path = $this->mergeVariables(
'vsprintf',
$settings['path'],
$variables);
- $artifact = $build->loadArtifact($settings['artifact']);
-
- $file = $artifact->loadPhabricatorFile();
+ $artifact = $build_target->loadArtifact($settings['artifact']);
+ $impl = $artifact->getArtifactImplementation();
+ $file = $impl->loadArtifactFile($viewer);
$fragment = id(new PhragmentFragmentQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->setViewer($viewer)
->withPaths(array($path))
->executeOne();
if ($fragment === null) {
PhragmentFragment::createFromFile(
- PhabricatorUser::getOmnipotentUser(),
+ $viewer,
$file,
$path,
PhabricatorPolicies::getMostOpenPolicy(),
PhabricatorPolicies::POLICY_USER);
} else {
if ($file->getMimeType() === 'application/zip') {
- $fragment->updateFromZIP(PhabricatorUser::getOmnipotentUser(), $file);
+ $fragment->updateFromZIP($viewer, $file);
} else {
- $fragment->updateFromFile(PhabricatorUser::getOmnipotentUser(), $file);
+ $fragment->updateFromFile($viewer, $file);
}
}
}
@@ -65,7 +66,7 @@
array(
'name' => pht('Publishes File'),
'key' => $this->getSetting('artifact'),
- 'type' => HarbormasterBuildArtifact::TYPE_FILE,
+ 'type' => HarbormasterFileArtifact::ARTIFACTCONST,
),
);
}
diff --git a/src/applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php
--- a/src/applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php
+++ b/src/applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php
@@ -34,8 +34,7 @@
$settings['path'],
$variables);
- $artifact = $build->loadArtifact($settings['hostartifact']);
-
+ $artifact = $build_target->loadArtifact($settings['hostartifact']);
$lease = $artifact->loadDrydockLease();
$interface = $lease->getInterface('filesystem');
@@ -44,14 +43,13 @@
$file = $interface->saveFile($path, $settings['name']);
// Insert the artifact record.
- $artifact = $build->createArtifact(
- $build_target,
+ $artifact = $build_target->createArtifact(
+ PhabricatorUser::getOmnipotentUser(),
$settings['name'],
- HarbormasterBuildArtifact::TYPE_FILE);
- $artifact->setArtifactData(array(
- 'filePHID' => $file->getPHID(),
- ));
- $artifact->save();
+ HarbormasterFileArtifact::ARTIFACTCONST,
+ array(
+ 'filePHID' => $file->getPHID(),
+ ));
}
public function getArtifactInputs() {
@@ -59,7 +57,7 @@
array(
'name' => pht('Upload From Host'),
'key' => $this->getSetting('hostartifact'),
- 'type' => HarbormasterBuildArtifact::TYPE_HOST,
+ 'type' => HarbormasterHostArtifact::ARTIFACTCONST,
),
);
}
@@ -69,7 +67,7 @@
array(
'name' => pht('Uploaded File'),
'key' => $this->getSetting('name'),
- 'type' => HarbormasterBuildArtifact::TYPE_FILE,
+ 'type' => HarbormasterHostArtifact::ARTIFACTCONST,
),
);
}
diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuild.php b/src/applications/harbormaster/storage/build/HarbormasterBuild.php
--- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php
@@ -235,36 +235,6 @@
return $log;
}
- public function createArtifact(
- HarbormasterBuildTarget $build_target,
- $artifact_key,
- $artifact_type) {
-
- $artifact =
- HarbormasterBuildArtifact::initializeNewBuildArtifact($build_target);
- $artifact->setArtifactKey(
- $this->getPHID(),
- $this->getBuildGeneration(),
- $artifact_key);
- $artifact->setArtifactType($artifact_type);
- $artifact->save();
- return $artifact;
- }
-
- public function loadArtifact($name) {
- $artifact = id(new HarbormasterBuildArtifactQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withArtifactKeys(
- $this->getPHID(),
- $this->getBuildGeneration(),
- array($name))
- ->executeOne();
- if ($artifact === null) {
- throw new Exception(pht('Artifact not found!'));
- }
- return $artifact;
- }
-
public function retrieveVariablesFromBuild() {
$results = array(
'buildable.diff' => null,
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
@@ -10,19 +10,18 @@
protected $artifactData = array();
private $buildTarget = self::ATTACHABLE;
-
- const TYPE_FILE = 'file';
- const TYPE_HOST = 'host';
- const TYPE_URI = 'uri';
+ private $artifactImplementation;
public static function initializeNewBuildArtifact(
HarbormasterBuildTarget $build_target) {
return id(new HarbormasterBuildArtifact())
+ ->attachBuildTarget($build_target)
->setBuildTargetPHID($build_target->getPHID());
}
protected function getConfiguration() {
return array(
+ self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'artifactData' => self::SERIALIZATION_JSON,
),
@@ -43,6 +42,11 @@
) + parent::getConfiguration();
}
+ public function generatePHID() {
+ return PhabricatorPHID::generateNewPHID(
+ HarbormasterBuildArtifactPHIDType::TYPECONST);
+ }
+
public function attachBuildTarget(HarbormasterBuildTarget $build_target) {
$this->buildTarget = $build_target;
return $this;
@@ -52,113 +56,58 @@
return $this->assertAttached($this->buildTarget);
}
- public function setArtifactKey($build_phid, $build_gen, $key) {
- $this->artifactIndex =
- PhabricatorHash::digestForIndex($build_phid.$build_gen.$key);
+ public function setArtifactKey($key) {
+ $target = $this->getBuildTarget();
+ $this->artifactIndex = self::getArtifactIndex($target, $key);
$this->artifactKey = $key;
return $this;
}
- public function getObjectItemView(PhabricatorUser $viewer) {
- $data = $this->getArtifactData();
- switch ($this->getArtifactType()) {
- case self::TYPE_FILE:
- $handle = id(new PhabricatorHandleQuery())
- ->setViewer($viewer)
- ->withPHIDs($data)
- ->executeOne();
-
- return id(new PHUIObjectItemView())
- ->setObjectName(pht('File'))
- ->setHeader($handle->getFullName())
- ->setHref($handle->getURI());
- case self::TYPE_HOST:
- $leases = id(new DrydockLeaseQuery())
- ->setViewer($viewer)
- ->withIDs(array($data['drydock-lease']))
- ->execute();
- $lease = idx($leases, $data['drydock-lease']);
-
- return id(new PHUIObjectItemView())
- ->setObjectName(pht('Drydock Lease'))
- ->setHeader($lease->getID())
- ->setHref('/drydock/lease/'.$lease->getID());
- case self::TYPE_URI:
- return id(new PHUIObjectItemView())
- ->setObjectName($data['name'])
- ->setHeader($data['uri'])
- ->setHref($data['uri']);
- default:
- return null;
- }
- }
-
- public function loadDrydockLease() {
- if ($this->getArtifactType() !== self::TYPE_HOST) {
- throw new Exception(
- pht(
- '`%s` may only be called on host artifacts.',
- __FUNCTION__));
- }
+ public static function getArtifactIndex(
+ HarbormasterBuildTarget $target,
+ $artifact_key) {
- $data = $this->getArtifactData();
+ $build = $target->getBuild();
- // FIXME: Is there a better way of doing this?
- // TODO: Policy stuff, etc.
- $lease = id(new DrydockLease())->load(
- $data['drydock-lease']);
- if ($lease === null) {
- throw new Exception(pht('Associated Drydock lease not found!'));
- }
- $resource = id(new DrydockResource())->load(
- $lease->getResourceID());
- if ($resource === null) {
- throw new Exception(pht('Associated Drydock resource not found!'));
- }
- $lease->attachResource($resource);
+ $parts = array(
+ $build->getPHID(),
+ $target->getBuildGeneration(),
+ $artifact_key,
+ );
+ $parts = implode("\0", $parts);
- return $lease;
+ return PhabricatorHash::digestForIndex($parts);
}
- public function loadPhabricatorFile() {
- if ($this->getArtifactType() !== self::TYPE_FILE) {
- throw new Exception(
- pht(
- '`%s` may only be called on file artifacts.',
- __FUNCTION__));
+ public function releaseArtifact() {
+ $impl = $this->getArtifactImplementation();
+
+ if ($impl) {
+ $impl->releaseArtifact(PhabricatorUser::getOmnipotentUser());
}
- $data = $this->getArtifactData();
+ return null;
+ }
- // The data for TYPE_FILE is an array with a single PHID in it.
- $phid = $data['filePHID'];
+ public function getArtifactImplementation() {
+ if ($this->artifactImplementation === null) {
+ $type = $this->getArtifactType();
+ $impl = HarbormasterArtifact::getArtifactType($type);
+ if (!$impl) {
+ return null;
+ }
- $file = id(new PhabricatorFileQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withPHIDs(array($phid))
- ->executeOne();
- if ($file === null) {
- throw new Exception(pht('Associated file not found!'));
+ $impl = clone $impl;
+ $impl->setBuildArtifact($this);
+ $this->artifactImplementation = $impl;
}
- return $file;
- }
- public function release() {
- switch ($this->getArtifactType()) {
- case self::TYPE_HOST:
- $this->releaseDrydockLease();
- break;
- }
+ return $this->artifactImplementation;
}
- public function releaseDrydockLease() {
- $lease = $this->loadDrydockLease();
- $resource = $lease->getResource();
- $blueprint = $resource->getBlueprint();
- if ($lease->isActive()) {
- $blueprint->releaseLease($resource, $lease);
- }
+ public function getProperty($key, $default = null) {
+ return idx($this->artifactData, $key, $default);
}
diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
--- a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
@@ -201,6 +201,54 @@
);
}
+ public function createArtifact(
+ PhabricatorUser $actor,
+ $artifact_key,
+ $artifact_type,
+ array $artifact_data) {
+
+ $impl = HarbormasterArtifact::getArtifactType($artifact_type);
+ if (!$impl) {
+ throw new Exception(
+ pht(
+ 'There is no implementation available for artifacts of type "%s".',
+ $artifact_type));
+ }
+
+ $impl->validateArtifactData($artifact_data);
+
+ $artifact = HarbormasterBuildArtifact::initializeNewBuildArtifact($this)
+ ->setArtifactKey($artifact_key)
+ ->setArtifactType($artifact_type)
+ ->setArtifactData($artifact_data);
+
+ $impl = $artifact->getArtifactImplementation();
+ $impl->willCreateArtifact($actor);
+
+ return $artifact->save();
+ }
+
+ public function loadArtifact($artifact_key) {
+ $indexes = array();
+
+ $indexes[] = HarbormasterBuildArtifact::getArtifactIndex(
+ $this,
+ $artifact_key);
+
+ $artifact = id(new HarbormasterBuildArtifactQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withArtifactIndexes($indexes)
+ ->executeOne();
+ if ($artifact === null) {
+ throw new Exception(
+ pht(
+ 'Artifact "%s" not found!',
+ $artifact_key));
+ }
+
+ return $artifact;
+ }
+
/* -( Status )------------------------------------------------------------- */

File Metadata

Mime Type
text/plain
Expires
Tue, Mar 4, 4:24 PM (18 h, 56 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7222182
Default Alt Text
D13900.id33564.diff (45 KB)

Event Timeline