1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-22 05:20:56 +01:00

Add a bin/herald test ... for doing test runs via the CLI

Summary: Ref T13216. See D19666. It's currently tricky to profile Herald test runs since you have to submit a form and repeating them is a bit of a mess. Provide a simple CLI wrapper so we can use `--xprofile`. This is also maybe nice-to-have if we're ever debugging anything here.

Test Plan: Ran `bin/herald test --object ... --type ...` and got a sensible looking transcript in the UI.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13216

Differential Revision: https://secure.phabricator.com/D19806
This commit is contained in:
epriestley 2018-11-14 15:23:07 -08:00
parent 8a8123c9db
commit 533e4e13b3
6 changed files with 185 additions and 17 deletions

1
bin/herald Symbolic link
View file

@ -0,0 +1 @@
../scripts/setup/manage_herald.php

21
scripts/setup/manage_herald.php Executable file
View file

@ -0,0 +1,21 @@
#!/usr/bin/env php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/init/init-script.php';
$args = new PhutilArgumentParser($argv);
$args->setTagline(pht('manage Herald'));
$args->setSynopsis(<<<EOSYNOPSIS
**herald** __command__ [__options__]
Manage and debug Herald.
EOSYNOPSIS
);
$args->parseStandardArguments();
$workflows = id(new PhutilClassMapQuery())
->setAncestorClass('HeraldManagementWorkflow')
->execute();
$workflows[] = new PhutilHelpArgumentWorkflow();
$args->parseWorkflows($workflows);

View file

@ -1488,6 +1488,7 @@ phutil_register_library_map(array(
'HeraldInvalidConditionException' => 'applications/herald/engine/exception/HeraldInvalidConditionException.php',
'HeraldMailableState' => 'applications/herald/state/HeraldMailableState.php',
'HeraldManageGlobalRulesCapability' => 'applications/herald/capability/HeraldManageGlobalRulesCapability.php',
'HeraldManagementWorkflow' => 'applications/herald/management/HeraldManagementWorkflow.php',
'HeraldManiphestTaskAdapter' => 'applications/maniphest/herald/HeraldManiphestTaskAdapter.php',
'HeraldNewController' => 'applications/herald/controller/HeraldNewController.php',
'HeraldNewObjectField' => 'applications/herald/field/HeraldNewObjectField.php',
@ -1537,6 +1538,7 @@ phutil_register_library_map(array(
'HeraldSupportActionGroup' => 'applications/herald/action/HeraldSupportActionGroup.php',
'HeraldSupportFieldGroup' => 'applications/herald/field/HeraldSupportFieldGroup.php',
'HeraldTestConsoleController' => 'applications/herald/controller/HeraldTestConsoleController.php',
'HeraldTestManagementWorkflow' => 'applications/herald/management/HeraldTestManagementWorkflow.php',
'HeraldTextFieldValue' => 'applications/herald/value/HeraldTextFieldValue.php',
'HeraldTokenizerFieldValue' => 'applications/herald/value/HeraldTokenizerFieldValue.php',
'HeraldTransactionQuery' => 'applications/herald/query/HeraldTransactionQuery.php',
@ -6975,6 +6977,7 @@ phutil_register_library_map(array(
'HeraldInvalidConditionException' => 'Exception',
'HeraldMailableState' => 'HeraldState',
'HeraldManageGlobalRulesCapability' => 'PhabricatorPolicyCapability',
'HeraldManagementWorkflow' => 'PhabricatorManagementWorkflow',
'HeraldManiphestTaskAdapter' => 'HeraldAdapter',
'HeraldNewController' => 'HeraldController',
'HeraldNewObjectField' => 'HeraldField',
@ -7031,6 +7034,7 @@ phutil_register_library_map(array(
'HeraldSupportActionGroup' => 'HeraldActionGroup',
'HeraldSupportFieldGroup' => 'HeraldFieldGroup',
'HeraldTestConsoleController' => 'HeraldController',
'HeraldTestManagementWorkflow' => 'HeraldManagementWorkflow',
'HeraldTextFieldValue' => 'HeraldFieldValue',
'HeraldTokenizerFieldValue' => 'HeraldFieldValue',
'HeraldTransactionQuery' => 'PhabricatorApplicationTransactionQuery',

View file

@ -0,0 +1,4 @@
<?php
abstract class HeraldManagementWorkflow
extends PhabricatorManagementWorkflow {}

View file

@ -0,0 +1,139 @@
<?php
final class HeraldTestManagementWorkflow
extends HeraldManagementWorkflow {
protected function didConstruct() {
$this
->setName('test')
->setExamples('**test** --object __object__ --type __type__')
->setSynopsis(
pht(
'Test content rules for an object. Executes a dry run, like the '.
'web UI test console.'))
->setArguments(
array(
array(
'name' => 'object',
'param' => 'object',
'help' => pht('Run rules on this object.'),
),
array(
'name' => 'type',
'param' => 'type',
'help' => pht('Run rules for this content type.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$viewer = $this->getViewer();
$object_name = $args->getArg('object');
if (!strlen($object_name)) {
throw new PhutilArgumentUsageException(
pht('Specify an object to test rules for with "--object".'));
}
$objects = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withNames(array($object_name))
->execute();
if (!$objects) {
throw new PhutilArgumentUsageException(
pht(
'Unable to load specified object ("%s").',
$object_name));
}
$object = head($objects);
$adapters = HeraldAdapter::getAllAdapters();
$can_select = array();
$display_adapters = array();
foreach ($adapters as $key => $adapter) {
if (!$adapter->isTestAdapterForObject($object)) {
continue;
}
if (!$adapter->isAvailableToUser($viewer)) {
continue;
}
$display_adapters[$key] = $adapter;
if ($adapter->canCreateTestAdapterForObject($object)) {
$can_select[$key] = $adapter;
}
}
$content_type = $args->getArg('type');
if (!strlen($content_type)) {
throw new PhutilArgumentUsageException(
pht(
'Specify a content type to run rules for. For this object, valid '.
'content types are: %s.',
implode(', ', array_keys($can_select))));
}
if (!isset($can_select[$content_type])) {
if (!isset($display_adapters[$content_type])) {
throw new PhutilArgumentUsageException(
pht(
'Specify a content type to run rules for. The specified content '.
'type ("%s") is not valid. For this object, valid content types '.
'are: %s.',
$content_type,
implode(', ', array_keys($can_select))));
} else {
throw new PhutilArgumentUsageException(
pht(
'The specified content type ("%s") does not support dry runs. '.
'Choose a testable content type. For this object, valid content '.
'types are: %s.',
$content_type,
implode(', ', array_keys($can_select))));
}
}
$adapter = $can_select[$content_type]->newTestAdapter(
$viewer,
$object);
$content_source = $this->newContentSource();
$adapter
->setContentSource($content_source)
->setIsNewObject(false)
->setViewer($viewer);
$rules = id(new HeraldRuleQuery())
->setViewer($viewer)
->withContentTypes(array($adapter->getAdapterContentType()))
->withDisabled(false)
->needConditionsAndActions(true)
->needAppliedToPHIDs(array($object->getPHID()))
->needValidateAuthors(true)
->execute();
$engine = id(new HeraldEngine())
->setDryRun(true);
$effects = $engine->applyRules($rules, $adapter);
$engine->applyEffects($effects, $adapter, $rules);
$xscript = $engine->getTranscript();
$uri = '/herald/transcript/'.$xscript->getID().'/';
$uri = PhabricatorEnv::getProductionURI($uri);
echo tsprintf(
"%s\n\n __%s__\n\n",
pht('Test run complete. Transcript:'),
$uri);
return 0;
}
}

View file

@ -30,36 +30,35 @@ final class HeraldTranscriptQuery
protected function loadPage() {
$transcript = new HeraldTranscript();
$conn_r = $transcript->establishConnection('r');
$conn = $transcript->establishConnection('r');
// NOTE: Transcripts include a potentially enormous amount of serialized
// data, so we're loading only some of the fields here if the caller asked
// for partial records.
if ($this->needPartialRecords) {
$fields = implode(
', ',
array(
'id',
'phid',
'objectPHID',
'time',
'duration',
'dryRun',
'host',
));
$fields = array(
'id',
'phid',
'objectPHID',
'time',
'duration',
'dryRun',
'host',
);
$fields = qsprintf($conn, '%LC', $fields);
} else {
$fields = '*';
$fields = qsprintf($conn, '*');
}
$rows = queryfx_all(
$conn_r,
$conn,
'SELECT %Q FROM %T t %Q %Q %Q',
$fields,
$transcript->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$this->buildWhereClause($conn),
$this->buildOrderClause($conn),
$this->buildLimitClause($conn));
$transcripts = $transcript->loadAllFromArray($rows);