mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-24 04:29:04 +01:00
(stable) Promote 2021 Week 6
This commit is contained in:
commit
3e42f8be12
39 changed files with 1013 additions and 218 deletions
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
// @phase worker
|
||||||
|
|
||||||
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery(
|
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery(
|
||||||
'PhabricatorDashboardQuery');
|
'PhabricatorDashboardQuery');
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
// @phase worker
|
||||||
|
|
||||||
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery('HeraldRuleQuery');
|
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery('HeraldRuleQuery');
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
// @phase worker
|
||||||
|
|
||||||
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery('HeraldRuleQuery');
|
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery('HeraldRuleQuery');
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
// @phase worker
|
||||||
|
|
||||||
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery(
|
PhabricatorRebuildIndexesWorker::rebuildObjectsWithQuery(
|
||||||
'PhabricatorRepositoryQuery');
|
'PhabricatorRepositoryQuery');
|
||||||
|
|
5
resources/sql/autopatches/20210122.queuecontainer.01.sql
Normal file
5
resources/sql/autopatches/20210122.queuecontainer.01.sql
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_worker.worker_activetask
|
||||||
|
ADD containerPHID VARBINARY(64);
|
||||||
|
|
||||||
|
ALTER TABLE {$NAMESPACE}_worker.worker_archivetask
|
||||||
|
ADD containerPHID VARBINARY(64);
|
|
@ -5145,9 +5145,11 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorWorkerDestructionEngineExtension' => 'infrastructure/daemon/workers/engineextension/PhabricatorWorkerDestructionEngineExtension.php',
|
'PhabricatorWorkerDestructionEngineExtension' => 'infrastructure/daemon/workers/engineextension/PhabricatorWorkerDestructionEngineExtension.php',
|
||||||
'PhabricatorWorkerLeaseQuery' => 'infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php',
|
'PhabricatorWorkerLeaseQuery' => 'infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php',
|
||||||
'PhabricatorWorkerManagementCancelWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementCancelWorkflow.php',
|
'PhabricatorWorkerManagementCancelWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementCancelWorkflow.php',
|
||||||
|
'PhabricatorWorkerManagementDelayWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementDelayWorkflow.php',
|
||||||
'PhabricatorWorkerManagementExecuteWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementExecuteWorkflow.php',
|
'PhabricatorWorkerManagementExecuteWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementExecuteWorkflow.php',
|
||||||
'PhabricatorWorkerManagementFloodWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementFloodWorkflow.php',
|
'PhabricatorWorkerManagementFloodWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementFloodWorkflow.php',
|
||||||
'PhabricatorWorkerManagementFreeWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementFreeWorkflow.php',
|
'PhabricatorWorkerManagementFreeWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementFreeWorkflow.php',
|
||||||
|
'PhabricatorWorkerManagementPriorityWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementPriorityWorkflow.php',
|
||||||
'PhabricatorWorkerManagementRetryWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementRetryWorkflow.php',
|
'PhabricatorWorkerManagementRetryWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementRetryWorkflow.php',
|
||||||
'PhabricatorWorkerManagementWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php',
|
'PhabricatorWorkerManagementWorkflow' => 'infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php',
|
||||||
'PhabricatorWorkerPermanentFailureException' => 'infrastructure/daemon/workers/exception/PhabricatorWorkerPermanentFailureException.php',
|
'PhabricatorWorkerPermanentFailureException' => 'infrastructure/daemon/workers/exception/PhabricatorWorkerPermanentFailureException.php',
|
||||||
|
@ -6113,7 +6115,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
'PhabricatorApplicationTransactionInterface',
|
'PhabricatorApplicationTransactionInterface',
|
||||||
'PhabricatorProjectInterface',
|
'PhabricatorProjectInterface',
|
||||||
'AlmanacPropertyInterface',
|
|
||||||
'PhabricatorDestructibleInterface',
|
'PhabricatorDestructibleInterface',
|
||||||
'PhabricatorNgramsInterface',
|
'PhabricatorNgramsInterface',
|
||||||
'PhabricatorConduitResultInterface',
|
'PhabricatorConduitResultInterface',
|
||||||
|
@ -11973,9 +11974,11 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorWorkerDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
|
'PhabricatorWorkerDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
|
||||||
'PhabricatorWorkerLeaseQuery' => 'PhabricatorQuery',
|
'PhabricatorWorkerLeaseQuery' => 'PhabricatorQuery',
|
||||||
'PhabricatorWorkerManagementCancelWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
'PhabricatorWorkerManagementCancelWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||||
|
'PhabricatorWorkerManagementDelayWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||||
'PhabricatorWorkerManagementExecuteWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
'PhabricatorWorkerManagementExecuteWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||||
'PhabricatorWorkerManagementFloodWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
'PhabricatorWorkerManagementFloodWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||||
'PhabricatorWorkerManagementFreeWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
'PhabricatorWorkerManagementFreeWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||||
|
'PhabricatorWorkerManagementPriorityWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||||
'PhabricatorWorkerManagementRetryWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
'PhabricatorWorkerManagementRetryWorkflow' => 'PhabricatorWorkerManagementWorkflow',
|
||||||
'PhabricatorWorkerManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
'PhabricatorWorkerManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||||
'PhabricatorWorkerPermanentFailureException' => 'Exception',
|
'PhabricatorWorkerPermanentFailureException' => 'Exception',
|
||||||
|
|
|
@ -6,7 +6,6 @@ final class AlmanacNamespace
|
||||||
PhabricatorPolicyInterface,
|
PhabricatorPolicyInterface,
|
||||||
PhabricatorApplicationTransactionInterface,
|
PhabricatorApplicationTransactionInterface,
|
||||||
PhabricatorProjectInterface,
|
PhabricatorProjectInterface,
|
||||||
AlmanacPropertyInterface,
|
|
||||||
PhabricatorDestructibleInterface,
|
PhabricatorDestructibleInterface,
|
||||||
PhabricatorNgramsInterface,
|
PhabricatorNgramsInterface,
|
||||||
PhabricatorConduitResultInterface {
|
PhabricatorConduitResultInterface {
|
||||||
|
@ -17,13 +16,10 @@ final class AlmanacNamespace
|
||||||
protected $viewPolicy;
|
protected $viewPolicy;
|
||||||
protected $editPolicy;
|
protected $editPolicy;
|
||||||
|
|
||||||
private $almanacProperties = self::ATTACHABLE;
|
|
||||||
|
|
||||||
public static function initializeNewNamespace() {
|
public static function initializeNewNamespace() {
|
||||||
return id(new self())
|
return id(new self())
|
||||||
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
|
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
|
||||||
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
|
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN);
|
||||||
->attachAlmanacProperties(array());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getConfiguration() {
|
protected function getConfiguration() {
|
||||||
|
@ -113,53 +109,6 @@ final class AlmanacNamespace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( AlmanacPropertyInterface )------------------------------------------- */
|
|
||||||
|
|
||||||
|
|
||||||
public function attachAlmanacProperties(array $properties) {
|
|
||||||
assert_instances_of($properties, 'AlmanacProperty');
|
|
||||||
$this->almanacProperties = mpull($properties, null, 'getFieldName');
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAlmanacProperties() {
|
|
||||||
return $this->assertAttached($this->almanacProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasAlmanacProperty($key) {
|
|
||||||
$this->assertAttached($this->almanacProperties);
|
|
||||||
return isset($this->almanacProperties[$key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAlmanacProperty($key) {
|
|
||||||
return $this->assertAttachedKey($this->almanacProperties, $key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAlmanacPropertyValue($key, $default = null) {
|
|
||||||
if ($this->hasAlmanacProperty($key)) {
|
|
||||||
return $this->getAlmanacProperty($key)->getFieldValue();
|
|
||||||
} else {
|
|
||||||
return $default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAlmanacPropertyFieldSpecifications() {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function newAlmanacPropertyEditEngine() {
|
|
||||||
throw new PhutilMethodNotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAlmanacPropertySetTransactionType() {
|
|
||||||
throw new PhutilMethodNotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAlmanacPropertyDeleteTransactionType() {
|
|
||||||
throw new PhutilMethodNotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -490,7 +490,7 @@ abstract class PhabricatorApplication
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function getCustomPolicySetting($capability) {
|
private function getCustomPolicySetting($capability) {
|
||||||
if (!$this->isCapabilityEditable($capability)) {
|
if (!$this->isCapabilityEditable($capability)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -516,7 +516,7 @@ abstract class PhabricatorApplication
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final private function getCustomCapabilitySpecification($capability) {
|
private function getCustomCapabilitySpecification($capability) {
|
||||||
$custom = $this->getCustomCapabilities();
|
$custom = $this->getCustomCapabilities();
|
||||||
if (!isset($custom[$capability])) {
|
if (!isset($custom[$capability])) {
|
||||||
throw new Exception(pht("Unknown capability '%s'!", $capability));
|
throw new Exception(pht("Unknown capability '%s'!", $capability));
|
||||||
|
|
|
@ -203,7 +203,7 @@ final class DifferentialHunk
|
||||||
return implode('', $this->makeContent($include));
|
return implode('', $this->makeContent($include));
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function makeContent($include) {
|
private function makeContent($include) {
|
||||||
$lines = $this->getSplitLines();
|
$lines = $this->getSplitLines();
|
||||||
$results = array();
|
$results = array();
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,7 @@ abstract class DiffusionRequest extends Phobject {
|
||||||
*
|
*
|
||||||
* @task new
|
* @task new
|
||||||
*/
|
*/
|
||||||
final private function __construct() {
|
private function __construct() {
|
||||||
// <private>
|
// <private>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ abstract class DiffusionRequest extends Phobject {
|
||||||
* @return DiffusionRequest New request object.
|
* @return DiffusionRequest New request object.
|
||||||
* @task new
|
* @task new
|
||||||
*/
|
*/
|
||||||
final private static function newFromIdentifier(
|
private static function newFromIdentifier(
|
||||||
$identifier,
|
$identifier,
|
||||||
PhabricatorUser $viewer,
|
PhabricatorUser $viewer,
|
||||||
$need_edit = false) {
|
$need_edit = false) {
|
||||||
|
@ -174,7 +174,7 @@ abstract class DiffusionRequest extends Phobject {
|
||||||
* @return DiffusionRequest New request object.
|
* @return DiffusionRequest New request object.
|
||||||
* @task new
|
* @task new
|
||||||
*/
|
*/
|
||||||
final private static function newFromRepository(
|
private static function newFromRepository(
|
||||||
PhabricatorRepository $repository) {
|
PhabricatorRepository $repository) {
|
||||||
|
|
||||||
$map = array(
|
$map = array(
|
||||||
|
@ -205,7 +205,7 @@ abstract class DiffusionRequest extends Phobject {
|
||||||
* @return void
|
* @return void
|
||||||
* @task new
|
* @task new
|
||||||
*/
|
*/
|
||||||
final private function initializeFromDictionary(array $data) {
|
private function initializeFromDictionary(array $data) {
|
||||||
$blob = idx($data, 'blob');
|
$blob = idx($data, 'blob');
|
||||||
if (strlen($blob)) {
|
if (strlen($blob)) {
|
||||||
$blob = self::parseRequestBlob($blob, $this->supportsBranches());
|
$blob = self::parseRequestBlob($blob, $this->supportsBranches());
|
||||||
|
|
|
@ -8,9 +8,16 @@ final class PhabricatorPolicyManagementUnlockWorkflow
|
||||||
->setName('unlock')
|
->setName('unlock')
|
||||||
->setSynopsis(
|
->setSynopsis(
|
||||||
pht(
|
pht(
|
||||||
'Unlock an object which has policies that prevent it from being '.
|
'Unlock one or more objects by changing their view policies, edit '.
|
||||||
'viewed or edited.'))
|
'policies, or owners.'))
|
||||||
->setExamples('**unlock** --view __user__ __object__')
|
->setHelp(
|
||||||
|
pht(
|
||||||
|
'Identify each __object__ by passing an object name '.
|
||||||
|
'(like "T123") or a PHID (like "PHID-ABCD-1234...").'.
|
||||||
|
"\n\n".
|
||||||
|
'Not every type of object has an editable view policy, edit '.
|
||||||
|
'policy, or owner, so not all modes will work with all objects. '))
|
||||||
|
->setExamples('**unlock** --view __user__ __object__ ...')
|
||||||
->setArguments(
|
->setArguments(
|
||||||
array(
|
array(
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -682,7 +682,6 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
|
|
||||||
$this->queueCommitImportTask(
|
$this->queueCommitImportTask(
|
||||||
$repository,
|
$repository,
|
||||||
$commit->getID(),
|
|
||||||
$commit->getPHID(),
|
$commit->getPHID(),
|
||||||
$task_priority,
|
$task_priority,
|
||||||
$via = 'discovery');
|
$via = 'discovery');
|
||||||
|
|
|
@ -85,10 +85,9 @@ abstract class PhabricatorRepositoryEngine extends Phobject {
|
||||||
|
|
||||||
final protected function queueCommitImportTask(
|
final protected function queueCommitImportTask(
|
||||||
PhabricatorRepository $repository,
|
PhabricatorRepository $repository,
|
||||||
$commit_id,
|
|
||||||
$commit_phid,
|
$commit_phid,
|
||||||
$task_priority,
|
$task_priority,
|
||||||
$via = null) {
|
$via) {
|
||||||
|
|
||||||
$vcs = $repository->getVersionControlSystem();
|
$vcs = $repository->getVersionControlSystem();
|
||||||
switch ($vcs) {
|
switch ($vcs) {
|
||||||
|
@ -109,7 +108,7 @@ abstract class PhabricatorRepositoryEngine extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = array(
|
$data = array(
|
||||||
'commitID' => $commit_id,
|
'commitPHID' => $commit_phid,
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($via !== null) {
|
if ($via !== null) {
|
||||||
|
@ -119,6 +118,7 @@ abstract class PhabricatorRepositoryEngine extends Phobject {
|
||||||
$options = array(
|
$options = array(
|
||||||
'priority' => $task_priority,
|
'priority' => $task_priority,
|
||||||
'objectPHID' => $commit_phid,
|
'objectPHID' => $commit_phid,
|
||||||
|
'containerPHID' => $repository->getPHID(),
|
||||||
);
|
);
|
||||||
|
|
||||||
PhabricatorWorker::scheduleTask($class, $data, $options);
|
PhabricatorWorker::scheduleTask($class, $data, $options);
|
||||||
|
|
|
@ -597,7 +597,6 @@ final class PhabricatorRepositoryRefEngine
|
||||||
|
|
||||||
$this->queueCommitImportTask(
|
$this->queueCommitImportTask(
|
||||||
$repository,
|
$repository,
|
||||||
$row['id'],
|
|
||||||
$row['phid'],
|
$row['phid'],
|
||||||
$task_priority,
|
$task_priority,
|
||||||
$via = 'ref');
|
$via = 'ref');
|
||||||
|
|
|
@ -247,8 +247,9 @@ final class PhabricatorRepositoryManagementReparseWorkflow
|
||||||
// all the requested steps explicitly.
|
// all the requested steps explicitly.
|
||||||
|
|
||||||
$spec = array(
|
$spec = array(
|
||||||
'commitID' => $commit->getID(),
|
'commitPHID' => $commit->getPHID(),
|
||||||
'only' => !$importing,
|
'only' => !$importing,
|
||||||
|
'via' => 'reparse',
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($classes as $class) {
|
foreach ($classes as $class) {
|
||||||
|
@ -258,6 +259,8 @@ final class PhabricatorRepositoryManagementReparseWorkflow
|
||||||
$spec,
|
$spec,
|
||||||
array(
|
array(
|
||||||
'priority' => PhabricatorWorker::PRIORITY_IMPORT,
|
'priority' => PhabricatorWorker::PRIORITY_IMPORT,
|
||||||
|
'objectPHID' => $commit->getPHID(),
|
||||||
|
'containerPHID' => $repository->getPHID(),
|
||||||
));
|
));
|
||||||
} catch (PhabricatorWorkerPermanentFailureException $ex) {
|
} catch (PhabricatorWorkerPermanentFailureException $ex) {
|
||||||
// See T13315. We expect some reparse steps to occasionally raise
|
// See T13315. We expect some reparse steps to occasionally raise
|
||||||
|
|
|
@ -11,30 +11,51 @@ abstract class PhabricatorRepositoryCommitParserWorker
|
||||||
return $this->commit;
|
return $this->commit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$commit_id = idx($this->getTaskData(), 'commitID');
|
$viewer = $this->getViewer();
|
||||||
if (!$commit_id) {
|
$task_data = $this->getTaskData();
|
||||||
throw new PhabricatorWorkerPermanentFailureException(
|
|
||||||
pht('No "%s" in task data.', 'commitID'));
|
$commit_query = id(new DiffusionCommitQuery())
|
||||||
|
->setViewer($viewer);
|
||||||
|
|
||||||
|
$commit_phid = idx($task_data, 'commitPHID');
|
||||||
|
|
||||||
|
// TODO: See T13591. This supports execution of legacy tasks and can
|
||||||
|
// eventually be removed. Newer tasks use "commitPHID" instead of
|
||||||
|
// "commitID".
|
||||||
|
if (!$commit_phid) {
|
||||||
|
$commit_id = idx($task_data, 'commitID');
|
||||||
|
if ($commit_id) {
|
||||||
|
$legacy_commit = id(clone $commit_query)
|
||||||
|
->withIDs(array($commit_id))
|
||||||
|
->executeOne();
|
||||||
|
if ($legacy_commit) {
|
||||||
|
$commit_phid = $legacy_commit->getPHID();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$commit = id(new DiffusionCommitQuery())
|
if (!$commit_phid) {
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
throw new PhabricatorWorkerPermanentFailureException(
|
||||||
->withIDs(array($commit_id))
|
pht('Task data has no "commitPHID".'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$commit = id(clone $commit_query)
|
||||||
|
->withPHIDs(array($commit_phid))
|
||||||
->executeOne();
|
->executeOne();
|
||||||
if (!$commit) {
|
if (!$commit) {
|
||||||
throw new PhabricatorWorkerPermanentFailureException(
|
throw new PhabricatorWorkerPermanentFailureException(
|
||||||
pht('Commit "%s" does not exist.', $commit_id));
|
pht('Commit "%s" does not exist.', $commit_phid));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($commit->isUnreachable()) {
|
if ($commit->isUnreachable()) {
|
||||||
throw new PhabricatorWorkerPermanentFailureException(
|
throw new PhabricatorWorkerPermanentFailureException(
|
||||||
pht(
|
pht(
|
||||||
'Commit "%s" (with internal ID "%s") is no longer reachable from '.
|
'Commit "%s" (with PHID "%s") is no longer reachable from any '.
|
||||||
'any branch, tag, or ref in this repository, so it will not be '.
|
'branch, tag, or ref in this repository, so it will not be '.
|
||||||
'imported. This usually means that the branch the commit was on '.
|
'imported. This usually means that the branch the commit was on '.
|
||||||
'was deleted or overwritten.',
|
'was deleted or overwritten.',
|
||||||
$commit->getMonogram(),
|
$commit->getMonogram(),
|
||||||
$commit_id));
|
$commit_phid));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->commit = $commit;
|
$this->commit = $commit;
|
||||||
|
@ -51,10 +72,42 @@ abstract class PhabricatorRepositoryCommitParserWorker
|
||||||
$this->parseCommit($repository, $this->commit);
|
$this->parseCommit($repository, $this->commit);
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function shouldQueueFollowupTasks() {
|
private function shouldQueueFollowupTasks() {
|
||||||
return !idx($this->getTaskData(), 'only');
|
return !idx($this->getTaskData(), 'only');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final protected function queueCommitTask($task_class) {
|
||||||
|
if (!$this->shouldQueueFollowupTasks()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$commit = $this->loadCommit();
|
||||||
|
$repository = $commit->getRepository();
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'commitPHID' => $commit->getPHID(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$task_data = $this->getTaskData();
|
||||||
|
if (isset($task_data['via'])) {
|
||||||
|
$data['via'] = $task_data['via'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$options = array(
|
||||||
|
// We queue followup tasks at default priority so that the queue finishes
|
||||||
|
// work it has started before starting more work. If followups are queued
|
||||||
|
// at the same priority level, we do all message parses first, then all
|
||||||
|
// change parses, etc. This makes progress uneven. See T11677 for
|
||||||
|
// discussion.
|
||||||
|
'priority' => parent::PRIORITY_DEFAULT,
|
||||||
|
|
||||||
|
'objectPHID' => $commit->getPHID(),
|
||||||
|
'containerPHID' => $repository->getPHID(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->queueTask($task_class, $data, $options);
|
||||||
|
}
|
||||||
|
|
||||||
protected function getImportStepFlag() {
|
protected function getImportStepFlag() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -112,7 +165,7 @@ abstract class PhabricatorRepositoryCommitParserWorker
|
||||||
|
|
||||||
$commit = id(new DiffusionCommitQuery())
|
$commit = id(new DiffusionCommitQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withIDs(array(idx($this->getTaskData(), 'commitID')))
|
->withPHIDs(array(idx($this->getTaskData(), 'commitPHID')))
|
||||||
->executeOne();
|
->executeOne();
|
||||||
if (!$commit) {
|
if (!$commit) {
|
||||||
return $suffix;
|
return $suffix;
|
||||||
|
|
|
@ -396,6 +396,8 @@ final class PhabricatorChangeParserTestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSubversionParser() {
|
public function testSubversionParser() {
|
||||||
|
$this->requireBinaryForTest('svn');
|
||||||
|
|
||||||
$repository = $this->buildDiscoveredRepository('CHC');
|
$repository = $this->buildDiscoveredRepository('CHC');
|
||||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||||
|
|
||||||
|
@ -955,6 +957,8 @@ final class PhabricatorChangeParserTestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSubversionPartialParser() {
|
public function testSubversionPartialParser() {
|
||||||
|
$this->requireBinaryForTest('svn');
|
||||||
|
|
||||||
$repository = $this->buildBareRepository('CHD');
|
$repository = $this->buildBareRepository('CHD');
|
||||||
$repository->setDetail('svn-subpath', 'trunk/');
|
$repository->setDetail('svn-subpath', 'trunk/');
|
||||||
|
|
||||||
|
@ -1059,6 +1063,8 @@ final class PhabricatorChangeParserTestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSubversionValidRootParser() {
|
public function testSubversionValidRootParser() {
|
||||||
|
$this->requireBinaryForTest('svn');
|
||||||
|
|
||||||
// First, automatically configure the root correctly.
|
// First, automatically configure the root correctly.
|
||||||
$repository = $this->buildBareRepository('CHD');
|
$repository = $this->buildBareRepository('CHD');
|
||||||
id(new PhabricatorRepositoryPullEngine())
|
id(new PhabricatorRepositoryPullEngine())
|
||||||
|
@ -1104,6 +1110,8 @@ final class PhabricatorChangeParserTestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSubversionForeignStubsParser() {
|
public function testSubversionForeignStubsParser() {
|
||||||
|
$this->requireBinaryForTest('svn');
|
||||||
|
|
||||||
$repository = $this->buildBareRepository('CHE');
|
$repository = $this->buildBareRepository('CHE');
|
||||||
$repository->setDetail('svn-subpath', 'branch/');
|
$repository->setDetail('svn-subpath', 'branch/');
|
||||||
|
|
||||||
|
|
|
@ -99,14 +99,7 @@ abstract class PhabricatorRepositoryCommitChangeParserWorker
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function finishParse() {
|
protected function finishParse() {
|
||||||
$commit = $this->commit;
|
$this->queueCommitTask('PhabricatorRepositoryCommitPublishWorker');
|
||||||
if ($this->shouldQueueFollowupTasks()) {
|
|
||||||
$this->queueTask(
|
|
||||||
'PhabricatorRepositoryCommitPublishWorker',
|
|
||||||
array(
|
|
||||||
'commitID' => $commit->getID(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function writeCommitChanges(
|
private function writeCommitChanges(
|
||||||
|
|
|
@ -24,21 +24,7 @@ abstract class PhabricatorRepositoryCommitMessageParserWorker
|
||||||
$this->updateCommitData($commit, $data);
|
$this->updateCommitData($commit, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->shouldQueueFollowupTasks()) {
|
$this->queueCommitTask($this->getFollowupTaskClass());
|
||||||
$this->queueTask(
|
|
||||||
$this->getFollowupTaskClass(),
|
|
||||||
array(
|
|
||||||
'commitID' => $commit->getID(),
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
// We queue followup tasks at default priority so that the queue
|
|
||||||
// finishes work it has started before starting more work. If
|
|
||||||
// followups are queued at the same priority level, we do all
|
|
||||||
// message parses first, then all change parses, etc. This makes
|
|
||||||
// progress uneven. See T11677 for discussion.
|
|
||||||
'priority' => PhabricatorWorker::PRIORITY_DEFAULT,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function updateCommitData(
|
final protected function updateCommitData(
|
||||||
|
|
|
@ -1389,7 +1389,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
return $xactions;
|
return $xactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function queuePublishing() {
|
private function queuePublishing() {
|
||||||
$object = $this->publishableObject;
|
$object = $this->publishableObject;
|
||||||
$xactions = $this->publishableTransactions;
|
$xactions = $this->publishableTransactions;
|
||||||
|
|
||||||
|
@ -4207,7 +4207,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
* @return dict<string, wild> Serializable editor state.
|
* @return dict<string, wild> Serializable editor state.
|
||||||
* @task workers
|
* @task workers
|
||||||
*/
|
*/
|
||||||
final private function getWorkerState() {
|
private function getWorkerState() {
|
||||||
$state = array();
|
$state = array();
|
||||||
foreach ($this->getAutomaticStateProperties() as $property) {
|
foreach ($this->getAutomaticStateProperties() as $property) {
|
||||||
$state[$property] = $this->$property;
|
$state[$property] = $this->$property;
|
||||||
|
@ -4336,7 +4336,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
* @return map<string, wild> Map of encoded values.
|
* @return map<string, wild> Map of encoded values.
|
||||||
* @task workers
|
* @task workers
|
||||||
*/
|
*/
|
||||||
final private function encodeStateForStorage(
|
private function encodeStateForStorage(
|
||||||
array $state,
|
array $state,
|
||||||
array $encodings) {
|
array $encodings) {
|
||||||
|
|
||||||
|
@ -4382,7 +4382,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
* @return map<string, wild> Map of decoded values.
|
* @return map<string, wild> Map of decoded values.
|
||||||
* @task workers
|
* @task workers
|
||||||
*/
|
*/
|
||||||
final private function decodeStateFromStorage(
|
private function decodeStateFromStorage(
|
||||||
array $state,
|
array $state,
|
||||||
array $encodings) {
|
array $encodings) {
|
||||||
|
|
||||||
|
|
83
src/docs/user/field/worker_queue.diviner
Normal file
83
src/docs/user/field/worker_queue.diviner
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
@title Managing the Worker Queue
|
||||||
|
@group fieldmanual
|
||||||
|
|
||||||
|
Advanced guide to managing the background worker task queue.
|
||||||
|
|
||||||
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
|
Phabricator uses daemonized worker processes to execute some tasks (like
|
||||||
|
importing repositories and sending mail) in the background.
|
||||||
|
|
||||||
|
In most cases, this queue will automatically execute tasks in an appropriate
|
||||||
|
order. However, in some cases you may want to exercise greater control over
|
||||||
|
which tasks execute, when, and at what priority.
|
||||||
|
|
||||||
|
Reference: Priority Levels
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Tasks queued by Phabricator use these default priority levels:
|
||||||
|
|
||||||
|
| Priority | Name | Tasks |
|
||||||
|
|---|---|---|
|
||||||
|
| 1000 | `ALERTS` | Time-sensitive notifications and email. |
|
||||||
|
| 2000 | `DEFAULT` | Normal publishing and processing. |
|
||||||
|
| 2500 | `COMMIT` | Import of commits in existing repositories. |
|
||||||
|
| 3000 | `BULK` | Edits applied via "Bulk Edit" interface. |
|
||||||
|
| 3500 | `INDEX` | Search engine index updates. |
|
||||||
|
| 4000 | `IMPORT` | Import of commits in new repositories. |
|
||||||
|
|
||||||
|
Tasks with smaller priority numbers execute before tasks with larger priority
|
||||||
|
numbers (for example, a task with priority 1000 will execute before a task
|
||||||
|
with priority 2000).
|
||||||
|
|
||||||
|
Any positive integer is a valid priority level, and if you adjust the priority
|
||||||
|
of tasks with `bin/worker priority` you may select any level even if
|
||||||
|
Phabricator would never naturally queue tasks at that level. For example, you
|
||||||
|
may adjust tasks to priority `5678`, which will make them execute after all
|
||||||
|
other types of natural tasks.
|
||||||
|
|
||||||
|
Although tasks usually execute in priority order, task execution order is not
|
||||||
|
strictly a function of priority, and task priority does not guarantee execution
|
||||||
|
order.
|
||||||
|
|
||||||
|
Large Repository Imports
|
||||||
|
========================
|
||||||
|
|
||||||
|
The most common case where you may want to make an adjustment to the default
|
||||||
|
behavior of the worker queue is when importing a very large repository like
|
||||||
|
the Linux kernel.
|
||||||
|
|
||||||
|
Although Phabricator will automatically process imports of new repositories at
|
||||||
|
a lower priority level than all other non-import tasks, you may still run into
|
||||||
|
issues like these:
|
||||||
|
|
||||||
|
- You may also want to import one or more //other// new repositories, and
|
||||||
|
would prefer they import at a higher priority.
|
||||||
|
- You may find overall repository performance is impacted by the large
|
||||||
|
repository import.
|
||||||
|
|
||||||
|
You can manually change the priority of tasks with `bin/worker priority`. For
|
||||||
|
example, if your copy of the Linux repository is `R123` and you'd like it to
|
||||||
|
import at a lower priority than all other tasks (including other imports of
|
||||||
|
new repositories), you can run a command like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
phabricator/ $ ./bin/worker priority --priority 5000 --container R123
|
||||||
|
```
|
||||||
|
|
||||||
|
This means: set all tasks associated with container `R123` (in this example,
|
||||||
|
the Linux repository) to priority 5000 (which is lower than any natural
|
||||||
|
priority).
|
||||||
|
|
||||||
|
You can delay tasks until later with `bin/worker delay`, which allows you to
|
||||||
|
schedule tasks to execute at night or over the weekend. For example, to
|
||||||
|
pause an import for 6 hours, run a command like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
phabricator/ $ ./bin/worker delay --until "6 hours" --container R123
|
||||||
|
```
|
||||||
|
|
||||||
|
The selected tasks will not execute until 6 hours from the time this command
|
||||||
|
is issued. You can also provide an explicit date, or "now" to let tasks begin
|
||||||
|
execution immediately.
|
|
@ -134,6 +134,7 @@ abstract class PhabricatorWorker extends Phobject {
|
||||||
array(
|
array(
|
||||||
'priority' => 'optional int|null',
|
'priority' => 'optional int|null',
|
||||||
'objectPHID' => 'optional string|null',
|
'objectPHID' => 'optional string|null',
|
||||||
|
'containerPHID' => 'optional string|null',
|
||||||
'delayUntil' => 'optional int|null',
|
'delayUntil' => 'optional int|null',
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -142,12 +143,14 @@ abstract class PhabricatorWorker extends Phobject {
|
||||||
$priority = self::PRIORITY_DEFAULT;
|
$priority = self::PRIORITY_DEFAULT;
|
||||||
}
|
}
|
||||||
$object_phid = idx($options, 'objectPHID');
|
$object_phid = idx($options, 'objectPHID');
|
||||||
|
$container_phid = idx($options, 'containerPHID');
|
||||||
|
|
||||||
$task = id(new PhabricatorWorkerActiveTask())
|
$task = id(new PhabricatorWorkerActiveTask())
|
||||||
->setTaskClass($task_class)
|
->setTaskClass($task_class)
|
||||||
->setData($data)
|
->setData($data)
|
||||||
->setPriority($priority)
|
->setPriority($priority)
|
||||||
->setObjectPHID($object_phid);
|
->setObjectPHID($object_phid)
|
||||||
|
->setContainerPHID($container_phid);
|
||||||
|
|
||||||
$delay = idx($options, 'delayUntil');
|
$delay = idx($options, 'delayUntil');
|
||||||
if ($delay) {
|
if ($delay) {
|
||||||
|
|
|
@ -6,7 +6,7 @@ final class PhabricatorWorkerManagementCancelWorkflow
|
||||||
protected function didConstruct() {
|
protected function didConstruct() {
|
||||||
$this
|
$this
|
||||||
->setName('cancel')
|
->setName('cancel')
|
||||||
->setExamples('**cancel** --id __id__')
|
->setExamples('**cancel** __selectors__')
|
||||||
->setSynopsis(
|
->setSynopsis(
|
||||||
pht(
|
pht(
|
||||||
'Cancel selected tasks. The work these tasks represent will never '.
|
'Cancel selected tasks. The work these tasks represent will never '.
|
||||||
|
@ -15,14 +15,21 @@ final class PhabricatorWorkerManagementCancelWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(PhutilArgumentParser $args) {
|
public function execute(PhutilArgumentParser $args) {
|
||||||
$console = PhutilConsole::getConsole();
|
|
||||||
$tasks = $this->loadTasks($args);
|
$tasks = $this->loadTasks($args);
|
||||||
|
|
||||||
|
if (!$tasks) {
|
||||||
|
$this->logWarn(
|
||||||
|
pht('NO TASKS'),
|
||||||
|
pht('No tasks selected to cancel.'));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cancel_count = 0;
|
||||||
foreach ($tasks as $task) {
|
foreach ($tasks as $task) {
|
||||||
$can_cancel = !$task->isArchived();
|
$can_cancel = !$task->isArchived();
|
||||||
if (!$can_cancel) {
|
if (!$can_cancel) {
|
||||||
$console->writeOut(
|
$this->logWarn(
|
||||||
"**<bg:yellow> %s </bg>** %s\n",
|
|
||||||
pht('ARCHIVED'),
|
pht('ARCHIVED'),
|
||||||
pht(
|
pht(
|
||||||
'%s is already archived, and can not be cancelled.',
|
'%s is already archived, and can not be cancelled.',
|
||||||
|
@ -32,20 +39,25 @@ final class PhabricatorWorkerManagementCancelWorkflow
|
||||||
|
|
||||||
// Forcibly break the lease if one exists, so we can archive the
|
// Forcibly break the lease if one exists, so we can archive the
|
||||||
// task.
|
// task.
|
||||||
$task->setLeaseOwner(null);
|
$task
|
||||||
$task->setLeaseExpires(PhabricatorTime::getNow());
|
->setLeaseOwner(null)
|
||||||
$task->archiveTask(
|
->setLeaseExpires(PhabricatorTime::getNow());
|
||||||
PhabricatorWorkerArchiveTask::RESULT_CANCELLED,
|
|
||||||
0);
|
|
||||||
|
|
||||||
$console->writeOut(
|
$task->archiveTask(PhabricatorWorkerArchiveTask::RESULT_CANCELLED, 0);
|
||||||
"**<bg:green> %s </bg>** %s\n",
|
|
||||||
|
$this->logInfo(
|
||||||
pht('CANCELLED'),
|
pht('CANCELLED'),
|
||||||
pht(
|
pht(
|
||||||
'%s was cancelled.',
|
'%s was cancelled.',
|
||||||
$this->describeTask($task)));
|
$this->describeTask($task)));
|
||||||
|
|
||||||
|
$cancel_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->logOkay(
|
||||||
|
pht('DONE'),
|
||||||
|
pht('Cancelled %s task(s).', new PhutilNumber($cancel_count)));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorWorkerManagementDelayWorkflow
|
||||||
|
extends PhabricatorWorkerManagementWorkflow {
|
||||||
|
|
||||||
|
protected function didConstruct() {
|
||||||
|
$this
|
||||||
|
->setName('delay')
|
||||||
|
->setExamples(
|
||||||
|
implode(
|
||||||
|
"\n",
|
||||||
|
array(
|
||||||
|
'**delay** __selectors__ --until __date__',
|
||||||
|
'**delay** __selectors__ --until __YYYY-MM-DD__',
|
||||||
|
'**delay** __selectors__ --until "6 hours"',
|
||||||
|
'**delay** __selectors__ --until now',
|
||||||
|
)))
|
||||||
|
->setSynopsis(
|
||||||
|
pht(
|
||||||
|
'Delay execution of selected tasks until the specified time.'))
|
||||||
|
->setArguments(
|
||||||
|
array_merge(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'name' => 'until',
|
||||||
|
'param' => 'date',
|
||||||
|
'help' => pht(
|
||||||
|
'Select the date or time to delay the selected tasks until.'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
$this->getTaskSelectionArguments()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(PhutilArgumentParser $args) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$until = $args->getArg('until');
|
||||||
|
$until = $this->parseTimeArgument($until);
|
||||||
|
|
||||||
|
if ($until === null) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Specify how long to delay tasks for with "--until".'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$tasks = $this->loadTasks($args);
|
||||||
|
|
||||||
|
if (!$tasks) {
|
||||||
|
$this->logWarn(
|
||||||
|
pht('NO TASKS'),
|
||||||
|
pht('No tasks selected to delay.'));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$delay_count = 0;
|
||||||
|
foreach ($tasks as $task) {
|
||||||
|
if ($task->isArchived()) {
|
||||||
|
$this->logWarn(
|
||||||
|
pht('ARCHIVED'),
|
||||||
|
pht(
|
||||||
|
'%s is already archived, and can not be delayed.',
|
||||||
|
$this->describeTask($task)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($task->getLeaseOwner()) {
|
||||||
|
$this->logWarn(
|
||||||
|
pht('LEASED'),
|
||||||
|
pht(
|
||||||
|
'% is already leased, and can not be delayed.',
|
||||||
|
$this->describeTask($task)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$task
|
||||||
|
->setLeaseExpires($until)
|
||||||
|
->save();
|
||||||
|
|
||||||
|
$this->logInfo(
|
||||||
|
pht('DELAY'),
|
||||||
|
pht(
|
||||||
|
'%s was delayed until "%s".',
|
||||||
|
$this->describeTask($task),
|
||||||
|
phabricator_datetime($until, $viewer)));
|
||||||
|
|
||||||
|
$delay_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logOkay(
|
||||||
|
pht('DONE'),
|
||||||
|
pht('Delayed %s task(s).', new PhutilNumber($delay_count)));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ final class PhabricatorWorkerManagementExecuteWorkflow
|
||||||
protected function didConstruct() {
|
protected function didConstruct() {
|
||||||
$this
|
$this
|
||||||
->setName('execute')
|
->setName('execute')
|
||||||
->setExamples('**execute** --id __id__')
|
->setExamples('**execute** __selectors__')
|
||||||
->setSynopsis(
|
->setSynopsis(
|
||||||
pht(
|
pht(
|
||||||
'Execute a task explicitly. This command ignores leases, is '.
|
'Execute a task explicitly. This command ignores leases, is '.
|
||||||
|
@ -27,18 +27,24 @@ final class PhabricatorWorkerManagementExecuteWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(PhutilArgumentParser $args) {
|
public function execute(PhutilArgumentParser $args) {
|
||||||
$console = PhutilConsole::getConsole();
|
|
||||||
$tasks = $this->loadTasks($args);
|
|
||||||
|
|
||||||
$is_retry = $args->getArg('retry');
|
$is_retry = $args->getArg('retry');
|
||||||
$is_repeat = $args->getArg('repeat');
|
$is_repeat = $args->getArg('repeat');
|
||||||
|
|
||||||
|
$tasks = $this->loadTasks($args);
|
||||||
|
if (!$tasks) {
|
||||||
|
$this->logWarn(
|
||||||
|
pht('NO TASKS'),
|
||||||
|
pht('No tasks selected to execute.'));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$execute_count = 0;
|
||||||
foreach ($tasks as $task) {
|
foreach ($tasks as $task) {
|
||||||
$can_execute = !$task->isArchived();
|
$can_execute = !$task->isArchived();
|
||||||
if (!$can_execute) {
|
if (!$can_execute) {
|
||||||
if (!$is_retry) {
|
if (!$is_retry) {
|
||||||
$console->writeOut(
|
$this->logWarn(
|
||||||
"**<bg:yellow> %s </bg>** %s\n",
|
|
||||||
pht('ARCHIVED'),
|
pht('ARCHIVED'),
|
||||||
pht(
|
pht(
|
||||||
'%s is already archived, and will not be executed. '.
|
'%s is already archived, and will not be executed. '.
|
||||||
|
@ -50,8 +56,7 @@ final class PhabricatorWorkerManagementExecuteWorkflow
|
||||||
$result_success = PhabricatorWorkerArchiveTask::RESULT_SUCCESS;
|
$result_success = PhabricatorWorkerArchiveTask::RESULT_SUCCESS;
|
||||||
if ($task->getResult() == $result_success) {
|
if ($task->getResult() == $result_success) {
|
||||||
if (!$is_repeat) {
|
if (!$is_repeat) {
|
||||||
$console->writeOut(
|
$this->logWarn(
|
||||||
"**<bg:yellow> %s </bg>** %s\n",
|
|
||||||
pht('SUCCEEDED'),
|
pht('SUCCEEDED'),
|
||||||
pht(
|
pht(
|
||||||
'%s has already succeeded, and will not be retried. '.
|
'%s has already succeeded, and will not be retried. '.
|
||||||
|
@ -61,9 +66,8 @@ final class PhabricatorWorkerManagementExecuteWorkflow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
echo tsprintf(
|
$this->logInfo(
|
||||||
"**<bg:yellow> %s </bg>** %s\n",
|
pht('UNARCHIVING'),
|
||||||
pht('ARCHIVED'),
|
|
||||||
pht(
|
pht(
|
||||||
'Unarchiving %s.',
|
'Unarchiving %s.',
|
||||||
$this->describeTask($task)));
|
$this->describeTask($task)));
|
||||||
|
@ -74,30 +78,36 @@ final class PhabricatorWorkerManagementExecuteWorkflow
|
||||||
// NOTE: This ignores leases, maybe it should respect them without
|
// NOTE: This ignores leases, maybe it should respect them without
|
||||||
// a parameter like --force?
|
// a parameter like --force?
|
||||||
|
|
||||||
$task->setLeaseOwner(null);
|
$task
|
||||||
$task->setLeaseExpires(PhabricatorTime::getNow());
|
->setLeaseOwner(null)
|
||||||
$task->save();
|
->setLeaseExpires(PhabricatorTime::getNow())
|
||||||
|
->save();
|
||||||
|
|
||||||
$task_data = id(new PhabricatorWorkerTaskData())->loadOneWhere(
|
$task_data = id(new PhabricatorWorkerTaskData())->loadOneWhere(
|
||||||
'id = %d',
|
'id = %d',
|
||||||
$task->getDataID());
|
$task->getDataID());
|
||||||
$task->setData($task_data->getData());
|
$task->setData($task_data->getData());
|
||||||
|
|
||||||
echo tsprintf(
|
$this->logInfo(
|
||||||
"%s\n",
|
pht('EXECUTE'),
|
||||||
pht(
|
pht(
|
||||||
'Executing task %d (%s)...',
|
'Executing %s...',
|
||||||
$task->getID(),
|
$this->describeTask($task)));
|
||||||
$task->getTaskClass()));
|
|
||||||
|
|
||||||
$task = $task->executeTask();
|
$task = $task->executeTask();
|
||||||
$ex = $task->getExecutionException();
|
|
||||||
|
|
||||||
|
$ex = $task->getExecutionException();
|
||||||
if ($ex) {
|
if ($ex) {
|
||||||
throw $ex;
|
throw $ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$execute_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->logOkay(
|
||||||
|
pht('DONE'),
|
||||||
|
pht('Executed %s task(s).', new PhutilNumber($execute_count)));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ final class PhabricatorWorkerManagementFreeWorkflow
|
||||||
protected function didConstruct() {
|
protected function didConstruct() {
|
||||||
$this
|
$this
|
||||||
->setName('free')
|
->setName('free')
|
||||||
->setExamples('**free** --id __id__')
|
->setExamples('**free** __selectors__')
|
||||||
->setSynopsis(
|
->setSynopsis(
|
||||||
pht(
|
pht(
|
||||||
'Free leases on selected tasks. If the daemon holding the lease is '.
|
'Free leases on selected tasks. If the daemon holding the lease is '.
|
||||||
|
@ -16,13 +16,20 @@ final class PhabricatorWorkerManagementFreeWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(PhutilArgumentParser $args) {
|
public function execute(PhutilArgumentParser $args) {
|
||||||
$console = PhutilConsole::getConsole();
|
|
||||||
$tasks = $this->loadTasks($args);
|
$tasks = $this->loadTasks($args);
|
||||||
|
|
||||||
|
if (!$tasks) {
|
||||||
|
$this->logWarn(
|
||||||
|
pht('NO TASKS'),
|
||||||
|
pht('No tasks selected to free leases on.'));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$free_count = 0;
|
||||||
foreach ($tasks as $task) {
|
foreach ($tasks as $task) {
|
||||||
if ($task->isArchived()) {
|
if ($task->isArchived()) {
|
||||||
$console->writeOut(
|
$this->logWarn(
|
||||||
"**<bg:yellow> %s </bg>** %s\n",
|
|
||||||
pht('ARCHIVED'),
|
pht('ARCHIVED'),
|
||||||
pht(
|
pht(
|
||||||
'%s is archived; archived tasks do not have leases.',
|
'%s is archived; archived tasks do not have leases.',
|
||||||
|
@ -31,8 +38,7 @@ final class PhabricatorWorkerManagementFreeWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($task->getLeaseOwner() === null) {
|
if ($task->getLeaseOwner() === null) {
|
||||||
$console->writeOut(
|
$this->logWarn(
|
||||||
"**<bg:yellow> %s </bg>** %s\n",
|
|
||||||
pht('FREE'),
|
pht('FREE'),
|
||||||
pht(
|
pht(
|
||||||
'%s has no active lease.',
|
'%s has no active lease.',
|
||||||
|
@ -40,18 +46,24 @@ final class PhabricatorWorkerManagementFreeWorkflow
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$task->setLeaseOwner(null);
|
$task
|
||||||
$task->setLeaseExpires(PhabricatorTime::getNow());
|
->setLeaseOwner(null)
|
||||||
$task->save();
|
->setLeaseExpires(PhabricatorTime::getNow())
|
||||||
|
->save();
|
||||||
|
|
||||||
$console->writeOut(
|
$this->logInfo(
|
||||||
"**<bg:green> %s </bg>** %s\n",
|
|
||||||
pht('LEASE FREED'),
|
pht('LEASE FREED'),
|
||||||
pht(
|
pht(
|
||||||
'%s was freed from its lease.',
|
'%s was freed from its lease.',
|
||||||
$this->describeTask($task)));
|
$this->describeTask($task)));
|
||||||
|
|
||||||
|
$free_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->logOkay(
|
||||||
|
pht('DONE'),
|
||||||
|
pht('Freed %s task lease(s).', new PhutilNumber($free_count)));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorWorkerManagementPriorityWorkflow
|
||||||
|
extends PhabricatorWorkerManagementWorkflow {
|
||||||
|
|
||||||
|
protected function didConstruct() {
|
||||||
|
$this
|
||||||
|
->setName('priority')
|
||||||
|
->setExamples('**priority** __selectors__ --priority __value__')
|
||||||
|
->setSynopsis(
|
||||||
|
pht(
|
||||||
|
'Change the priority of selected tasks, causing them to execute '.
|
||||||
|
'before or after other tasks.'))
|
||||||
|
->setArguments(
|
||||||
|
array_merge(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'name' => 'priority',
|
||||||
|
'param' => 'int',
|
||||||
|
'help' => pht(
|
||||||
|
'Set tasks to this priority. Tasks with a smaller priority '.
|
||||||
|
'value execute before tasks with a larger priority value.'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
$this->getTaskSelectionArguments()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(PhutilArgumentParser $args) {
|
||||||
|
$new_priority = $args->getArg('priority');
|
||||||
|
|
||||||
|
if ($new_priority === null) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Select a new priority for selected tasks with "--priority".'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_priority = (int)$new_priority;
|
||||||
|
if ($new_priority <= 0) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Priority must be a positive integer.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$tasks = $this->loadTasks($args);
|
||||||
|
|
||||||
|
if (!$tasks) {
|
||||||
|
$this->logWarn(
|
||||||
|
pht('NO TASKS'),
|
||||||
|
pht('No tasks selected to reprioritize.'));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$priority_count = 0;
|
||||||
|
foreach ($tasks as $task) {
|
||||||
|
$can_reprioritize = !$task->isArchived();
|
||||||
|
if (!$can_reprioritize) {
|
||||||
|
$this->logWarn(
|
||||||
|
pht('ARCHIVED'),
|
||||||
|
pht(
|
||||||
|
'%s is already archived, and can not be reprioritized.',
|
||||||
|
$this->describeTask($task)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$old_priority = (int)$task->getPriority();
|
||||||
|
|
||||||
|
if ($old_priority === $new_priority) {
|
||||||
|
$this->logWarn(
|
||||||
|
pht('UNCHANGED'),
|
||||||
|
pht(
|
||||||
|
'%s already has priority "%s".',
|
||||||
|
$this->describeTask($task),
|
||||||
|
$new_priority));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$task
|
||||||
|
->setPriority($new_priority)
|
||||||
|
->save();
|
||||||
|
|
||||||
|
$this->logInfo(
|
||||||
|
pht('PRIORITY'),
|
||||||
|
pht(
|
||||||
|
'%s was reprioritized (from "%d" to "%d").',
|
||||||
|
$this->describeTask($task),
|
||||||
|
$old_priority,
|
||||||
|
$new_priority));
|
||||||
|
|
||||||
|
$priority_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logOkay(
|
||||||
|
pht('DONE'),
|
||||||
|
pht('Reprioritized %s task(s).', new PhutilNumber($priority_count)));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ final class PhabricatorWorkerManagementRetryWorkflow
|
||||||
protected function didConstruct() {
|
protected function didConstruct() {
|
||||||
$this
|
$this
|
||||||
->setName('retry')
|
->setName('retry')
|
||||||
->setExamples('**retry** --id __id__')
|
->setExamples('**retry** __selectors__')
|
||||||
->setSynopsis(
|
->setSynopsis(
|
||||||
pht(
|
pht(
|
||||||
'Retry selected tasks which previously failed permanently or '.
|
'Retry selected tasks which previously failed permanently or '.
|
||||||
|
@ -24,14 +24,21 @@ final class PhabricatorWorkerManagementRetryWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(PhutilArgumentParser $args) {
|
public function execute(PhutilArgumentParser $args) {
|
||||||
$console = PhutilConsole::getConsole();
|
|
||||||
$tasks = $this->loadTasks($args);
|
|
||||||
|
|
||||||
$is_repeat = $args->getArg('repeat');
|
$is_repeat = $args->getArg('repeat');
|
||||||
|
|
||||||
|
$tasks = $this->loadTasks($args);
|
||||||
|
if (!$tasks) {
|
||||||
|
$this->logWarn(
|
||||||
|
pht('NO TASKS'),
|
||||||
|
pht('No tasks selected to retry.'));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$retry_count = 0;
|
||||||
foreach ($tasks as $task) {
|
foreach ($tasks as $task) {
|
||||||
if (!$task->isArchived()) {
|
if (!$task->isArchived()) {
|
||||||
$console->writeOut(
|
$this->logWarn(
|
||||||
"**<bg:yellow> %s </bg>** %s\n",
|
|
||||||
pht('ACTIVE'),
|
pht('ACTIVE'),
|
||||||
pht(
|
pht(
|
||||||
'%s is already in the active task queue.',
|
'%s is already in the active task queue.',
|
||||||
|
@ -42,8 +49,7 @@ final class PhabricatorWorkerManagementRetryWorkflow
|
||||||
$result_success = PhabricatorWorkerArchiveTask::RESULT_SUCCESS;
|
$result_success = PhabricatorWorkerArchiveTask::RESULT_SUCCESS;
|
||||||
if ($task->getResult() == $result_success) {
|
if ($task->getResult() == $result_success) {
|
||||||
if (!$is_repeat) {
|
if (!$is_repeat) {
|
||||||
$console->writeOut(
|
$this->logWarn(
|
||||||
"**<bg:yellow> %s </bg>** %s\n",
|
|
||||||
pht('SUCCEEDED'),
|
pht('SUCCEEDED'),
|
||||||
pht(
|
pht(
|
||||||
'%s has already succeeded, and will not be repeated. '.
|
'%s has already succeeded, and will not be repeated. '.
|
||||||
|
@ -55,14 +61,19 @@ final class PhabricatorWorkerManagementRetryWorkflow
|
||||||
|
|
||||||
$task->unarchiveTask();
|
$task->unarchiveTask();
|
||||||
|
|
||||||
$console->writeOut(
|
$this->logInfo(
|
||||||
"**<bg:green> %s </bg>** %s\n",
|
|
||||||
pht('QUEUED'),
|
pht('QUEUED'),
|
||||||
pht(
|
pht(
|
||||||
'%s was queued for retry.',
|
'%s was queued for retry.',
|
||||||
$this->describeTask($task)));
|
$this->describeTask($task)));
|
||||||
|
|
||||||
|
$retry_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->logOkay(
|
||||||
|
pht('DONE'),
|
||||||
|
pht('Queued %s task(s) for retry.', new PhutilNumber($retry_count)));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,16 +14,54 @@ abstract class PhabricatorWorkerManagementWorkflow
|
||||||
array(
|
array(
|
||||||
'name' => 'class',
|
'name' => 'class',
|
||||||
'param' => 'name',
|
'param' => 'name',
|
||||||
'help' => pht('Select all tasks of a given class.'),
|
'help' => pht('Select tasks of a given class.'),
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'name' => 'min-failure-count',
|
'name' => 'min-failure-count',
|
||||||
'param' => 'int',
|
'param' => 'int',
|
||||||
'help' => pht('Limit to tasks with at least this many failures.'),
|
'help' => pht('Select tasks with a minimum failure count.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'max-failure-count',
|
||||||
|
'param' => 'int',
|
||||||
|
'help' => pht('Select tasks with a maximum failure count.'),
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'name' => 'active',
|
'name' => 'active',
|
||||||
'help' => pht('Select all active tasks.'),
|
'help' => pht('Select active tasks.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'archived',
|
||||||
|
'help' => pht('Select archived tasks.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'container',
|
||||||
|
'param' => 'name',
|
||||||
|
'help' => pht(
|
||||||
|
'Select tasks with the given container or containers.'),
|
||||||
|
'repeat' => true,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'object',
|
||||||
|
'param' => 'name',
|
||||||
|
'repeat' => true,
|
||||||
|
'help' => pht(
|
||||||
|
'Select tasks affecting the given object or objects.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'min-priority',
|
||||||
|
'param' => 'int',
|
||||||
|
'help' => pht('Select tasks with a minimum priority.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'max-priority',
|
||||||
|
'param' => 'int',
|
||||||
|
'help' => pht('Select tasks with a maximum priority.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'limit',
|
||||||
|
'param' => 'int',
|
||||||
|
'help' => pht('Limit selection to a maximum number of tasks.'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -31,14 +69,92 @@ abstract class PhabricatorWorkerManagementWorkflow
|
||||||
protected function loadTasks(PhutilArgumentParser $args) {
|
protected function loadTasks(PhutilArgumentParser $args) {
|
||||||
$ids = $args->getArg('id');
|
$ids = $args->getArg('id');
|
||||||
$class = $args->getArg('class');
|
$class = $args->getArg('class');
|
||||||
$min_failures = $args->getArg('min-failure-count');
|
|
||||||
$active = $args->getArg('active');
|
$active = $args->getArg('active');
|
||||||
|
$archived = $args->getArg('archived');
|
||||||
|
|
||||||
if (!$ids && !$class && !$min_failures && !$active) {
|
$container_names = $args->getArg('container');
|
||||||
|
$object_names = $args->getArg('object');
|
||||||
|
|
||||||
|
$min_failures = $args->getArg('min-failure-count');
|
||||||
|
$max_failures = $args->getArg('max-failure-count');
|
||||||
|
|
||||||
|
$min_priority = $args->getArg('min-priority');
|
||||||
|
$max_priority = $args->getArg('max-priority');
|
||||||
|
|
||||||
|
$limit = $args->getArg('limit');
|
||||||
|
|
||||||
|
$any_constraints = false;
|
||||||
|
if ($ids) {
|
||||||
|
$any_constraints = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($class) {
|
||||||
|
$any_constraints = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($active || $archived) {
|
||||||
|
$any_constraints = true;
|
||||||
|
if ($active && $archived) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'You can not specify both "--active" and "--archived" tasks: '.
|
||||||
|
'no tasks can match both constraints.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($container_names) {
|
||||||
|
$any_constraints = true;
|
||||||
|
$container_phids = $this->loadObjectPHIDsFromArguments($container_names);
|
||||||
|
} else {
|
||||||
|
$container_phids = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($object_names) {
|
||||||
|
$any_constraints = true;
|
||||||
|
$object_phids = $this->loadObjectPHIDsFromArguments($object_names);
|
||||||
|
} else {
|
||||||
|
$object_phids = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($min_failures !== null) || ($max_failures !== null)) {
|
||||||
|
$any_constraints = true;
|
||||||
|
if (($min_failures !== null) && ($max_failures !== null)) {
|
||||||
|
if ($min_failures > $max_failures) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Specified "--min-failures" must not be larger than '.
|
||||||
|
'specified "--max-failures".'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($min_priority !== null) || ($max_priority !== null)) {
|
||||||
|
$any_constraints = true;
|
||||||
|
if (($min_priority !== null) && ($max_priority !== null)) {
|
||||||
|
if ($min_priority > $max_priority) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Specified "--min-priority" may not be larger than '.
|
||||||
|
'specified "--max-priority".'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$any_constraints) {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
'Use "--id", "--class", "--active", and/or "--min-failure-count" '.
|
'Use constraint flags (like "--id" or "--class") to select which '.
|
||||||
'to select tasks.'));
|
'tasks to affect. Use "--help" for a list of supported constraint '.
|
||||||
|
'flags.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($limit !== null) {
|
||||||
|
$limit = (int)$limit;
|
||||||
|
if ($limit <= 0) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Specified "--limit" must be a positive integer.'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$active_query = new PhabricatorWorkerActiveTaskQuery();
|
$active_query = new PhabricatorWorkerActiveTaskQuery();
|
||||||
|
@ -56,13 +172,35 @@ abstract class PhabricatorWorkerManagementWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($min_failures) {
|
if ($min_failures) {
|
||||||
$active_query = $active_query->withFailureCountBetween(
|
$active_query->withFailureCountBetween($min_failures, $max_failures);
|
||||||
$min_failures, null);
|
$archive_query->withFailureCountBetween($min_failures, $max_failures);
|
||||||
$archive_query = $archive_query->withFailureCountBetween(
|
|
||||||
$min_failures, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$active_tasks = $active_query->execute();
|
if ($container_phids) {
|
||||||
|
$active_query->withContainerPHIDs($container_phids);
|
||||||
|
$archive_query->withContainerPHIDs($container_phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($object_phids) {
|
||||||
|
$active_query->withObjectPHIDs($object_phids);
|
||||||
|
$archive_query->withObjectPHIDs($object_phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($min_priority || $max_priority) {
|
||||||
|
$active_query->withPriorityBetween($min_priority, $max_priority);
|
||||||
|
$archive_query->withPriorityBetween($min_priority, $max_priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($limit) {
|
||||||
|
$active_query->setLimit($limit);
|
||||||
|
$archive_query->setLimit($limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($archived) {
|
||||||
|
$active_tasks = array();
|
||||||
|
} else {
|
||||||
|
$active_tasks = $active_query->execute();
|
||||||
|
}
|
||||||
|
|
||||||
if ($active) {
|
if ($active) {
|
||||||
$archive_tasks = array();
|
$archive_tasks = array();
|
||||||
|
@ -74,32 +212,22 @@ abstract class PhabricatorWorkerManagementWorkflow
|
||||||
mpull($active_tasks, null, 'getID') +
|
mpull($active_tasks, null, 'getID') +
|
||||||
mpull($archive_tasks, null, 'getID');
|
mpull($archive_tasks, null, 'getID');
|
||||||
|
|
||||||
|
if ($limit) {
|
||||||
|
$tasks = array_slice($tasks, 0, $limit, $preserve_keys = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if ($ids) {
|
if ($ids) {
|
||||||
foreach ($ids as $id) {
|
foreach ($ids as $id) {
|
||||||
if (empty($tasks[$id])) {
|
if (empty($tasks[$id])) {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht('No task exists with id "%s"!', $id));
|
pht('No task with ID "%s" matches the constraints!', $id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($class && $min_failures) {
|
|
||||||
if (!$tasks) {
|
// We check that IDs are valid, but for all other constraints it is
|
||||||
throw new PhutilArgumentUsageException(
|
// acceptable to select no tasks to act upon.
|
||||||
pht('No task exists with class "%s" and at least %d failures!',
|
|
||||||
$class,
|
|
||||||
$min_failures));
|
|
||||||
}
|
|
||||||
} else if ($class) {
|
|
||||||
if (!$tasks) {
|
|
||||||
throw new PhutilArgumentUsageException(
|
|
||||||
pht('No task exists with class "%s"!', $class));
|
|
||||||
}
|
|
||||||
} else if ($min_failures) {
|
|
||||||
if (!$tasks) {
|
|
||||||
throw new PhutilArgumentUsageException(
|
|
||||||
pht('No tasks exist with at least %d failures!', $min_failures));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When we lock tasks properly, this gets populated as a side effect. Just
|
// When we lock tasks properly, this gets populated as a side effect. Just
|
||||||
// fake it when doing manual CLI stuff. This makes sure CLI yields have
|
// fake it when doing manual CLI stuff. This makes sure CLI yields have
|
||||||
|
@ -110,6 +238,20 @@ abstract class PhabricatorWorkerManagementWorkflow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the user specified one or more "--id" flags, process the tasks in
|
||||||
|
// the given order. Otherwise, process them in FIFO order so the sequence
|
||||||
|
// is somewhat consistent with natural execution order.
|
||||||
|
|
||||||
|
// NOTE: When "--limit" is used, we end up selecting the newest tasks
|
||||||
|
// first. At time of writing, there's no way to order the queries
|
||||||
|
// correctly, so just accept it as reasonable behavior.
|
||||||
|
|
||||||
|
if ($ids) {
|
||||||
|
$tasks = array_select_keys($tasks, $ids);
|
||||||
|
} else {
|
||||||
|
$tasks = msort($tasks, 'getID');
|
||||||
|
}
|
||||||
|
|
||||||
return $tasks;
|
return $tasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,4 +259,52 @@ abstract class PhabricatorWorkerManagementWorkflow
|
||||||
return pht('Task %d (%s)', $task->getID(), $task->getTaskClass());
|
return pht('Task %d (%s)', $task->getID(), $task->getTaskClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function loadObjectPHIDsFromArguments(array $names) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$seen_names = array();
|
||||||
|
foreach ($names as $name) {
|
||||||
|
if (isset($seen_names[$name])) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Object "%s" is specified more than once. Specify only unique '.
|
||||||
|
'objects.',
|
||||||
|
$name));
|
||||||
|
}
|
||||||
|
$seen_names[$name] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$object_query = id(new PhabricatorObjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withNames($names);
|
||||||
|
|
||||||
|
$object_query->execute();
|
||||||
|
|
||||||
|
$name_map = $object_query->getNamedResults();
|
||||||
|
$phid_map = array();
|
||||||
|
foreach ($names as $name) {
|
||||||
|
if (!isset($name_map[$name])) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'No object with name "%s" could be loaded.',
|
||||||
|
$name));
|
||||||
|
}
|
||||||
|
|
||||||
|
$phid = $name_map[$name]->getPHID();
|
||||||
|
|
||||||
|
if (isset($phid_map[$phid])) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Names "%s" and "%s" identify the same object. Specify only '.
|
||||||
|
'unique objects.',
|
||||||
|
$name,
|
||||||
|
$phid_map[$phid]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$phid_map[$phid] = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_keys($phid_map);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,13 @@ abstract class PhabricatorWorkerTaskQuery
|
||||||
private $dateModifiedSince;
|
private $dateModifiedSince;
|
||||||
private $dateCreatedBefore;
|
private $dateCreatedBefore;
|
||||||
private $objectPHIDs;
|
private $objectPHIDs;
|
||||||
|
private $containerPHIDs;
|
||||||
private $classNames;
|
private $classNames;
|
||||||
private $limit;
|
private $limit;
|
||||||
private $minFailureCount;
|
private $minFailureCount;
|
||||||
private $maxFailureCount;
|
private $maxFailureCount;
|
||||||
|
private $minPriority;
|
||||||
|
private $maxPriority;
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
$this->ids = $ids;
|
$this->ids = $ids;
|
||||||
|
@ -32,6 +35,11 @@ abstract class PhabricatorWorkerTaskQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withContainerPHIDs(array $phids) {
|
||||||
|
$this->containerPHIDs = $phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function withClassNames(array $names) {
|
public function withClassNames(array $names) {
|
||||||
$this->classNames = $names;
|
$this->classNames = $names;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -43,6 +51,12 @@ abstract class PhabricatorWorkerTaskQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withPriorityBetween($min, $max) {
|
||||||
|
$this->minPriority = $min;
|
||||||
|
$this->maxPriority = $max;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function setLimit($limit) {
|
public function setLimit($limit) {
|
||||||
$this->limit = $limit;
|
$this->limit = $limit;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -65,6 +79,13 @@ abstract class PhabricatorWorkerTaskQuery
|
||||||
$this->objectPHIDs);
|
$this->objectPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->containerPHIDs !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'containerPHID IN (%Ls)',
|
||||||
|
$this->containerPHIDs);
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->dateModifiedSince !== null) {
|
if ($this->dateModifiedSince !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
|
@ -100,6 +121,20 @@ abstract class PhabricatorWorkerTaskQuery
|
||||||
$this->maxFailureCount);
|
$this->maxFailureCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->minPriority !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'priority >= %d',
|
||||||
|
$this->minPriority);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->maxPriority !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'priority <= %d',
|
||||||
|
$this->maxPriority);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->formatWhereClause($conn, $where);
|
return $this->formatWhereClause($conn, $where);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,7 @@ final class PhabricatorWorkerActiveTask extends PhabricatorWorkerTask {
|
||||||
->setDataID($this->getDataID())
|
->setDataID($this->getDataID())
|
||||||
->setPriority($this->getPriority())
|
->setPriority($this->getPriority())
|
||||||
->setObjectPHID($this->getObjectPHID())
|
->setObjectPHID($this->getObjectPHID())
|
||||||
|
->setContainerPHID($this->getContainerPHID())
|
||||||
->setResult($result)
|
->setResult($result)
|
||||||
->setDuration($duration)
|
->setDuration($duration)
|
||||||
->setDateCreated($this->getDateCreated())
|
->setDateCreated($this->getDateCreated())
|
||||||
|
|
|
@ -87,6 +87,7 @@ final class PhabricatorWorkerArchiveTask extends PhabricatorWorkerTask {
|
||||||
->setDataID($this->getDataID())
|
->setDataID($this->getDataID())
|
||||||
->setPriority($this->getPriority())
|
->setPriority($this->getPriority())
|
||||||
->setObjectPHID($this->getObjectPHID())
|
->setObjectPHID($this->getObjectPHID())
|
||||||
|
->setContainerPHID($this->getContainerPHID())
|
||||||
->setDateCreated($this->getDateCreated())
|
->setDateCreated($this->getDateCreated())
|
||||||
->insert();
|
->insert();
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ abstract class PhabricatorWorkerTask extends PhabricatorWorkerDAO {
|
||||||
protected $dataID;
|
protected $dataID;
|
||||||
protected $priority;
|
protected $priority;
|
||||||
protected $objectPHID;
|
protected $objectPHID;
|
||||||
|
protected $containerPHID;
|
||||||
|
|
||||||
private $data;
|
private $data;
|
||||||
private $executionException;
|
private $executionException;
|
||||||
|
@ -25,11 +26,15 @@ abstract class PhabricatorWorkerTask extends PhabricatorWorkerDAO {
|
||||||
'failureTime' => 'epoch?',
|
'failureTime' => 'epoch?',
|
||||||
'priority' => 'uint32',
|
'priority' => 'uint32',
|
||||||
'objectPHID' => 'phid?',
|
'objectPHID' => 'phid?',
|
||||||
|
'containerPHID' => 'phid?',
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
'key_object' => array(
|
'key_object' => array(
|
||||||
'columns' => array('objectPHID'),
|
'columns' => array('objectPHID'),
|
||||||
),
|
),
|
||||||
|
'key_container' => array(
|
||||||
|
'columns' => array('containerPHID'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1759,6 +1759,36 @@ final class PhabricatorUSEnglishTranslation
|
||||||
'These inline comments will be saved and published.',
|
'These inline comments will be saved and published.',
|
||||||
),
|
),
|
||||||
|
|
||||||
|
'Delayed %s task(s).' => array(
|
||||||
|
'Delayed 1 task.',
|
||||||
|
'Delayed %s tasks.',
|
||||||
|
),
|
||||||
|
|
||||||
|
'Freed %s task lease(s).' => array(
|
||||||
|
'Freed 1 task lease.',
|
||||||
|
'Freed %s task leases.',
|
||||||
|
),
|
||||||
|
|
||||||
|
'Cancelled %s task(s).' => array(
|
||||||
|
'Cancelled 1 task.',
|
||||||
|
'Cancelled %s tasks.',
|
||||||
|
),
|
||||||
|
|
||||||
|
'Queued %s task(s) for retry.' => array(
|
||||||
|
'Queued 1 task for retry.',
|
||||||
|
'Queued %s tasks for retry.',
|
||||||
|
),
|
||||||
|
|
||||||
|
'Reprioritized %s task(s).' => array(
|
||||||
|
'Reprioritized one task.',
|
||||||
|
'Reprioritized %s tasks.',
|
||||||
|
),
|
||||||
|
|
||||||
|
'Executed %s task(s).' => array(
|
||||||
|
'Executed 1 task.',
|
||||||
|
'Executed %s tasks.',
|
||||||
|
),
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function getExternalCursorStringForResult($object) {
|
private function getExternalCursorStringForResult($object) {
|
||||||
$cursor = $this->newExternalCursorStringForResult($object);
|
$cursor = $this->newExternalCursorStringForResult($object);
|
||||||
|
|
||||||
if (!is_string($cursor)) {
|
if (!is_string($cursor)) {
|
||||||
|
@ -154,7 +154,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
||||||
return $this->externalCursorString;
|
return $this->externalCursorString;
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function setExternalCursorString($external_cursor) {
|
private function setExternalCursorString($external_cursor) {
|
||||||
$this->externalCursorString = $external_cursor;
|
$this->externalCursorString = $external_cursor;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -168,17 +168,17 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function getInternalCursorObject() {
|
private function getInternalCursorObject() {
|
||||||
return $this->internalCursorObject;
|
return $this->internalCursorObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function setInternalCursorObject(
|
private function setInternalCursorObject(
|
||||||
PhabricatorQueryCursor $cursor) {
|
PhabricatorQueryCursor $cursor) {
|
||||||
$this->internalCursorObject = $cursor;
|
$this->internalCursorObject = $cursor;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function getInternalCursorFromExternalCursor(
|
private function getInternalCursorFromExternalCursor(
|
||||||
$cursor_string) {
|
$cursor_string) {
|
||||||
|
|
||||||
$cursor_object = $this->newInternalCursorFromExternalCursor($cursor_string);
|
$cursor_object = $this->newInternalCursorFromExternalCursor($cursor_string);
|
||||||
|
@ -196,7 +196,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
||||||
return $cursor_object;
|
return $cursor_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function getPagingMapFromCursorObject(
|
private function getPagingMapFromCursorObject(
|
||||||
PhabricatorQueryCursor $cursor,
|
PhabricatorQueryCursor $cursor,
|
||||||
array $keys) {
|
array $keys) {
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,10 @@ final class PhabricatorStoragePatch extends Phobject {
|
||||||
private $after;
|
private $after;
|
||||||
private $legacy;
|
private $legacy;
|
||||||
private $dead;
|
private $dead;
|
||||||
|
private $phase;
|
||||||
|
|
||||||
|
const PHASE_DEFAULT = 'default';
|
||||||
|
const PHASE_WORKER = 'worker';
|
||||||
|
|
||||||
public function __construct(array $dict) {
|
public function __construct(array $dict) {
|
||||||
$this->key = $dict['key'];
|
$this->key = $dict['key'];
|
||||||
|
@ -18,6 +22,7 @@ final class PhabricatorStoragePatch extends Phobject {
|
||||||
$this->name = $dict['name'];
|
$this->name = $dict['name'];
|
||||||
$this->after = $dict['after'];
|
$this->after = $dict['after'];
|
||||||
$this->dead = $dict['dead'];
|
$this->dead = $dict['dead'];
|
||||||
|
$this->phase = $dict['phase'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLegacy() {
|
public function getLegacy() {
|
||||||
|
@ -44,6 +49,10 @@ final class PhabricatorStoragePatch extends Phobject {
|
||||||
return $this->key;
|
return $this->key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPhase() {
|
||||||
|
return $this->phase;
|
||||||
|
}
|
||||||
|
|
||||||
public function isDead() {
|
public function isDead() {
|
||||||
return $this->dead;
|
return $this->dead;
|
||||||
}
|
}
|
||||||
|
@ -52,4 +61,31 @@ final class PhabricatorStoragePatch extends Phobject {
|
||||||
return ($this->getType() == 'php');
|
return ($this->getType() == 'php');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getPhaseList() {
|
||||||
|
return array_keys(self::getPhaseMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getDefaultPhase() {
|
||||||
|
return self::PHASE_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getPhaseMap() {
|
||||||
|
return array(
|
||||||
|
self::PHASE_DEFAULT => array(
|
||||||
|
'order' => 0,
|
||||||
|
),
|
||||||
|
self::PHASE_WORKER => array(
|
||||||
|
'order' => 1,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newSortVector() {
|
||||||
|
$map = self::getPhaseMap();
|
||||||
|
$phase = $this->getPhase();
|
||||||
|
|
||||||
|
return id(new PhutilSortVector())
|
||||||
|
->addInt($map[$phase]['order']);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ final class PhabricatorStorageManagementStatusWorkflow
|
||||||
$table = id(new PhutilConsoleTable())
|
$table = id(new PhutilConsoleTable())
|
||||||
->setShowHeader(false)
|
->setShowHeader(false)
|
||||||
->addColumn('id', array('title' => pht('ID')))
|
->addColumn('id', array('title' => pht('ID')))
|
||||||
|
->addColumn('phase', array('title' => pht('Phase')))
|
||||||
->addColumn('host', array('title' => pht('Host')))
|
->addColumn('host', array('title' => pht('Host')))
|
||||||
->addColumn('status', array('title' => pht('Status')))
|
->addColumn('status', array('title' => pht('Status')))
|
||||||
->addColumn('duration', array('title' => pht('Duration')))
|
->addColumn('duration', array('title' => pht('Duration')))
|
||||||
|
@ -49,16 +50,22 @@ final class PhabricatorStorageManagementStatusWorkflow
|
||||||
$duration = pht('%s us', new PhutilNumber($duration));
|
$duration = pht('%s us', new PhutilNumber($duration));
|
||||||
}
|
}
|
||||||
|
|
||||||
$table->addRow(array(
|
if (in_array($patch->getFullKey(), $applied)) {
|
||||||
'id' => $patch->getFullKey(),
|
$status = pht('Applied');
|
||||||
'host' => $ref->getRefKey(),
|
} else {
|
||||||
'status' => in_array($patch->getFullKey(), $applied)
|
$status = pht('Not Applied');
|
||||||
? pht('Applied')
|
}
|
||||||
: pht('Not Applied'),
|
|
||||||
'duration' => $duration,
|
$table->addRow(
|
||||||
'type' => $patch->getType(),
|
array(
|
||||||
'name' => $patch->getName(),
|
'id' => $patch->getFullKey(),
|
||||||
));
|
'phase' => $patch->getPhase(),
|
||||||
|
'host' => $ref->getRefKey(),
|
||||||
|
'status' => $status,
|
||||||
|
'duration' => $duration,
|
||||||
|
'type' => $patch->getType(),
|
||||||
|
'name' => $patch->getName(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
$table->draw();
|
$table->draw();
|
||||||
|
|
|
@ -156,7 +156,7 @@ abstract class PhabricatorStorageManagementWorkflow
|
||||||
return $err;
|
return $err;
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function doAdjustSchemata(
|
private function doAdjustSchemata(
|
||||||
PhabricatorStorageManagementAPI $api,
|
PhabricatorStorageManagementAPI $api,
|
||||||
$unsafe) {
|
$unsafe) {
|
||||||
|
|
||||||
|
@ -913,7 +913,7 @@ abstract class PhabricatorStorageManagementWorkflow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final private function doUpgradeSchemata(
|
private function doUpgradeSchemata(
|
||||||
array $apis,
|
array $apis,
|
||||||
$apply_only,
|
$apply_only,
|
||||||
$no_quickstart,
|
$no_quickstart,
|
||||||
|
@ -922,6 +922,10 @@ abstract class PhabricatorStorageManagementWorkflow
|
||||||
$patches = $this->patches;
|
$patches = $this->patches;
|
||||||
$is_dryrun = $this->dryRun;
|
$is_dryrun = $this->dryRun;
|
||||||
|
|
||||||
|
// We expect that patches should already be sorted properly. However,
|
||||||
|
// phase behavior will be wrong if they aren't, so make sure.
|
||||||
|
$patches = msortv($patches, 'newSortVector');
|
||||||
|
|
||||||
$api_map = array();
|
$api_map = array();
|
||||||
foreach ($apis as $api) {
|
foreach ($apis as $api) {
|
||||||
$api_map[$api->getRef()->getRefKey()] = $api;
|
$api_map[$api->getRef()->getRefKey()] = $api;
|
||||||
|
|
|
@ -27,10 +27,20 @@ abstract class PhabricatorSQLPatchList extends Phobject {
|
||||||
$directory));
|
$directory));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$patch_type = $matches[1];
|
||||||
|
$patch_full_path = rtrim($directory, '/').'/'.$patch;
|
||||||
|
|
||||||
|
$attributes = array();
|
||||||
|
if ($patch_type === 'php') {
|
||||||
|
$attributes = $this->getPHPPatchAttributes(
|
||||||
|
$patch,
|
||||||
|
$patch_full_path);
|
||||||
|
}
|
||||||
|
|
||||||
$patches[$patch] = array(
|
$patches[$patch] = array(
|
||||||
'type' => $matches[1],
|
'type' => $patch_type,
|
||||||
'name' => rtrim($directory, '/').'/'.$patch,
|
'name' => $patch_full_path,
|
||||||
);
|
) + $attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $patches;
|
return $patches;
|
||||||
|
@ -45,8 +55,16 @@ abstract class PhabricatorSQLPatchList extends Phobject {
|
||||||
$specs = array();
|
$specs = array();
|
||||||
$seen_namespaces = array();
|
$seen_namespaces = array();
|
||||||
|
|
||||||
|
$phases = PhabricatorStoragePatch::getPhaseList();
|
||||||
|
$phases = array_fuse($phases);
|
||||||
|
|
||||||
|
$default_phase = PhabricatorStoragePatch::getDefaultPhase();
|
||||||
|
|
||||||
foreach ($patch_lists as $patch_list) {
|
foreach ($patch_lists as $patch_list) {
|
||||||
$last_key = null;
|
$last_keys = array_fill_keys(
|
||||||
|
array_keys($phases),
|
||||||
|
null);
|
||||||
|
|
||||||
foreach ($patch_list->getPatches() as $key => $patch) {
|
foreach ($patch_list->getPatches() as $key => $patch) {
|
||||||
if (!is_array($patch)) {
|
if (!is_array($patch)) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
|
@ -63,6 +81,7 @@ abstract class PhabricatorSQLPatchList extends Phobject {
|
||||||
'after' => true,
|
'after' => true,
|
||||||
'legacy' => true,
|
'legacy' => true,
|
||||||
'dead' => true,
|
'dead' => true,
|
||||||
|
'phase' => true,
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($patch as $pkey => $pval) {
|
foreach ($patch as $pkey => $pval) {
|
||||||
|
@ -128,8 +147,26 @@ abstract class PhabricatorSQLPatchList extends Phobject {
|
||||||
$patch['legacy'] = false;
|
$patch['legacy'] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!array_key_exists('phase', $patch)) {
|
||||||
|
$patch['phase'] = $default_phase;
|
||||||
|
}
|
||||||
|
|
||||||
|
$patch_phase = $patch['phase'];
|
||||||
|
|
||||||
|
if (!isset($phases[$patch_phase])) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Storage patch "%s" specifies it should apply in phase "%s", '.
|
||||||
|
'but this phase is unrecognized. Valid phases are: %s.',
|
||||||
|
$full_key,
|
||||||
|
$patch_phase,
|
||||||
|
implode(', ', array_keys($phases))));
|
||||||
|
}
|
||||||
|
|
||||||
|
$last_key = $last_keys[$patch_phase];
|
||||||
|
|
||||||
if (!array_key_exists('after', $patch)) {
|
if (!array_key_exists('after', $patch)) {
|
||||||
if ($last_key === null) {
|
if ($last_key === null && $patch_phase === $default_phase) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
"Patch '%s' is missing key 'after', and is the first patch ".
|
"Patch '%s' is missing key 'after', and is the first patch ".
|
||||||
|
@ -139,10 +176,14 @@ abstract class PhabricatorSQLPatchList extends Phobject {
|
||||||
$full_key,
|
$full_key,
|
||||||
get_class($patch_list)));
|
get_class($patch_list)));
|
||||||
} else {
|
} else {
|
||||||
$patch['after'] = array($last_key);
|
if ($last_key === null) {
|
||||||
|
$patch['after'] = array();
|
||||||
|
} else {
|
||||||
|
$patch['after'] = array($last_key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$last_key = $full_key;
|
$last_keys[$patch_phase] = $full_key;
|
||||||
|
|
||||||
foreach ($patch['after'] as $after_key => $after) {
|
foreach ($patch['after'] as $after_key => $after) {
|
||||||
if (strpos($after, ':') === false) {
|
if (strpos($after, ':') === false) {
|
||||||
|
@ -186,6 +227,21 @@ abstract class PhabricatorSQLPatchList extends Phobject {
|
||||||
$key,
|
$key,
|
||||||
$after));
|
$after));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$patch_phase = $patch['phase'];
|
||||||
|
$after_phase = $specs[$after]['phase'];
|
||||||
|
|
||||||
|
if ($patch_phase !== $after_phase) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Storage patch "%s" executes in phase "%s", but depends on '.
|
||||||
|
'patch "%s" which is in a different phase ("%s"). Patches '.
|
||||||
|
'may not have dependencies across phases.',
|
||||||
|
$key,
|
||||||
|
$patch_phase,
|
||||||
|
$after,
|
||||||
|
$after_phase));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +252,94 @@ abstract class PhabricatorSQLPatchList extends Phobject {
|
||||||
|
|
||||||
// TODO: Detect cycles?
|
// TODO: Detect cycles?
|
||||||
|
|
||||||
|
$patches = msortv($patches, 'newSortVector');
|
||||||
|
|
||||||
return $patches;
|
return $patches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getPHPPatchAttributes($patch_name, $full_path) {
|
||||||
|
$data = Filesystem::readFile($full_path);
|
||||||
|
|
||||||
|
$phase_list = PhabricatorStoragePatch::getPhaseList();
|
||||||
|
$phase_map = array_fuse($phase_list);
|
||||||
|
|
||||||
|
$attributes = array();
|
||||||
|
|
||||||
|
$lines = phutil_split_lines($data, false);
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
// Skip over the "PHP" line.
|
||||||
|
if (preg_match('(^<\?)', $line)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip over blank lines.
|
||||||
|
if (!strlen(trim($line))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a "//" comment...
|
||||||
|
if (preg_match('(^\s*//)', $line)) {
|
||||||
|
$matches = null;
|
||||||
|
if (preg_match('(^\s*//\s*@(\S+)(?:\s+(.*))?\z)', $line, $matches)) {
|
||||||
|
$attr_key = $matches[1];
|
||||||
|
$attr_value = trim(idx($matches, 2));
|
||||||
|
|
||||||
|
switch ($attr_key) {
|
||||||
|
case 'phase':
|
||||||
|
$phase_name = $attr_value;
|
||||||
|
|
||||||
|
if (!strlen($phase_name)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Storage patch "%s" specifies a "@phase" attribute with '.
|
||||||
|
'no phase value. Phase attributes must specify a value, '.
|
||||||
|
'like "@phase default".',
|
||||||
|
$patch_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($phase_map[$phase_name])) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Storage patch "%s" specifies a "@phase" value ("%s"), '.
|
||||||
|
'but this is not a recognized phase. Valid phases '.
|
||||||
|
'are: %s.',
|
||||||
|
$patch_name,
|
||||||
|
$phase_name,
|
||||||
|
implode(', ', $phase_list)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($attributes['phase'])) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Storage patch "%s" specifies a "@phase" value ("%s"), '.
|
||||||
|
'but it already has a specified phase ("%s"). Patches '.
|
||||||
|
'may not specify multiple phases.',
|
||||||
|
$patch_name,
|
||||||
|
$phase_name,
|
||||||
|
$attributes['phase']));
|
||||||
|
}
|
||||||
|
|
||||||
|
$attributes[$attr_key] = $phase_name;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Storage patch "%s" specifies attribute "%s", but this '.
|
||||||
|
'attribute is unknown.',
|
||||||
|
$patch_name,
|
||||||
|
$attr_key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is anything else, we're all done. Attributes must be marked
|
||||||
|
// in the header of the file.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return $attributes;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue