1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-20 01:08:50 +02:00

Merge branch 'master' into redesign-2015

This commit is contained in:
epriestley 2015-06-22 12:26:41 -07:00
commit b55f9b6120
73 changed files with 1893 additions and 491 deletions

View file

@ -0,0 +1,14 @@
CREATE TABLE {$NAMESPACE}_harbormaster.harbormaster_buildlintmessage (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
buildTargetPHID VARBINARY(64) NOT NULL,
path LONGTEXT NOT NULL,
line INT UNSIGNED,
characterOffset INT UNSIGNED,
code VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
severity VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
name VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL,
properties LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
KEY `key_target` (buildTargetPHID)
) ENGINE=INNODB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,13 @@
CREATE TABLE {$NAMESPACE}_harbormaster.harbormaster_buildunitmessage (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
buildTargetPHID VARBINARY(64) NOT NULL,
engine VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL,
namespace VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL,
name VARCHAR(255) COLLATE {$COLLATE_TEXT} NOT NULL,
result VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
duration DOUBLE,
properties LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
KEY `key_target` (buildTargetPHID)
) ENGINE=INNODB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildplan
ADD planAutoKey VARCHAR(32) COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildstep
ADD stepAutoKey VARCHAR(32) COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_build
ADD planAutoKey VARCHAR(32) COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_passphrase.passphrase_credential
ADD authorPHID VARBINARY(64) NOT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_passphrase.passphrase_credential
ADD spacePHID VARBINARY(64);

View file

@ -360,6 +360,7 @@ phutil_register_library_map(array(
'DifferentialDiffTableOfContentsView' => 'applications/differential/view/DifferentialDiffTableOfContentsView.php',
'DifferentialDiffTestCase' => 'applications/differential/storage/__tests__/DifferentialDiffTestCase.php',
'DifferentialDiffTransaction' => 'applications/differential/storage/DifferentialDiffTransaction.php',
'DifferentialDiffTransactionQuery' => 'applications/differential/query/DifferentialDiffTransactionQuery.php',
'DifferentialDiffViewController' => 'applications/differential/controller/DifferentialDiffViewController.php',
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php',
'DifferentialDraft' => 'applications/differential/storage/DifferentialDraft.php',
@ -826,19 +827,22 @@ phutil_register_library_map(array(
'FundInitiativeTransactionQuery' => 'applications/fund/query/FundInitiativeTransactionQuery.php',
'FundInitiativeViewController' => 'applications/fund/controller/FundInitiativeViewController.php',
'FundSchemaSpec' => 'applications/fund/storage/FundSchemaSpec.php',
'HarbormasterArcLintBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcLintBuildStepImplementation.php',
'HarbormasterArcUnitBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterArcUnitBuildStepImplementation.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',
'HarbormasterBuildArtifactQuery' => 'applications/harbormaster/query/HarbormasterBuildArtifactQuery.php',
'HarbormasterBuildAutoplan' => 'applications/harbormaster/autoplan/HarbormasterBuildAutoplan.php',
'HarbormasterBuildCommand' => 'applications/harbormaster/storage/HarbormasterBuildCommand.php',
'HarbormasterBuildDependencyDatasource' => 'applications/harbormaster/typeahead/HarbormasterBuildDependencyDatasource.php',
'HarbormasterBuildEngine' => 'applications/harbormaster/engine/HarbormasterBuildEngine.php',
'HarbormasterBuildFailureException' => 'applications/harbormaster/exception/HarbormasterBuildFailureException.php',
'HarbormasterBuildGraph' => 'applications/harbormaster/engine/HarbormasterBuildGraph.php',
'HarbormasterBuildItem' => 'applications/harbormaster/storage/build/HarbormasterBuildItem.php',
'HarbormasterBuildItemPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildItemPHIDType.php',
'HarbormasterBuildItemQuery' => 'applications/harbormaster/query/HarbormasterBuildItemQuery.php',
'HarbormasterBuildLintMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildLintMessage.php',
'HarbormasterBuildLog' => 'applications/harbormaster/storage/build/HarbormasterBuildLog.php',
'HarbormasterBuildLogPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildLogPHIDType.php',
'HarbormasterBuildLogQuery' => 'applications/harbormaster/query/HarbormasterBuildLogQuery.php',
@ -870,6 +874,7 @@ phutil_register_library_map(array(
'HarbormasterBuildTransaction' => 'applications/harbormaster/storage/HarbormasterBuildTransaction.php',
'HarbormasterBuildTransactionEditor' => 'applications/harbormaster/editor/HarbormasterBuildTransactionEditor.php',
'HarbormasterBuildTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildTransactionQuery.php',
'HarbormasterBuildUnitMessage' => 'applications/harbormaster/storage/build/HarbormasterBuildUnitMessage.php',
'HarbormasterBuildViewController' => 'applications/harbormaster/controller/HarbormasterBuildViewController.php',
'HarbormasterBuildWorker' => 'applications/harbormaster/worker/HarbormasterBuildWorker.php',
'HarbormasterBuildable' => 'applications/harbormaster/storage/HarbormasterBuildable.php',
@ -901,6 +906,7 @@ phutil_register_library_map(array(
'HarbormasterPlanRunController' => 'applications/harbormaster/controller/HarbormasterPlanRunController.php',
'HarbormasterPlanViewController' => 'applications/harbormaster/controller/HarbormasterPlanViewController.php',
'HarbormasterPublishFragmentBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterPublishFragmentBuildStepImplementation.php',
'HarbormasterQueryAutotargetsConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryAutotargetsConduitAPIMethod.php',
'HarbormasterQueryBuildablesConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryBuildablesConduitAPIMethod.php',
'HarbormasterQueryBuildsConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterQueryBuildsConduitAPIMethod.php',
'HarbormasterRemarkupRule' => 'applications/harbormaster/remarkup/HarbormasterRemarkupRule.php',
@ -911,6 +917,7 @@ phutil_register_library_map(array(
'HarbormasterStepAddController' => 'applications/harbormaster/controller/HarbormasterStepAddController.php',
'HarbormasterStepDeleteController' => 'applications/harbormaster/controller/HarbormasterStepDeleteController.php',
'HarbormasterStepEditController' => 'applications/harbormaster/controller/HarbormasterStepEditController.php',
'HarbormasterTargetEngine' => 'applications/harbormaster/engine/HarbormasterTargetEngine.php',
'HarbormasterTargetWorker' => 'applications/harbormaster/worker/HarbormasterTargetWorker.php',
'HarbormasterThrowExceptionBuildStep' => 'applications/harbormaster/step/HarbormasterThrowExceptionBuildStep.php',
'HarbormasterUIEventListener' => 'applications/harbormaster/event/HarbormasterUIEventListener.php',
@ -1262,6 +1269,7 @@ phutil_register_library_map(array(
'PassphraseConduitAPIMethod' => 'applications/passphrase/conduit/PassphraseConduitAPIMethod.php',
'PassphraseController' => 'applications/passphrase/controller/PassphraseController.php',
'PassphraseCredential' => 'applications/passphrase/storage/PassphraseCredential.php',
'PassphraseCredentialAuthorPolicyRule' => 'applications/passphrase/policyrule/PassphraseCredentialAuthorPolicyRule.php',
'PassphraseCredentialConduitController' => 'applications/passphrase/controller/PassphraseCredentialConduitController.php',
'PassphraseCredentialControl' => 'applications/passphrase/view/PassphraseCredentialControl.php',
'PassphraseCredentialCreateController' => 'applications/passphrase/controller/PassphraseCredentialCreateController.php',
@ -1281,6 +1289,8 @@ phutil_register_library_map(array(
'PassphraseCredentialTypeTestCase' => 'applications/passphrase/credentialtype/__tests__/PassphraseCredentialTypeTestCase.php',
'PassphraseCredentialViewController' => 'applications/passphrase/controller/PassphraseCredentialViewController.php',
'PassphraseDAO' => 'applications/passphrase/storage/PassphraseDAO.php',
'PassphraseDefaultEditCapability' => 'applications/passphrase/capability/PassphraseDefaultEditCapability.php',
'PassphraseDefaultViewCapability' => 'applications/passphrase/capability/PassphraseDefaultViewCapability.php',
'PassphraseNoteCredentialType' => 'applications/passphrase/credentialtype/PassphraseNoteCredentialType.php',
'PassphrasePasswordCredentialType' => 'applications/passphrase/credentialtype/PassphrasePasswordCredentialType.php',
'PassphrasePasswordKey' => 'applications/passphrase/keys/PassphrasePasswordKey.php',
@ -3710,6 +3720,7 @@ phutil_register_library_map(array(
'DifferentialDiffTableOfContentsView' => 'AphrontView',
'DifferentialDiffTestCase' => 'PhutilTestCase',
'DifferentialDiffTransaction' => 'PhabricatorApplicationTransaction',
'DifferentialDiffTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'DifferentialDiffViewController' => 'DifferentialController',
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher',
'DifferentialDraft' => 'DifferentialDAO',
@ -4241,6 +4252,9 @@ phutil_register_library_map(array(
'FundInitiativeTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'FundInitiativeViewController' => 'FundController',
'FundSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'HarbormasterArcLintBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterArcUnitBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterAutotargetsTestCase' => 'PhabricatorTestCase',
'HarbormasterBuild' => array(
'HarbormasterDAO',
'PhabricatorApplicationTransactionInterface',
@ -4248,19 +4262,19 @@ phutil_register_library_map(array(
),
'HarbormasterBuildAbortedException' => 'Exception',
'HarbormasterBuildActionController' => 'HarbormasterController',
'HarbormasterBuildArcanistAutoplan' => 'HarbormasterBuildAutoplan',
'HarbormasterBuildArtifact' => array(
'HarbormasterDAO',
'PhabricatorPolicyInterface',
),
'HarbormasterBuildArtifactQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'HarbormasterBuildAutoplan' => 'Phobject',
'HarbormasterBuildCommand' => 'HarbormasterDAO',
'HarbormasterBuildDependencyDatasource' => 'PhabricatorTypeaheadDatasource',
'HarbormasterBuildEngine' => 'Phobject',
'HarbormasterBuildFailureException' => 'Exception',
'HarbormasterBuildGraph' => 'AbstractDirectedGraph',
'HarbormasterBuildItem' => 'HarbormasterDAO',
'HarbormasterBuildItemPHIDType' => 'PhabricatorPHIDType',
'HarbormasterBuildItemQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'HarbormasterBuildLintMessage' => 'HarbormasterDAO',
'HarbormasterBuildLog' => array(
'HarbormasterDAO',
'PhabricatorPolicyInterface',
@ -4314,6 +4328,7 @@ phutil_register_library_map(array(
'HarbormasterBuildTransaction' => 'PhabricatorApplicationTransaction',
'HarbormasterBuildTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
'HarbormasterBuildTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'HarbormasterBuildUnitMessage' => 'HarbormasterDAO',
'HarbormasterBuildViewController' => 'HarbormasterController',
'HarbormasterBuildWorker' => 'HarbormasterWorker',
'HarbormasterBuildable' => array(
@ -4349,6 +4364,7 @@ phutil_register_library_map(array(
'HarbormasterPlanRunController' => 'HarbormasterController',
'HarbormasterPlanViewController' => 'HarbormasterPlanController',
'HarbormasterPublishFragmentBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterQueryAutotargetsConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
'HarbormasterQueryBuildablesConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
'HarbormasterQueryBuildsConduitAPIMethod' => 'HarbormasterConduitAPIMethod',
'HarbormasterRemarkupRule' => 'PhabricatorObjectRemarkupRule',
@ -4359,6 +4375,7 @@ phutil_register_library_map(array(
'HarbormasterStepAddController' => 'HarbormasterController',
'HarbormasterStepDeleteController' => 'HarbormasterController',
'HarbormasterStepEditController' => 'HarbormasterController',
'HarbormasterTargetEngine' => 'Phobject',
'HarbormasterTargetWorker' => 'HarbormasterWorker',
'HarbormasterThrowExceptionBuildStep' => 'HarbormasterBuildStepImplementation',
'HarbormasterUIEventListener' => 'PhabricatorEventListener',
@ -4768,7 +4785,9 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface',
'PhabricatorDestructibleInterface',
'PhabricatorSpacesInterface',
),
'PassphraseCredentialAuthorPolicyRule' => 'PhabricatorPolicyRule',
'PassphraseCredentialConduitController' => 'PassphraseController',
'PassphraseCredentialControl' => 'AphrontFormControl',
'PassphraseCredentialCreateController' => 'PassphraseController',
@ -4788,6 +4807,8 @@ phutil_register_library_map(array(
'PassphraseCredentialTypeTestCase' => 'PhabricatorTestCase',
'PassphraseCredentialViewController' => 'PassphraseController',
'PassphraseDAO' => 'PhabricatorLiskDAO',
'PassphraseDefaultEditCapability' => 'PhabricatorPolicyCapability',
'PassphraseDefaultViewCapability' => 'PhabricatorPolicyCapability',
'PassphraseNoteCredentialType' => 'PassphraseCredentialType',
'PassphrasePasswordCredentialType' => 'PassphraseCredentialType',
'PassphrasePasswordKey' => 'PassphraseAbstractKey',

View file

@ -606,6 +606,23 @@ abstract class PhabricatorApplication
return idx($spec, 'template');
}
final public function getDefaultObjectTypePolicyMap() {
$map = array();
foreach ($this->getCustomCapabilities() as $capability => $spec) {
if (empty($spec['template'])) {
continue;
}
if (empty($spec['capability'])) {
continue;
}
$default = $this->getPolicy($capability);
$map[$spec['template']][$spec['capability']] = $default;
}
return $map;
}
public function getApplicationSearchDocumentTypes() {
return array();
}

View file

@ -53,6 +53,7 @@ final class PhabricatorCountdownApplication extends PhabricatorApplication {
PhabricatorCountdownDefaultViewCapability::CAPABILITY => array(
'caption' => pht('Default view policy for new countdowns.'),
'template' => PhabricatorCountdownCountdownPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
);
}

View file

@ -187,6 +187,7 @@ final class PhabricatorDifferentialApplication extends PhabricatorApplication {
DifferentialDefaultViewCapability::CAPABILITY => array(
'caption' => pht('Default view policy for newly created revisions.'),
'template' => DifferentialRevisionPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
);
}

View file

@ -0,0 +1,10 @@
<?php
final class DifferentialDiffTransactionQuery
extends PhabricatorApplicationTransactionQuery {
public function getTemplateApplicationTransaction() {
return new DifferentialDiffTransaction();
}
}

View file

@ -381,6 +381,7 @@ final class DifferentialDiff
$results = array();
$results['buildable.diff'] = $this->getID();
if ($this->revisionID) {
$revision = $this->getRevision();
$results['buildable.revision'] = $revision->getID();
$repo = $revision->getRepository();
@ -390,6 +391,7 @@ final class DifferentialDiff
$results['repository.vcs'] = $repo->getVersionControlSystem();
$results['repository.uri'] = $repo->getPublicCloneURI();
}
}
return $results;
}

View file

@ -142,10 +142,12 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
return array(
DiffusionDefaultViewCapability::CAPABILITY => array(
'template' => PhabricatorRepositoryRepositoryPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
DiffusionDefaultEditCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
'template' => PhabricatorRepositoryRepositoryPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
DiffusionDefaultPushCapability::CAPABILITY => array(
'template' => PhabricatorRepositoryRepositoryPHIDType::TYPECONST,

View file

@ -57,10 +57,12 @@ final class PhabricatorDivinerApplication extends PhabricatorApplication {
return array(
DivinerDefaultViewCapability::CAPABILITY => array(
'template' => DivinerBookPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
DivinerDefaultEditCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
'template' => DivinerBookPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
);
}

View file

@ -74,10 +74,12 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication {
return array(
DrydockDefaultViewCapability::CAPABILITY => array(
'template' => DrydockBlueprintPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
DrydockDefaultEditCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
'template' => DrydockBlueprintPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
DrydockCreateBlueprintsCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,

View file

@ -61,6 +61,7 @@ final class PhabricatorFilesApplication extends PhabricatorApplication {
FilesDefaultViewCapability::CAPABILITY => array(
'caption' => pht('Default view policy for newly created files.'),
'template' => PhabricatorFileFilePHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
);
}

View file

@ -0,0 +1,61 @@
<?php
final class HarbormasterAutotargetsTestCase extends PhabricatorTestCase {
protected function getPhabricatorTestCaseConfiguration() {
return array(
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
);
}
public function testGenerateHarbormasterAutotargets() {
$viewer = $this->generateNewTestUser();
$raw_diff = <<<EODIFF
diff --git a/fruit b/fruit
new file mode 100644
index 0000000..1c0f49d
--- /dev/null
+++ b/fruit
@@ -0,0 +1,2 @@
+apal
+banan
EODIFF;
$parser = new ArcanistDiffParser();
$changes = $parser->parseDiff($raw_diff);
$diff = DifferentialDiff::newFromRawChanges($viewer, $changes)
->setLintStatus(DifferentialLintStatus::LINT_AUTO_SKIP)
->setUnitStatus(DifferentialUnitStatus::UNIT_AUTO_SKIP)
->attachRevision(null)
->save();
$params = array(
'objectPHID' => $diff->getPHID(),
'targetKeys' => array(
HarbormasterArcLintBuildStepImplementation::STEPKEY,
HarbormasterArcUnitBuildStepImplementation::STEPKEY,
),
);
// Creation of autotargets should work from an empty state.
$result = id(new ConduitCall('harbormaster.queryautotargets', $params))
->setUser($viewer)
->execute();
$targets = idx($result, 'targetMap');
foreach ($params['targetKeys'] as $target_key) {
$this->assertTrue((bool)$result['targetMap'][$target_key]);
}
// Querying the same autotargets again should produce the same results,
// not make new ones.
$retry = id(new ConduitCall('harbormaster.queryautotargets', $params))
->setUser($viewer)
->execute();
$this->assertEqual($result, $retry);
}
}

View file

@ -0,0 +1,16 @@
<?php
final class HarbormasterBuildArcanistAutoplan
extends HarbormasterBuildAutoplan {
const PLANKEY = 'arcanist';
public function getAutoplanPlanKey() {
return self::PLANKEY;
}
public function getAutoplanName() {
return pht('Arcanist Client Results');
}
}

View file

@ -0,0 +1,44 @@
<?php
abstract class HarbormasterBuildAutoplan extends Phobject {
abstract public function getAutoplanPlanKey();
abstract public function getAutoplanName();
public static function getAutoplan($key) {
return idx(self::getAllAutoplans(), $key);
}
public static function getAllAutoplans() {
static $plans;
if ($plans === null) {
$objects = id(new PhutilSymbolLoader())
->setAncestorClass(__CLASS__)
->loadObjects();
$map = array();
foreach ($objects as $object) {
$key = $object->getAutoplanPlanKey();
if (!empty($map[$key])) {
$other = $map[$key];
throw new Exception(
pht(
'Two build autoplans (of classes "%s" and "%s") define the same '.
'key ("%s"). Each autoplan must have a unique key.',
get_class($other),
get_class($object),
$key));
}
$map[$key] = $object;
}
ksort($map);
$plans = $map;
}
return $plans;
}
}

View file

@ -0,0 +1,76 @@
<?php
final class HarbormasterQueryAutotargetsConduitAPIMethod
extends HarbormasterConduitAPIMethod {
public function getAPIMethodName() {
return 'harbormaster.queryautotargets';
}
public function getMethodDescription() {
return pht('Load or create build autotargets.');
}
protected function defineParamTypes() {
return array(
'objectPHID' => 'phid',
'targetKeys' => 'list<string>',
);
}
protected function defineReturnType() {
return 'map<string, phid>';
}
protected function execute(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$phid = $request->getValue('objectPHID');
// NOTE: We use withNames() to let monograms like "D123" work, which makes
// this a little easier to test. Real PHIDs will still work as expected.
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withNames(array($phid))
->executeOne();
if (!$object) {
throw new Exception(
pht(
'No such object "%s" exists.',
$phid));
}
if (!($object instanceof HarbormasterBuildableInterface)) {
throw new Exception(
pht(
'Object "%s" does not implement interface "%s". Autotargets may '.
'only be queried for buildable objects.',
$phid,
'HarbormasterBuildableInterface'));
}
$autotargets = $request->getValue('targetKeys', array());
if ($autotargets) {
$targets = id(new HarbormasterTargetEngine())
->setViewer($viewer)
->setObject($object)
->setAutoTargetKeys($autotargets)
->buildTargets();
} else {
$targets = array();
}
// Reorder the results according to the request order so we can make test
// assertions that subsequent calls return the same results.
$map = mpull($targets, 'getPHID');
$map = array_select_keys($map, $autotargets);
return array(
'targetMap' => $map,
);
}
}

View file

@ -2,21 +2,4 @@
abstract class HarbormasterController extends PhabricatorController {
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$can_create = $this->hasApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('New Build Plan'))
->setHref($this->getApplicationURI('plan/edit/'))
->setDisabled(!$can_create)
->setWorkflow(!$can_create)
->setIcon('fa-plus-square'));
return $crumbs;
}
}

View file

@ -3,22 +3,20 @@
final class HarbormasterPlanDisableController
extends HarbormasterPlanController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$this->requireApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->withIDs(array($request->getURIData('id')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$plan) {
return new Aphront404Response();
@ -63,14 +61,11 @@ final class HarbormasterPlanDisableController
$button = pht('Disable Plan');
}
$dialog = id(new AphrontDialogView())
->setUser($viewer)
return $this->newDialog()
->setTitle($title)
->appendChild($body)
->addSubmitButton($button)
->addCancelButton($plan_uri);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}

View file

@ -2,23 +2,22 @@
final class HarbormasterPlanEditController extends HarbormasterPlanController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$this->requireApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
if ($this->id) {
$id = $request->getURIData('id');
if ($id) {
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$plan) {
return new Aphront404Response();
@ -43,6 +42,7 @@ final class HarbormasterPlanEditController extends HarbormasterPlanController {
$editor = id(new HarbormasterBuildPlanEditor())
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request);
try {

View file

@ -2,19 +2,13 @@
final class HarbormasterPlanListController extends HarbormasterPlanController {
private $queryKey;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->queryKey = idx($data, 'queryKey');
}
public function processRequest() {
public function handleRequest(AphrontRequest $request) {
$controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($this->queryKey)
->setQueryKey($request->getURIData('queryKey'))
->setSearchEngine(new HarbormasterBuildPlanSearchEngine())
->setNavigation($this->buildSideNavView());
@ -44,4 +38,22 @@ final class HarbormasterPlanListController extends HarbormasterPlanController {
return $this->buildSideNavView(true)->getMenu();
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$can_create = $this->hasApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('New Build Plan'))
->setHref($this->getApplicationURI('plan/edit/'))
->setDisabled(!$can_create)
->setWorkflow(!$can_create)
->setIcon('fa-plus-square'));
return $crumbs;
}
}

View file

@ -2,20 +2,18 @@
final class HarbormasterPlanRunController extends HarbormasterController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$this->requireApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
$plan_id = $this->id;
$plan_id = $request->getURIData('id');
// NOTE: At least for now, this only requires the "Can Manage Plans"
// capability, not the "Can Edit" capability. Possibly it should have
// a more stringent requirement, though.
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withIDs(array($plan_id))
@ -87,15 +85,12 @@ final class HarbormasterPlanRunController extends HarbormasterController {
->setError($e_name)
->setValue($v_name));
$dialog = id(new AphrontDialogView())
return $this->newDialog()
->setWidth(AphrontDialogView::WIDTH_FULL)
->setUser($viewer)
->setTitle($title)
->appendChild($form)
->addCancelButton($cancel_uri)
->addSubmitButton($save_button);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}

View file

@ -2,17 +2,10 @@
final class HarbormasterPlanViewController extends HarbormasterPlanController {
private $id;
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getviewer();
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $this->id;
$id = $request->getURIData('id');
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
@ -79,11 +72,9 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
}
private function buildStepList(HarbormasterBuildPlan $plan) {
$request = $this->getRequest();
$viewer = $request->getUser();
$viewer = $this->getViewer();
$run_order =
HarbormasterBuildGraph::determineDependencyExecution($plan);
$run_order = HarbormasterBuildGraph::determineDependencyExecution($plan);
$steps = id(new HarbormasterBuildStepQuery())
->setViewer($viewer)
@ -91,9 +82,16 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
->execute();
$steps = mpull($steps, null, 'getPHID');
$can_edit = $this->hasApplicationCapability(
$has_manage = $this->hasApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
$has_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$plan,
PhabricatorPolicyCapability::CAN_EDIT);
$can_edit = ($has_manage && $has_edit);
$step_list = id(new PHUIObjectItemListView())
->setUser($viewer)
->setNoDataString(
@ -222,12 +220,32 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
$step_list->addItem($item);
}
return array($step_list, $has_any_conflicts, $is_deadlocking);
$step_list->setFlush(true);
$plan_id = $plan->getID();
$header = id(new PHUIHeaderView())
->setHeader(pht('Build Steps'))
->addActionLink(
id(new PHUIButtonView())
->setText(pht('Add Build Step'))
->setHref($this->getApplicationURI("step/add/{$plan_id}/"))
->setTag('a')
->setIcon(
id(new PHUIIconView())
->setIconFont('fa-plus'))
->setDisabled(!$can_edit)
->setWorkflow(true));
$step_box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($step_list);
return array($step_box, $has_any_conflicts, $is_deadlocking);
}
private function buildActionList(HarbormasterBuildPlan $plan) {
$request = $this->getRequest();
$viewer = $request->getUser();
$viewer = $this->getViewer();
$id = $plan->getID();
$list = id(new PhabricatorActionListView())
@ -235,9 +253,16 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
->setObject($plan)
->setObjectURI($this->getApplicationURI("plan/{$id}/"));
$can_edit = $this->hasApplicationCapability(
$has_manage = $this->hasApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
$has_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$plan,
PhabricatorPolicyCapability::CAN_EDIT);
$can_edit = ($has_manage && $has_edit);
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Plan'))
@ -264,20 +289,12 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
->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)
->setDisabled(!$has_manage)
->setIcon('fa-play-circle'));
return $list;

View file

@ -2,22 +2,20 @@
final class HarbormasterStepAddController extends HarbormasterController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$this->requireApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->withIDs(array($request->getURIData('id')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$plan) {
return new Aphront404Response();
@ -26,10 +24,17 @@ final class HarbormasterStepAddController extends HarbormasterController {
$plan_id = $plan->getID();
$cancel_uri = $this->getApplicationURI("plan/{$plan_id}/");
$all = HarbormasterBuildStepImplementation::getImplementations();
foreach ($all as $key => $impl) {
if ($impl->shouldRequireAutotargeting()) {
unset($all[$key]);
}
}
$errors = array();
if ($request->isFormPost()) {
$class = $request->getStr('class');
if (!HarbormasterBuildStepImplementation::getImplementation($class)) {
if (empty($all[$class])) {
$errors[] = pht('Choose the type of build step you want to add.');
}
if (!$errors) {
@ -41,7 +46,6 @@ final class HarbormasterStepAddController extends HarbormasterController {
$control = id(new AphrontFormRadioButtonControl())
->setName('class');
$all = HarbormasterBuildStepImplementation::getImplementations();
foreach ($all as $class => $implementation) {
$control->addButton(
$class,

View file

@ -2,27 +2,25 @@
final class HarbormasterStepDeleteController extends HarbormasterController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$this->requireApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
$id = $this->id;
$id = $request->getURIData('id');
$step = id(new HarbormasterBuildStepQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if ($step === null) {
throw new Exception(pht('Build step not found!'));
if (!$step) {
return new Aphront404Response();
}
$plan_id = $step->getBuildPlan()->getID();
@ -33,19 +31,14 @@ final class HarbormasterStepDeleteController extends HarbormasterController {
return id(new AphrontRedirectResponse())->setURI($done_uri);
}
$dialog = new AphrontDialogView();
$dialog->setTitle(pht('Really Delete Step?'))
->setUser($viewer)
->addSubmitButton(pht('Delete Build Step'))
->addCancelButton($done_uri);
$dialog->appendChild(
phutil_tag(
'p',
array(),
return $this->newDialog()
->setTitle(pht('Really Delete Step?'))
->appendParagraph(
pht(
"Are you sure you want to delete this step? ".
"This can't be undone!")));
return id(new AphrontDialogResponse())->setDialog($dialog);
"This can't be undone!"))
->addCancelButton($done_uri)
->addSubmitButton(pht('Delete Build Step'));
}
}

View file

@ -2,27 +2,22 @@
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();
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$this->requireApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
if ($this->id) {
$id = $request->getURIData('id');
if ($id) {
$step = id(new HarbormasterBuildStepQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$step) {
return new Aphront404Response();
@ -31,23 +26,35 @@ final class HarbormasterStepEditController extends HarbormasterController {
$is_new = false;
} else {
$plan_id = $request->getURIData('plan');
$class = $request->getURIData('class');
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withIDs(array($this->planID))
->withIDs(array($plan_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$plan) {
return new Aphront404Response();
}
$impl = HarbormasterBuildStepImplementation::getImplementation(
$this->className);
$impl = HarbormasterBuildStepImplementation::getImplementation($class);
if (!$impl) {
return new Aphront404Response();
}
if ($impl->shouldRequireAutotargeting()) {
// No manual creation of autotarget steps.
return new Aphront404Response();
}
$step = HarbormasterBuildStep::initializeNewStep($viewer)
->setBuildPlanPHID($plan->getPHID())
->setClassName($this->className);
->setClassName($class);
$is_new = true;
}

View file

@ -0,0 +1,251 @@
<?php
final class HarbormasterTargetEngine extends Phobject {
private $viewer;
private $object;
private $autoTargetKeys;
public function setViewer($viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setObject(HarbormasterBuildableInterface $object) {
$this->object = $object;
return $this;
}
public function getObject() {
return $this->object;
}
public function setAutoTargetKeys(array $auto_keys) {
$this->autoTargetKeys = $auto_keys;
return $this;
}
public function getAutoTargetKeys() {
return $this->autoTargetKeys;
}
public function buildTargets() {
$object = $this->getObject();
$viewer = $this->getViewer();
$step_map = $this->generateBuildStepMap($this->getAutoTargetKeys());
$buildable = HarbormasterBuildable::createOrLoadExisting(
$viewer,
$object->getHarbormasterBuildablePHID(),
$object->getHarbormasterContainerPHID());
$target_map = $this->generateBuildTargetMap($buildable, $step_map);
return $target_map;
}
/**
* Get a map of the @{class:HarbormasterBuildStep} objects for a list of
* autotarget keys.
*
* This method creates the steps if they do not yet exist.
*
* @param list<string> Autotarget keys, like `"core.arc.lint"`.
* @return map<string, object> Map of keys to step objects.
*/
private function generateBuildStepMap(array $autotargets) {
$viewer = $this->getViewer();
$autosteps = $this->getAutosteps($autotargets);
$autosteps = mgroup($autosteps, 'getBuildStepAutotargetPlanKey');
$plans = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withPlanAutoKeys(array_keys($autosteps))
->needBuildSteps(true)
->execute();
$plans = mpull($plans, null, 'getPlanAutoKey');
// NOTE: When creating the plan and steps, we save the autokeys as the
// names. These won't actually be shown in the UI, but make the data more
// consistent for secondary consumers like typeaheads.
$step_map = array();
foreach ($autosteps as $plan_key => $steps) {
$plan = idx($plans, $plan_key);
if (!$plan) {
$plan = HarbormasterBuildPlan::initializeNewBuildPlan($viewer)
->setName($plan_key)
->setPlanAutoKey($plan_key);
}
$current = $plan->getBuildSteps();
$current = mpull($current, null, 'getStepAutoKey');
$new_steps = array();
foreach ($steps as $step_key => $step) {
if (isset($current[$step_key])) {
$step_map[$step_key] = $current[$step_key];
continue;
}
$new_step = HarbormasterBuildStep::initializeNewStep($viewer)
->setName($step_key)
->setClassName(get_class($step))
->setStepAutoKey($step_key);
$new_steps[$step_key] = $new_step;
}
if ($new_steps) {
$plan->openTransaction();
if (!$plan->getPHID()) {
$plan->save();
}
foreach ($new_steps as $step_key => $step) {
$step->setBuildPlanPHID($plan->getPHID());
$step->save();
$step->attachBuildPlan($plan);
$step_map[$step_key] = $step;
}
$plan->saveTransaction();
}
}
return $step_map;
}
/**
* Get all of the @{class:HarbormasterBuildStepImplementation} objects for
* a list of autotarget keys.
*
* @param list<string> Autotarget keys, like `"core.arc.lint"`.
* @return map<string, object> Map of keys to implementations.
*/
private function getAutosteps(array $autotargets) {
$all_steps = HarbormasterBuildStepImplementation::getImplementations();
$all_steps = mpull($all_steps, null, 'getBuildStepAutotargetStepKey');
// Make sure all the targets really exist.
foreach ($autotargets as $autotarget) {
if (empty($all_steps[$autotarget])) {
throw new Exception(
pht(
'No build step provides autotarget "%s"!',
$autotarget));
}
}
return array_select_keys($all_steps, $autotargets);
}
/**
* Get a list of @{class:HarbormasterBuildTarget} objects for a list of
* autotarget keys.
*
* If some targets or builds do not exist, they are created.
*
* @param HarbormasterBuildable A buildable.
* @param map<string, object> Map of keys to steps.
* @return map<string, object> Map of keys to targets.
*/
private function generateBuildTargetMap(
HarbormasterBuildable $buildable,
array $step_map) {
$viewer = $this->getViewer();
$plan_map = mgroup($step_map, 'getBuildPlanPHID');
$builds = id(new HarbormasterBuildQuery())
->setViewer($viewer)
->withBuildablePHIDs(array($buildable->getPHID()))
->withBuildPlanPHIDs(array_keys($plan_map))
->needBuildTargets(true)
->execute();
$autobuilds = array();
foreach ($builds as $build) {
$plan_key = $build->getBuildPlan()->getPlanAutoKey();
$autobuilds[$plan_key] = $build;
}
$new_builds = array();
foreach ($plan_map as $plan_phid => $steps) {
$plan = head($steps)->getBuildPlan();
$plan_key = $plan->getPlanAutoKey();
$build = idx($autobuilds, $plan_key);
if ($build) {
// We already have a build for this set of targets, so we don't need
// to do any work. (It's possible the build is an older build that
// doesn't have all of the right targets if new autotargets were
// recently introduced, but we don't currently try to construct them.)
continue;
}
// NOTE: Normally, `applyPlan()` does not actually generate targets.
// We need to apply the plan in-process to perform target generation.
// This is fine as long as autotargets are empty containers that don't
// do any work, which they always should be.
PhabricatorWorker::setRunAllTasksInProcess(true);
try {
// NOTE: We might race another process here to create the same build
// with the same `planAutoKey`. The database will prevent this and
// using autotargets only currently makes sense if you just created the
// resource and "own" it, so we don't try to handle this, but may need
// to be more careful here if use of autotargets expands.
$build = $buildable->applyPlan($plan);
PhabricatorWorker::setRunAllTasksInProcess(false);
} catch (Exception $ex) {
PhabricatorWorker::setRunAllTasksInProcess(false);
throw $ex;
}
$new_builds[] = $build;
}
if ($new_builds) {
$all_targets = id(new HarbormasterBuildTargetQuery())
->setViewer($viewer)
->withBuildPHIDs(mpull($new_builds, 'getPHID'))
->execute();
} else {
$all_targets = array();
}
foreach ($builds as $build) {
foreach ($build->getBuildTargets() as $target) {
$all_targets[] = $target;
}
}
$target_map = array();
foreach ($all_targets as $target) {
$target_key = $target
->getImplementation()
->getBuildStepAutotargetStepKey();
if (!$target_key) {
continue;
}
$target_map[$target_key] = $target;
}
$target_map = array_select_keys($target_map, array_keys($step_map));
return $target_map;
}
}

View file

@ -1,33 +0,0 @@
<?php
final class HarbormasterBuildItemPHIDType extends PhabricatorPHIDType {
const TYPECONST = 'HMBI';
public function getTypeName() {
return pht('Build Item');
}
public function newObject() {
return new HarbormasterBuildItem();
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new HarbormasterBuildItemQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$build_item = $objects[$phid];
}
}
}

View file

@ -1,60 +0,0 @@
<?php
final class HarbormasterBuildItemQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
protected function loadPage() {
$table = new HarbormasterBuildItem();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid in (%Ls)',
$this->phids);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
public function getQueryApplicationClass() {
return 'PhabricatorHarbormasterApplication';
}
}

View file

@ -7,6 +7,8 @@ final class HarbormasterBuildPlanQuery
private $phids;
private $statuses;
private $datasourceQuery;
private $planAutoKeys;
private $needBuildSteps;
public function withIDs(array $ids) {
$this->ids = $ids;
@ -28,6 +30,16 @@ final class HarbormasterBuildPlanQuery
return $this;
}
public function withPlanAutoKeys(array $keys) {
$this->planAutoKeys = $keys;
return $this;
}
public function needBuildSteps($need) {
$this->needBuildSteps = $need;
return $this;
}
public function newResultObject() {
return new HarbormasterBuildPlan();
}
@ -36,6 +48,26 @@ final class HarbormasterBuildPlanQuery
return $this->loadStandardPage($this->newResultObject());
}
protected function didFilterPage(array $page) {
if ($this->needBuildSteps) {
$plan_phids = mpull($page, 'getPHID');
$steps = id(new HarbormasterBuildStepQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
->withBuildPlanPHIDs($plan_phids)
->execute();
$steps = mgroup($steps, 'getBuildPlanPHID');
foreach ($page as $plan) {
$plan_steps = idx($steps, $plan->getPHID(), array());
$plan->attachBuildSteps($plan_steps);
}
}
return $page;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
@ -67,6 +99,13 @@ final class HarbormasterBuildPlanQuery
$this->datasourceQuery);
}
if ($this->planAutoKeys !== null) {
$where[] = qsprintf(
$conn,
'planAutoKey IN (%Ls)',
$this->planAutoKeys);
}
return $where;
}

View file

@ -88,6 +88,10 @@ final class HarbormasterBuildPlanSearchEngine
$item->setDisabled(true);
}
if ($plan->isAutoplan()) {
$item->addIcon('fa-lock grey', pht('Autoplan'));
}
$item->setHref($this->getApplicationURI("plan/{$id}/"));
$list->addItem($item);

View file

@ -0,0 +1,38 @@
<?php
final class HarbormasterArcLintBuildStepImplementation
extends HarbormasterBuildStepImplementation {
const STEPKEY = 'arcanist.lint';
public function getBuildStepAutotargetPlanKey() {
return HarbormasterBuildArcanistAutoplan::PLANKEY;
}
public function getBuildStepAutotargetStepKey() {
return self::STEPKEY;
}
public function shouldRequireAutotargeting() {
return true;
}
public function getName() {
return pht('Arcanist Lint Results');
}
public function getGenericDescription() {
return pht('Automatic `arc lint` step.');
}
public function execute(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
return;
}
public function shouldWaitForMessage(HarbormasterBuildTarget $target) {
return true;
}
}

View file

@ -0,0 +1,38 @@
<?php
final class HarbormasterArcUnitBuildStepImplementation
extends HarbormasterBuildStepImplementation {
const STEPKEY = 'arcanist.unit';
public function getBuildStepAutotargetPlanKey() {
return HarbormasterBuildArcanistAutoplan::PLANKEY;
}
public function getBuildStepAutotargetStepKey() {
return self::STEPKEY;
}
public function shouldRequireAutotargeting() {
return true;
}
public function getName() {
return pht('Arcanist Unit Results');
}
public function getGenericDescription() {
return pht('Automatic `arc unit` step.');
}
public function execute(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
return;
}
public function shouldWaitForMessage(HarbormasterBuildTarget $target) {
return true;
}
}

View file

@ -1,5 +1,8 @@
<?php
/**
* @task autotarget Automatic Targets
*/
abstract class HarbormasterBuildStepImplementation extends Phobject {
private $settings;
@ -249,4 +252,20 @@ abstract class HarbormasterBuildStepImplementation extends Phobject {
}
}
/* -( Automatic Targets )-------------------------------------------------- */
public function getBuildStepAutotargetStepKey() {
return null;
}
public function getBuildStepAutotargetPlanKey() {
throw new PhutilMethodNotImplementedException();
}
public function shouldRequireAutotargeting() {
return false;
}
}

View file

@ -141,8 +141,14 @@ final class HarbormasterBuildable extends HarbormasterDAO
$build = HarbormasterBuild::initializeNewBuild($viewer)
->setBuildablePHID($this->getPHID())
->setBuildPlanPHID($plan->getPHID())
->setBuildStatus(HarbormasterBuild::STATUS_PENDING)
->save();
->setBuildStatus(HarbormasterBuild::STATUS_PENDING);
$auto_key = $plan->getPlanAutoKey();
if ($auto_key) {
$build->setPlanAutoKey($auto_key);
}
$build->save();
PhabricatorWorker::scheduleTask(
'HarbormasterBuildWorker',

View file

@ -9,6 +9,7 @@ final class HarbormasterBuild extends HarbormasterDAO
protected $buildPlanPHID;
protected $buildStatus;
protected $buildGeneration;
protected $planAutoKey;
private $buildable = self::ATTACHABLE;
private $buildPlan = self::ATTACHABLE;
@ -148,6 +149,7 @@ final class HarbormasterBuild extends HarbormasterDAO
self::CONFIG_COLUMN_SCHEMA => array(
'buildStatus' => 'text32',
'buildGeneration' => 'uint32',
'planAutoKey' => 'text32?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_buildable' => array(
@ -159,6 +161,10 @@ final class HarbormasterBuild extends HarbormasterDAO
'key_status' => array(
'columns' => array('buildStatus'),
),
'key_planautokey' => array(
'columns' => array('buildablePHID', 'planAutoKey'),
'unique' => true,
),
),
) + parent::getConfiguration();
}

View file

@ -1,19 +0,0 @@
<?php
final class HarbormasterBuildItem extends HarbormasterDAO {
protected $name;
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_NO_TABLE => true,
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
HarbormasterBuildItemPHIDType::TYPECONST);
}
}

View file

@ -0,0 +1,98 @@
<?php
final class HarbormasterBuildLintMessage
extends HarbormasterDAO {
protected $buildTargetPHID;
protected $path;
protected $line;
protected $characterOffset;
protected $code;
protected $severity;
protected $name;
protected $properties = array();
private $buildTarget = self::ATTACHABLE;
public static function initializeNewLintMessage(
HarbormasterBuildTarget $build_target) {
return id(new HarbormasterBuildLintMessage())
->setBuildTargetPHID($build_target->getPHID());
}
public static function newFromDictionary(
HarbormasterBuildTarget $build_target,
array $dict) {
$obj = self::initializeNewLintMessage($build_target);
$spec = array(
'path' => 'string',
'line' => 'optional int',
'char' => 'optional int',
'code' => 'string',
'severity' => 'string',
'name' => 'string',
'description' => 'optional string',
);
// We're just going to ignore extra keys for now, to make it easier to
// add stuff here later on.
$dict = array_select_keys($dict, array_keys($spec));
PhutilTypeSpec::checkMap($dict, $spec);
$obj->setPath($dict['path']);
$obj->setLine(idx($dict, 'line'));
$obj->setCharacterOffset(idx($dict, 'char'));
$obj->setCode($dict['code']);
$obj->setSeverity($dict['severity']);
$obj->setName($dict['name']);
$description = idx($dict, 'description');
if (strlen($description)) {
$obj->setProperty('description', $description);
}
return $obj;
}
protected function getConfiguration() {
return array(
self::CONFIG_SERIALIZATION => array(
'properties' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'path' => 'text',
'line' => 'uint32?',
'characterOffset' => 'uint32?',
'code' => 'text32',
'severity' => 'text32',
'name' => 'text255',
),
self::CONFIG_KEY_SCHEMA => array(
'key_target' => array(
'columns' => array('buildTargetPHID'),
),
),
) + parent::getConfiguration();
}
public function attachBuildTarget(HarbormasterBuildTarget $build_target) {
$this->buildTarget = $build_target;
return $this;
}
public function getBuildTarget() {
return $this->assertAttached($this->buildTarget);
}
public function getProperty($key, $default = null) {
return idx($this->properties, $key, $default);
}
public function setProperty($key, $value) {
$this->properties[$key] = $value;
return $this;
}
}

View file

@ -175,8 +175,16 @@ final class HarbormasterBuildTarget extends HarbormasterDAO
return $this->implementation;
}
public function isAutotarget() {
try {
return (bool)$this->getImplementation()->getBuildStepAutotargetPlanKey();
} catch (Exception $e) {
return false;
}
}
public function getName() {
if (strlen($this->name)) {
if (strlen($this->name) && !$this->isAutotarget()) {
return $this->name;
}

View file

@ -0,0 +1,100 @@
<?php
final class HarbormasterBuildUnitMessage
extends HarbormasterDAO {
protected $buildTargetPHID;
protected $engine;
protected $namespace;
protected $name;
protected $result;
protected $duration;
protected $properties = array();
private $buildTarget = self::ATTACHABLE;
public static function initializeNewUnitMessage(
HarbormasterBuildTarget $build_target) {
return id(new HarbormasterBuildLintMessage())
->setBuildTargetPHID($build_target->getPHID());
}
public static function newFromDictionary(
HarbormasterBuildTarget $build_target,
array $dict) {
$obj = self::initializeNewUnitMessage($build_target);
$spec = array(
'engine' => 'optional string',
'namespace' => 'optional string',
'name' => 'string',
'result' => 'string',
'duration' => 'optional float',
'path' => 'optional string',
'coverage' => 'optional string',
);
// We're just going to ignore extra keys for now, to make it easier to
// add stuff here later on.
$dict = array_select_keys($dict, array_keys($spec));
PhutilTypeSpec::checkMap($dict, $spec);
$obj->setEngine(idx($dict, 'engine', ''));
$obj->setNamespace(idx($dict, 'namespace', ''));
$obj->setName($dict['name']);
$obj->setResult($dict['result']);
$obj->setDuration(idx($dict, 'duration'));
$path = idx($dict, 'path');
if (strlen($path)) {
$obj->setProperty('path', $path);
}
$coverage = idx($dict, 'coverage');
if (strlen($coverage)) {
$obj->setProperty('coverage', $coverage);
}
return $obj;
}
protected function getConfiguration() {
return array(
self::CONFIG_SERIALIZATION => array(
'properties' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'engine' => 'text255',
'namespace' => 'text255',
'name' => 'text255',
'result' => 'text32',
'duration' => 'double?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_target' => array(
'columns' => array('buildTargetPHID'),
),
),
) + parent::getConfiguration();
}
public function attachBuildTarget(HarbormasterBuildTarget $build_target) {
$this->buildTarget = $build_target;
return $this;
}
public function getBuildTarget() {
return $this->assertAttached($this->buildTarget);
}
public function getProperty($key, $default = null) {
return idx($this->properties, $key, $default);
}
public function setProperty($key, $value) {
$this->properties[$key] = $value;
return $this;
}
}

View file

@ -1,5 +1,8 @@
<?php
/**
* @task autoplan Autoplans
*/
final class HarbormasterBuildPlan extends HarbormasterDAO
implements
PhabricatorApplicationTransactionInterface,
@ -8,6 +11,7 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
protected $name;
protected $planStatus;
protected $planAutoKey;
const STATUS_ACTIVE = 'active';
const STATUS_DISABLED = 'disabled';
@ -16,7 +20,9 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
public static function initializeNewBuildPlan(PhabricatorUser $actor) {
return id(new HarbormasterBuildPlan())
->setPlanStatus(self::STATUS_ACTIVE);
->setName('')
->setPlanStatus(self::STATUS_ACTIVE)
->attachBuildSteps(array());
}
protected function getConfiguration() {
@ -25,6 +31,7 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'sort128',
'planStatus' => 'text32',
'planAutoKey' => 'text32?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_status' => array(
@ -33,6 +40,10 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
'key_name' => array(
'columns' => array('name'),
),
'key_planautokey' => array(
'columns' => array('planAutoKey'),
'unique' => true,
),
),
) + parent::getConfiguration();
}
@ -57,6 +68,33 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
}
/* -( Autoplans )---------------------------------------------------------- */
public function isAutoplan() {
return ($this->getPlanAutoKey() !== null);
}
public function getAutoplan() {
if (!$this->isAutoplan()) {
return null;
}
return HarbormasterBuildAutoplan::getAutoplan($this->getPlanAutoKey());
}
public function getName() {
$autoplan = $this->getAutoplan();
if ($autoplan) {
return $autoplan->getAutoplanName();
}
return parent::getName();
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */
@ -102,12 +140,22 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return PhabricatorPolicies::getMostOpenPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
// NOTE: In practice, this policy is always limited by the "Mangage
// Build Plans" policy.
if ($this->isAutoplan()) {
return PhabricatorPolicies::POLICY_NOONE;
}
return PhabricatorPolicies::getMostOpenPolicy();
}
}
@ -117,6 +165,19 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
}
public function describeAutomaticCapability($capability) {
return null;
$messages = array();
switch ($capability) {
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->isAutoplan()) {
$messages[] = pht(
'This is an autoplan (a builtin plan provided by an application) '.
'so it can not be edited.');
}
break;
}
return $messages;
}
}

View file

@ -12,13 +12,16 @@ final class HarbormasterBuildStep extends HarbormasterDAO
protected $className;
protected $details = array();
protected $sequence = 0;
protected $stepAutoKey;
private $buildPlan = self::ATTACHABLE;
private $customFields = self::ATTACHABLE;
private $implementation;
public static function initializeNewStep(PhabricatorUser $actor) {
return id(new HarbormasterBuildStep());
return id(new HarbormasterBuildStep())
->setName('')
->setDescription('');
}
protected function getConfiguration() {
@ -37,11 +40,16 @@ final class HarbormasterBuildStep extends HarbormasterDAO
// which predated editable names. These should be backfilled with
// default names, then the code for handling `null` shoudl be removed.
'name' => 'text255?',
'stepAutoKey' => 'text32?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_plan' => array(
'columns' => array('buildPlanPHID'),
),
'key_stepautokey' => array(
'columns' => array('buildPlanPHID', 'stepAutoKey'),
'unique' => true,
),
),
) + parent::getConfiguration();
}
@ -88,6 +96,10 @@ final class HarbormasterBuildStep extends HarbormasterDAO
return $this->implementation;
}
public function isAutostep() {
return ($this->getStepAutoKey() !== null);
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
@ -118,6 +130,7 @@ final class HarbormasterBuildStep extends HarbormasterDAO
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}

View file

@ -77,9 +77,11 @@ final class PhabricatorLegalpadApplication extends PhabricatorApplication {
LegalpadCreateDocumentsCapability::CAPABILITY => array(),
LegalpadDefaultViewCapability::CAPABILITY => array(
'template' => PhabricatorLegalpadDocumentPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
LegalpadDefaultEditCapability::CAPABILITY => array(
'template' => PhabricatorLegalpadDocumentPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
);
}

View file

@ -132,10 +132,12 @@ final class PhabricatorManiphestApplication extends PhabricatorApplication {
ManiphestDefaultViewCapability::CAPABILITY => array(
'caption' => pht('Default view policy for newly created tasks.'),
'template' => ManiphestTaskPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
ManiphestDefaultEditCapability::CAPABILITY => array(
'caption' => pht('Default edit policy for newly created tasks.'),
'template' => ManiphestTaskPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
ManiphestEditStatusCapability::CAPABILITY => array(),
ManiphestEditAssignCapability::CAPABILITY => array(),

View file

@ -25,6 +25,12 @@ final class ManiphestBatchEditController extends ManiphestController {
$task_ids = $request->getStrList('batch');
}
if (!$task_ids) {
throw new Exception(
pht(
'No tasks are selected.'));
}
$tasks = id(new ManiphestTaskQuery())
->setViewer($viewer)
->withIDs($task_ids)
@ -37,6 +43,12 @@ final class ManiphestBatchEditController extends ManiphestController {
->needProjectPHIDs(true)
->execute();
if (!$tasks) {
throw new Exception(
pht(
"You don't have permission to edit any of the selected tasks."));
}
if ($project) {
$cancel_uri = '/project/board/'.$project->getID().'/';
$redirect_uri = $cancel_uri;

View file

@ -6,13 +6,13 @@
*/
final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
private $taskIDs = array();
private $taskPHIDs = array();
private $authorPHIDs = array();
private $ownerPHIDs = array();
private $taskIDs;
private $taskPHIDs;
private $authorPHIDs;
private $ownerPHIDs;
private $noOwner;
private $anyOwner;
private $subscriberPHIDs = array();
private $subscriberPHIDs;
private $dateCreatedAfter;
private $dateCreatedBefore;
private $dateModifiedAfter;
@ -216,75 +216,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
$task_dao = new ManiphestTask();
$conn = $task_dao->establishConnection('r');
$where = array();
$where[] = $this->buildTaskIDsWhereClause($conn);
$where[] = $this->buildTaskPHIDsWhereClause($conn);
$where[] = $this->buildStatusWhereClause($conn);
$where[] = $this->buildStatusesWhereClause($conn);
$where[] = $this->buildDependenciesWhereClause($conn);
$where[] = $this->buildAuthorWhereClause($conn);
$where[] = $this->buildOwnerWhereClause($conn);
$where[] = $this->buildFullTextWhereClause($conn);
if ($this->dateCreatedAfter) {
$where[] = qsprintf(
$conn,
'task.dateCreated >= %d',
$this->dateCreatedAfter);
}
if ($this->dateCreatedBefore) {
$where[] = qsprintf(
$conn,
'task.dateCreated <= %d',
$this->dateCreatedBefore);
}
if ($this->dateModifiedAfter) {
$where[] = qsprintf(
$conn,
'task.dateModified >= %d',
$this->dateModifiedAfter);
}
if ($this->dateModifiedBefore) {
$where[] = qsprintf(
$conn,
'task.dateModified <= %d',
$this->dateModifiedBefore);
}
if ($this->priorities) {
$where[] = qsprintf(
$conn,
'task.priority IN (%Ld)',
$this->priorities);
}
if ($this->subpriorities) {
$where[] = qsprintf(
$conn,
'task.subpriority IN (%Lf)',
$this->subpriorities);
}
if ($this->subpriorityMin) {
$where[] = qsprintf(
$conn,
'task.subpriority >= %f',
$this->subpriorityMin);
}
if ($this->subpriorityMax) {
$where[] = qsprintf(
$conn,
'task.subpriority <= %f',
$this->subpriorityMax);
}
$where[] = $this->buildWhereClauseParts($conn);
$where = $this->formatWhereClause($where);
$where = $this->buildWhereClause($conn);
$group_column = '';
switch ($this->groupBy) {
@ -392,28 +324,101 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
return $tasks;
}
private function buildTaskIDsWhereClause(AphrontDatabaseConnection $conn) {
if (!$this->taskIDs) {
return null;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
return qsprintf(
$where[] = $this->buildStatusWhereClause($conn);
$where[] = $this->buildDependenciesWhereClause($conn);
$where[] = $this->buildOwnerWhereClause($conn);
$where[] = $this->buildFullTextWhereClause($conn);
if ($this->taskIDs !== null) {
$where[] = qsprintf(
$conn,
'task.id in (%Ld)',
$this->taskIDs);
}
private function buildTaskPHIDsWhereClause(AphrontDatabaseConnection $conn) {
if (!$this->taskPHIDs) {
return null;
}
return qsprintf(
if ($this->taskPHIDs !== null) {
$where[] = qsprintf(
$conn,
'task.phid in (%Ls)',
$this->taskPHIDs);
}
if ($this->statuses !== null) {
$where[] = qsprintf(
$conn,
'task.status IN (%Ls)',
$this->statuses);
}
if ($this->authorPHIDs !== null) {
$where[] = qsprintf(
$conn,
'task.authorPHID in (%Ls)',
$this->authorPHIDs);
}
if ($this->dateCreatedAfter) {
$where[] = qsprintf(
$conn,
'task.dateCreated >= %d',
$this->dateCreatedAfter);
}
if ($this->dateCreatedBefore) {
$where[] = qsprintf(
$conn,
'task.dateCreated <= %d',
$this->dateCreatedBefore);
}
if ($this->dateModifiedAfter) {
$where[] = qsprintf(
$conn,
'task.dateModified >= %d',
$this->dateModifiedAfter);
}
if ($this->dateModifiedBefore) {
$where[] = qsprintf(
$conn,
'task.dateModified <= %d',
$this->dateModifiedBefore);
}
if ($this->priorities !== null) {
$where[] = qsprintf(
$conn,
'task.priority IN (%Ld)',
$this->priorities);
}
if ($this->subpriorities !== null) {
$where[] = qsprintf(
$conn,
'task.subpriority IN (%Lf)',
$this->subpriorities);
}
if ($this->subpriorityMin !== null) {
$where[] = qsprintf(
$conn,
'task.subpriority >= %f',
$this->subpriorityMin);
}
if ($this->subpriorityMax !== null) {
$where[] = qsprintf(
$conn,
'task.subpriority <= %f',
$this->subpriorityMax);
}
return $where;
}
private function buildStatusWhereClause(AphrontDatabaseConnection $conn) {
static $map = array(
self::STATUS_RESOLVED => ManiphestTaskStatus::STATUS_CLOSED_RESOLVED,
@ -448,27 +453,6 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
}
}
private function buildStatusesWhereClause(AphrontDatabaseConnection $conn) {
if ($this->statuses) {
return qsprintf(
$conn,
'task.status IN (%Ls)',
$this->statuses);
}
return null;
}
private function buildAuthorWhereClause(AphrontDatabaseConnection $conn) {
if (!$this->authorPHIDs) {
return null;
}
return qsprintf(
$conn,
'task.authorPHID in (%Ls)',
$this->authorPHIDs);
}
private function buildOwnerWhereClause(AphrontDatabaseConnection $conn) {
$subclause = array();
@ -590,7 +574,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
id(new ManiphestTask())->getTableName());
}
if ($this->subscriberPHIDs) {
if ($this->subscriberPHIDs !== null) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T e_ccs ON e_ccs.src = task.phid '.

View file

@ -73,10 +73,12 @@ final class PhabricatorNuanceApplication extends PhabricatorApplication {
NuanceSourceDefaultViewCapability::CAPABILITY => array(
'caption' => pht('Default view policy for newly created sources.'),
'template' => NuanceSourcePHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
NuanceSourceDefaultEditCapability::CAPABILITY => array(
'caption' => pht('Default edit policy for newly created sources.'),
'template' => NuanceSourcePHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
NuanceSourceManageCapability::CAPABILITY => array(),
);

View file

@ -63,4 +63,24 @@ final class PhabricatorPassphraseApplication extends PhabricatorApplication {
);
}
protected function getCustomCapabilities() {
$policy_key = id(new PassphraseCredentialAuthorPolicyRule())
->getObjectPolicyFullKey();
return array(
PassphraseDefaultViewCapability::CAPABILITY => array(
'caption' => pht('Default view policy for newly created credentials.'),
'template' => PassphraseCredentialPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
'default' => $policy_key,
),
PassphraseDefaultEditCapability::CAPABILITY => array(
'caption' => pht('Default edit policy for newly created credentials.'),
'template' => PassphraseCredentialPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
'default' => $policy_key,
),
);
}
}

View file

@ -0,0 +1,12 @@
<?php
final class PassphraseDefaultEditCapability
extends PhabricatorPolicyCapability {
const CAPABILITY = 'passphrase.default.edit';
public function getCapabilityName() {
return pht('Default Edit Policy');
}
}

View file

@ -0,0 +1,16 @@
<?php
final class PassphraseDefaultViewCapability
extends PhabricatorPolicyCapability {
const CAPABILITY = 'passphrase.default.view';
public function getCapabilityName() {
return pht('Default View Policy');
}
public function shouldAllowPublicPolicySetting() {
return true;
}
}

View file

@ -60,6 +60,7 @@ final class PassphraseCredentialEditController extends PassphraseController {
$e_name = true;
$v_desc = $credential->getDescription();
$v_space = $credential->getSpacePHID();
$v_username = $credential->getUsername();
$e_username = true;
@ -93,6 +94,7 @@ final class PassphraseCredentialEditController extends PassphraseController {
$v_is_locked = $request->getStr('lock');
$v_secret = $request->getStr('secret');
$v_space = $request->getStr('spacePHID');
$v_password = $request->getStr('password');
$v_decrypt = $v_secret;
@ -127,6 +129,7 @@ final class PassphraseCredentialEditController extends PassphraseController {
$type_is_locked = PassphraseCredentialTransaction::TYPE_LOCK;
$type_view_policy = PhabricatorTransactions::TYPE_VIEW_POLICY;
$type_edit_policy = PhabricatorTransactions::TYPE_EDIT_POLICY;
$type_space = PhabricatorTransactions::TYPE_SPACE;
$xactions = array();
@ -146,6 +149,10 @@ final class PassphraseCredentialEditController extends PassphraseController {
->setTransactionType($type_edit_policy)
->setNewValue($v_edit_policy);
$xactions[] = id(new PassphraseCredentialTransaction())
->setTransactionType($type_space)
->setNewValue($v_space);
// Open a transaction in case we're writing a new secret; this limits
// the amount of code which handles secret plaintexts.
$credential->openTransaction();
@ -244,13 +251,13 @@ final class PassphraseCredentialEditController extends PassphraseController {
->setValue($type->getCredentialTypeName()))
->appendChild(
id(new AphrontFormDividerControl()))
->appendChild(
->appendControl(
id(new AphrontFormPolicyControl())
->setName('viewPolicy')
->setPolicyObject($credential)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicies($policies))
->appendChild(
->appendControl(
id(new AphrontFormPolicyControl())
->setName('editPolicy')
->setPolicyObject($credential)

View file

@ -0,0 +1,48 @@
<?php
final class PassphraseCredentialAuthorPolicyRule
extends PhabricatorPolicyRule {
public function getObjectPolicyKey() {
return 'passphrase.author';
}
public function getObjectPolicyName() {
return pht('Credential Author');
}
public function getPolicyExplanation() {
return pht('The author of this credential can take this action.');
}
public function getRuleDescription() {
return pht('credential author');
}
public function canApplyToObject(PhabricatorPolicyInterface $object) {
return ($object instanceof PassphraseCredential);
}
public function applyRule(
PhabricatorUser $viewer,
$value,
PhabricatorPolicyInterface $object) {
$author_phid = $object->getAuthorPHID();
if (!$author_phid) {
return false;
}
$viewer_phid = $viewer->getPHID();
if (!$viewer_phid) {
return false;
}
return ($viewer_phid == $author_phid);
}
public function getValueControlType() {
return self::CONTROL_TYPE_NONE;
}
}

View file

@ -53,19 +53,12 @@ final class PassphraseCredentialQuery
return $this;
}
public function newResultObject() {
return new PassphraseCredential();
}
protected function loadPage() {
$table = new PassphraseCredential();
$conn_r = $table->establishConnection('r');
$rows = 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($rows);
return $this->loadStandardPage($this->newResultObject());
}
protected function willFilterPage(array $page) {
@ -99,61 +92,59 @@ final class PassphraseCredentialQuery
return $page;
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
$where[] = $this->buildPagingClause($conn_r);
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->credentialTypes) {
if ($this->credentialTypes !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'credentialType in (%Ls)',
$this->credentialTypes);
}
if ($this->providesTypes) {
if ($this->providesTypes !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'providesType IN (%Ls)',
$this->providesTypes);
}
if ($this->isDestroyed !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'isDestroyed = %d',
(int)$this->isDestroyed);
}
if ($this->allowConduit !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'allowConduit = %d',
(int)$this->allowConduit);
}
if (strlen($this->nameContains)) {
$where[] = qsprintf(
$conn_r,
'name LIKE %~',
$this->nameContains);
$conn,
'LOWER(name) LIKE %~',
phutil_utf8_strtolower($this->nameContains));
}
return $this->formatWhereClause($where);
return $where;
}
public function getQueryApplicationClass() {

View file

@ -11,58 +11,39 @@ final class PassphraseCredentialSearchEngine
return 'PhabricatorPassphraseApplication';
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter(
'isDestroyed',
$this->readBoolFromRequest($request, 'isDestroyed'));
$saved->setParameter('name', $request->getStr('name'));
return $saved;
public function newQuery() {
return new PassphraseCredentialQuery();
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PassphraseCredentialQuery());
$destroyed = $saved->getParameter('isDestroyed');
if ($destroyed !== null) {
$query->withIsDestroyed($destroyed);
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorSearchThreeStateField())
->setLabel(pht('Status'))
->setKey('isDestroyed')
->setOptions(
pht('Show All'),
pht('Show Only Destroyed Credentials'),
pht('Show Only Active Credentials')),
id(new PhabricatorSearchTextField())
->setLabel(pht('Name Contains'))
->setKey('name'),
);
}
$name = $saved->getParameter('name');
if (strlen($name)) {
$query->withNameContains($name);
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['isDestroyed'] !== null) {
$query->withIsDestroyed($map['isDestroyed']);
}
if (strlen($map['name'])) {
$query->withNameContains($map['name']);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$name = $saved_query->getParameter('name');
$form
->appendChild(
id(new AphrontFormSelectControl())
->setName('isDestroyed')
->setLabel(pht('Status'))
->setValue($this->getBoolFromQuery($saved_query, 'isDestroyed'))
->setOptions(
array(
'' => pht('Show All Credentials'),
'false' => pht('Show Only Active Credentials'),
'true' => pht('Show Only Destroyed Credentials'),
)))
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name Contains'))
->setValue($name));
}
protected function getURI($path) {
return '/passphrase/'.$path;
}

View file

@ -4,7 +4,8 @@ final class PassphraseCredential extends PassphraseDAO
implements
PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface,
PhabricatorDestructibleInterface {
PhabricatorDestructibleInterface,
PhabricatorSpacesInterface {
protected $name;
protected $credentialType;
@ -17,17 +18,29 @@ final class PassphraseCredential extends PassphraseDAO
protected $isDestroyed;
protected $isLocked = 0;
protected $allowConduit = 0;
protected $authorPHID;
protected $spacePHID;
private $secret = self::ATTACHABLE;
public static function initializeNewCredential(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
->withClasses(array('PhabricatorPassphraseApplication'))
->executeOne();
$view_policy = $app->getPolicy(PassphraseDefaultViewCapability::CAPABILITY);
$edit_policy = $app->getPolicy(PassphraseDefaultEditCapability::CAPABILITY);
return id(new PassphraseCredential())
->setName('')
->setUsername('')
->setDescription('')
->setIsDestroyed(0)
->setViewPolicy($actor->getPHID())
->setEditPolicy($actor->getPHID());
->setAuthorPHID($actor->getPHID())
->setViewPolicy($view_policy)
->setEditPolicy($edit_policy)
->setSpacePHID($actor->getDefaultSpacePHID());
}
public function getMonogram() {
@ -148,4 +161,13 @@ final class PassphraseCredential extends PassphraseDAO
$this->delete();
$this->saveTransaction();
}
/* -( PhabricatorSpacesInterface )----------------------------------------- */
public function getSpacePHID() {
return $this->spacePHID;
}
}

View file

@ -65,10 +65,12 @@ final class PhabricatorPasteApplication extends PhabricatorApplication {
PasteDefaultViewCapability::CAPABILITY => array(
'caption' => pht('Default view policy for newly created pastes.'),
'template' => PhabricatorPastePastePHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
PasteDefaultEditCapability::CAPABILITY => array(
'caption' => pht('Default edit policy for newly created pastes.'),
'template' => PhabricatorPastePastePHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
);
}

View file

@ -73,9 +73,11 @@ final class PhabricatorPholioApplication extends PhabricatorApplication {
return array(
PholioDefaultViewCapability::CAPABILITY => array(
'template' => PholioMockPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
PholioDefaultEditCapability::CAPABILITY => array(
'template' => PholioMockPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
);
}

View file

@ -6,9 +6,6 @@ final class PhabricatorPolicyEditController
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
// TODO: This doesn't do anything yet, but sets up template policies; see
// T6860.
$is_template = false;
$object_phid = $request->getURIData('objectPHID');
if ($object_phid) {
@ -23,7 +20,6 @@ final class PhabricatorPolicyEditController
$object_type = $request->getURIData('objectType');
if (!$object_type) {
$object_type = $request->getURIData('templateType');
$is_template = true;
}
$phid_types = PhabricatorPHIDType::getAllInstalledTypes($viewer);

View file

@ -3,24 +3,15 @@
final class PhabricatorPolicyExplainController
extends PhabricatorPolicyController {
private $phid;
private $capability;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->phid = $data['phid'];
$this->capability = $data['capability'];
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$phid = $this->phid;
$capability = $this->capability;
$phid = $request->getURIData('phid');
$capability = $request->getURIData('capability');
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
@ -84,11 +75,15 @@ final class PhabricatorPolicyExplainController
$handle->getTypeName(),
$handle->getObjectName());
return $dialog
$dialog
->setTitle(pht('Policy Details: %s', $object_name))
->appendParagraph($intro)
->appendChild($auto_info)
->addCancelButton($object_uri, pht('Done'));
$this->appendStrengthInformation($dialog, $object, $policy, $capability);
return $dialog;
}
private function appendSpaceInformation(
@ -180,4 +175,46 @@ final class PhabricatorPolicyExplainController
'object policy checks.'));
}
private function appendStrengthInformation(
AphrontDialogView $dialog,
PhabricatorPolicyInterface $object,
PhabricatorPolicy $policy,
$capability) {
$viewer = $this->getViewer();
$default_policy = PhabricatorPolicyQuery::getDefaultPolicyForObject(
$viewer,
$object,
$capability);
if (!$default_policy) {
return;
}
if ($default_policy->getPHID() == $policy->getPHID()) {
return;
}
if ($default_policy->isStrongerThan($policy)) {
$info = pht(
'This object has a less restrictive policy ("%s") than the default '.
'policy for similar objects (which is "%s").',
$policy->getShortName(),
$default_policy->getShortName());
} else if ($policy->isStrongerThan($default_policy)) {
$info = pht(
'This object has a more restrictive policy ("%s") than the default '.
'policy for similar objects (which is "%s").',
$policy->getShortName(),
$default_policy->getShortName());
} else {
$info = pht(
'This object has a different policy ("%s") than the default policy '.
'for similar objects (which is "%s").',
$policy->getShortName(),
$default_policy->getShortName());
}
$dialog->appendParagraph($info);
}
}

View file

@ -342,5 +342,46 @@ final class PhabricatorPolicyQuery
return $results;
}
public static function getDefaultPolicyForObject(
PhabricatorUser $viewer,
PhabricatorPolicyInterface $object,
$capability) {
$phid = $object->getPHID();
if (!$phid) {
return null;
}
$type = phid_get_type($phid);
$map = self::getDefaultObjectTypePolicyMap();
if (empty($map[$type][$capability])) {
return null;
}
$policy_phid = $map[$type][$capability];
return id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->withPHIDs(array($policy_phid))
->executeOne();
}
private static function getDefaultObjectTypePolicyMap() {
static $map;
if ($map === null) {
$map = array();
$apps = PhabricatorApplication::getAllApplications();
foreach ($apps as $app) {
$map += $app->getDefaultObjectTypePolicyMap();
}
}
return $map;
}
}

View file

@ -121,14 +121,17 @@ final class PhabricatorProjectApplication extends PhabricatorApplication {
ProjectDefaultViewCapability::CAPABILITY => array(
'caption' => pht('Default view policy for newly created projects.'),
'template' => PhabricatorProjectProjectPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
ProjectDefaultEditCapability::CAPABILITY => array(
'caption' => pht('Default edit policy for newly created projects.'),
'template' => PhabricatorProjectProjectPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
ProjectDefaultJoinCapability::CAPABILITY => array(
'caption' => pht('Default join policy for newly created projects.'),
'template' => PhabricatorProjectProjectPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_JOIN,
),
);
}

View file

@ -65,6 +65,7 @@ final class PhabricatorSlowvoteApplication extends PhabricatorApplication {
PhabricatorSlowvoteDefaultViewCapability::CAPABILITY => array(
'caption' => pht('Default view policy for new polls.'),
'template' => PhabricatorSlowvotePollPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
);
}

View file

@ -74,11 +74,13 @@ final class PhabricatorSpacesApplication extends PhabricatorApplication {
PhabricatorSpacesCapabilityDefaultView::CAPABILITY => array(
'caption' => pht('Default view policy for newly created spaces.'),
'template' => PhabricatorSpacesNamespacePHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
PhabricatorSpacesCapabilityDefaultEdit::CAPABILITY => array(
'caption' => pht('Default edit policy for newly created spaces.'),
'default' => PhabricatorPolicies::POLICY_ADMIN,
'template' => PhabricatorSpacesNamespacePHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
);
}

View file

@ -0,0 +1,247 @@
@title Troubleshooting Repository Imports
@group fieldmanual
Guide to the troubleshooting repositories which import incompletely.
Overview
========
When you first import an external source code repository (or push new commits to
a hosted repository), Phabricator imports those commits in the background.
While a repository is initially importing, some features won't work. While
individual commits are importing, some of their metadata won't be available in
the web UI.
Sometimes, the import process may hang or fail to complete. This document can
help you understand the import process and troubleshoot problems with it.
Understanding the Import Pipeline
=================================
Phabricator first performs commit discovery on repositories. This examines
a repository and identifies all the commits in it at a very shallow level,
then creates stub objects for them. These stub objects primarily serve to
assign various internal IDs to each commit.
Commit discovery occurs in the update phase, and you can learn more about
updates in @{article:Diffusion User Guide: Repository Updates}.
After commits are discovered, background tasks are queued to actually import
commits. These tasks do things like look at commit messages, trigger mentions
and autoclose rules, cache changes, trigger Herald, publish feed stories and
email, and apply Owners rules. You can learn more about some of these steps in
@{article:Diffusion User Guide: Autoclose}.
Specifically, the import pipeline has four steps:
- **Message**: Parses the commit message and author metadata.
- **Change**: Caches the paths the commit affected.
- **Owners**: Runs Owners rules.
- **Herald**: Runs Herald rules and publishes notifications.
These steps run in sequence for each commit, but all discovered commits import
in parallel.
Identifying Missing Steps
=========================
There are a few major pieces of information you can look at to understand where
the import process is stuck.
First, to identify which commits have missing import steps, run this command:
```
phabricator/ $ ./bin/repository importing rXYZ
```
That will show what work remains to be done. Each line shows a commit which
is discovered but not imported, and the import steps that are remaining for
that commit. Generally, the commit is stuck on the first step in the list.
Second, load the Daemon Console (at `/daemon/` in the web UI). This will show
what work is currently being done and waiting to complete. The most important
sections are "Queued Tasks" (work waiting in queue) and "Leased Tasks"
(work currently being done).
Third, run this command to look at the daemon logs:
```
phabricator/ $ ./bin/phd log
```
This can show you any errors the daemons have encountered recently.
The next sections will walk through how to use this information to understand
and resolve the issue.
Handling Permanent Failures
===========================
Some commits can not be imported, which will permanently stop a repository from
fully importing. These are rare, but can be caused by unusual data in a
repository, version peculiarities, or bugs in the importer.
Permanent failures usually look like a small number of commits stuck on the
"Message" or "Change" steps in the output of `repository importing`. If you
have a larger number of commits, it is less likely that there are any permanent
problems.
In the Daemon console, permanent failures usually look like a small number of
tasks in "Leased Tasks" with a large failure count. These tasks are retrying
until they succeed, but a bug is permanently preventing them from succeeding,
so they'll rack up a large number of retries over time.
In the daemon log, these commits usually emit specific errors showing why
they're failing to import.
These failures are the easiest to identify and understand, and can often be
resolved quickly. Choose some failing commit from the output of `bin/repository
importing` and use this command to re-run any missing steps manually in the
foreground:
```
phabricator/ $ ./bin/repository reparse --importing --trace rXYZabcdef012...
```
This command is always safe to run, no matter what the actual root cause of
the problem is.
If this fails with an error, you've likely identified a problem with
Phabricator. Collect as much information as you can about what makes the commit
special and file a bug in the upstream by following the instructions in
@{article:Contributing Bug Reports}.
If the commit imports cleanly, this is more likely to be caused by some other
issue.
Handling Temporary Failures
===========================
Some commits may temporarily fail to import: perhaps the network or services
may have briefly been down, or some configuration wasn't quite right, or the
daemons were killed halfway through the work.
These commits will retry eventually and usually succeed, but some of the retry
time limits are very conserative (up to 24 hours) and you might not want to
wait that long.
In the Daemon console, temporarily failures usually look like tasks in the
"Leased Tasks" column with a large "Expires" value but a low "Failures" count
(usually 0 or 1). The "Expires" column is showing how long Phabricator is
waiting to retry these tasks.
In the daemon log, these temporary failures might have created log entries, but
might also not have. For example, if the failure was rooted in a network issue
that probably will create a log entry, but if the faiulre was rooted in the
daemons being abruptly killed that may not create a log entry.
You can follow the instructions from "Handling Permanent Failures" above to
reparse steps individually to look for an error that represents a root cause,
but sometimes this can happen because of some transient issue which won't be
identifiable.
The easiest way to fix this is to restart the daemons. When you restart
daemons, all task leases are immediately expired, so any tasks waiting for a
long time will run right away:
```
phabricator/ $ ./bin/phd restart
```
This command is always safe to run, no matter what the actual root cause of
the problem is.
After restarting the daemons, any pending tasks should be able to retry
immediately.
For more information on managing the daemons, see
@{article:Managing Daemons with phd}.
Forced Parsing
==============
In rare cases, the actual tasks may be lost from the task queue. Usually, they
have been stolen by gremlins or spritied away by ghosts, or someone may have
been too ambitious with running manual SQL commands and deleted a bunch of
extra things they shouldn't have.
There is no normal set of conditions under which this should occur, but you can
force Phabricator to re-queue the tasks to recover from it if it does occur.
This will look like missing steps in `repository importing`, but nothing in the
"Queued Tasks" or "Leased Tasks" sections of the daemon console. The daemon
logs will also be empty, since the tasks have vanished.
To re-queue parse tasks for a repository, run this command, which will queue
up all of the missing work in `repository importing`:
```
phabricator/ $ ./bin/repository reparse --importing --all rXYZ
```
This command may cause duplicate work to occur if you have misdiagnosed the
problem and the tasks aren't actually lost. For example, it could queue a
second task to perform publishing, which could cause Phabricator to send a
second copy of email about the commit. Other than that, it is safe to run even
if this isn't the problem.
After running this command, you should see tasks in "Queued Tasks" and "Leased
Tasks" in the console which correspond to the commits in `repository
importing`, and progress should resume.
Forced Imports
==============
In some cases, you might want to force a repository to be flagged as imported
even though the import isn't complete. The most common and reasonable case
where you might want to do this is if you've identified a permanent failure
with a small number of commits (maybe just one) and reported it upstream, and
are waiting for a fix. You might want to start using the repository immediately,
even if a few things can't import yet.
You should be cautious about doing this. The "importing" flag controls
publishing of notifications and email, so if you flag a repository as imported
but it still has a lot of work queued, it may send an enormous amount of email
as that work completes.
To mark a repository as imported even though it really isn't, run this
command:
```
phabricator/ $ ./bin/repository mark-imported rXYZ
```
If you do this by mistake, you can reverse it later by using the
`--mark-not-imported` flag.
General Tips
============
Broadly, `bin/repository` contains several useful debugging commands which
let you figure out where failures are occuring. You can add the `--trace` flag
to any command to get more details about what it is doing. For any command,
you can use `help` to learn more about what it does and which flag it takes:
```
phabricator/ $ bin/repository help <command>
```
In particular, you can use flags with the the `repository reparse` command to
manually run parse steps in the foreground, including re-running steps and
running steps out of order.
Next Steps
==========
Continue by:
- returning to the @{article:Diffusion User Guide}.

View file

@ -94,9 +94,23 @@ repositories.
= Next Steps =
- Learn about creating a symbol index at
Continue by:
- learning how to creating a symbol index at
@{article:Diffusion User Guide: Symbol Indexes}; or
- set up repository hosting with
- setting up repository hosting with
@{article:Diffusion User Guide: Repository Hosting}; or
- understand daemons in detail with @{article:Managing Daemons with phd}; or
- give us feedback at @{article:Give Feedback! Get Support!}.
- managing repository hooks with
@{article:Diffusion User Guide: Commit Hooks}; or
- understanding daemons in more detail with
@{article:Managing Daemons with phd}.
If you're having trouble getting things working, these topic guides may be
helpful:
- get details about automatically closing tasks and revisions in response
to commits in @{article:Diffusion User Guide: Autoclose}; or
- understand how Phabricator updates repositories with
@{article:Diffusion User Guide: Repository Updates}; or
- fix issues with repository imports with
@{article:Troubleshooting Repository Imports}.

View file

@ -58,3 +58,11 @@ not necessarily that the repository is still importing.
no containing branch was configured to autoclose.
- //Field Not Present// This commit was processed before we implemented
this diagnostic feature, and no information is available.
Next Steps
==========
Continue by:
- troubleshooting in greater depth with
@{article:Troubleshooting Repository Imports}.

View file

@ -113,3 +113,11 @@ issues, using the `--trace` flag to get full details:
To catch potential issues with permissions, run this command as the same user
that the daemons run as.
Next Steps
==========
Continue by:
- troubleshooting in greater depth with
@{article:Troubleshooting Repository Imports}.

View file

@ -362,6 +362,7 @@ final class PHUIHeaderView extends AphrontTagView {
// NOTE: We'll do this even if the viewer has access to only one space, and
// show them information about the existence of spaces if they click
// through.
$use_space_policy = false;
if ($object instanceof PhabricatorSpacesInterface) {
$space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID(
$object);
@ -376,13 +377,46 @@ final class PHUIHeaderView extends AphrontTagView {
if ($space_policy) {
if ($space_policy->isStrongerThan($policy)) {
$policy = $space_policy;
$use_space_policy = true;
}
}
}
}
$container_classes = array();
$container_classes[] = 'policy-header-callout';
$phid = $object->getPHID();
// If we're going to show the object policy, try to determine if the object
// policy differs from the default policy. If it does, we'll call it out
// as changed.
if (!$use_space_policy) {
$default_policy = PhabricatorPolicyQuery::getDefaultPolicyForObject(
$viewer,
$object,
$view_capability);
if ($default_policy) {
if ($default_policy->getPHID() != $policy->getPHID()) {
$container_classes[] = 'policy-adjusted';
if ($default_policy->isStrongerThan($policy)) {
// The policy has strictly been weakened. For example, the
// default might be "All Users" and the current policy is "Public".
$container_classes[] = 'policy-adjusted-weaker';
} else if ($policy->isStrongerThan($default_policy)) {
// The policy has strictly been strengthened, and is now more
// restrictive than the default. For example, "All Users" has
// been replaced with "No One".
$container_classes[] = 'policy-adjusted-stronger';
} else {
// The policy has been adjusted but not strictly strengthened
// or weakened. For example, "Members of X" has been replaced with
// "Members of Y".
$container_classes[] = 'policy-adjusted-different';
}
}
}
}
$icon = id(new PHUIIconView())
->setIconFont($policy->getIcon().' bluegrey');
@ -395,7 +429,12 @@ final class PHUIHeaderView extends AphrontTagView {
),
$policy->getShortName());
return array($icon, $link);
return phutil_tag(
'span',
array(
'class' => implode(' ', $container_classes),
),
array($icon, $link));
}
}

View file

@ -144,6 +144,41 @@ body .phui-header-shell.phui-bleed-header
color: {$darkbluetext};
}
.policy-header-callout.policy-adjusted {
padding: 0 4px;
border-radius: 3px;
}
.policy-header-callout.policy-adjusted-weaker {
background: {$lightgreen};
border: 1px solid {$green};
}
.policy-header-callout.policy-adjusted-weaker .policy-link,
.policy-header-callout.policy-adjusted-weaker .phui-icon-view {
color: {$green};
}
.policy-header-callout.policy-adjusted-stronger {
background: {$lightred};
border: 1px solid {$red};
}
.policy-header-callout.policy-adjusted-stronger .policy-link,
.policy-header-callout.policy-adjusted-stronger .phui-icon-view {
color: {$red};
}
.policy-header-callout.policy-adjusted-different {
background: {$lightorange};
border: 1px solid {$orange};
}
.policy-header-callout.policy-adjusted-different .policy-link,
.policy-header-callout.policy-adjusted-different .phui-icon-view {
color: {$orange};
}
.phui-header-subheader .phui-header-status-dark {
color: {$sh-indigotext};
background: {$sh-indigobackground};