@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+185 -17
+1
bin/herald
··· 1 + ../scripts/setup/manage_herald.php
+21
scripts/setup/manage_herald.php
··· 1 + #!/usr/bin/env php 2 + <?php 3 + 4 + $root = dirname(dirname(dirname(__FILE__))); 5 + require_once $root.'/scripts/init/init-script.php'; 6 + 7 + $args = new PhutilArgumentParser($argv); 8 + $args->setTagline(pht('manage Herald')); 9 + $args->setSynopsis(<<<EOSYNOPSIS 10 + **herald** __command__ [__options__] 11 + Manage and debug Herald. 12 + 13 + EOSYNOPSIS 14 + ); 15 + $args->parseStandardArguments(); 16 + 17 + $workflows = id(new PhutilClassMapQuery()) 18 + ->setAncestorClass('HeraldManagementWorkflow') 19 + ->execute(); 20 + $workflows[] = new PhutilHelpArgumentWorkflow(); 21 + $args->parseWorkflows($workflows);
+4
src/__phutil_library_map__.php
··· 1488 1488 'HeraldInvalidConditionException' => 'applications/herald/engine/exception/HeraldInvalidConditionException.php', 1489 1489 'HeraldMailableState' => 'applications/herald/state/HeraldMailableState.php', 1490 1490 'HeraldManageGlobalRulesCapability' => 'applications/herald/capability/HeraldManageGlobalRulesCapability.php', 1491 + 'HeraldManagementWorkflow' => 'applications/herald/management/HeraldManagementWorkflow.php', 1491 1492 'HeraldManiphestTaskAdapter' => 'applications/maniphest/herald/HeraldManiphestTaskAdapter.php', 1492 1493 'HeraldNewController' => 'applications/herald/controller/HeraldNewController.php', 1493 1494 'HeraldNewObjectField' => 'applications/herald/field/HeraldNewObjectField.php', ··· 1537 1538 'HeraldSupportActionGroup' => 'applications/herald/action/HeraldSupportActionGroup.php', 1538 1539 'HeraldSupportFieldGroup' => 'applications/herald/field/HeraldSupportFieldGroup.php', 1539 1540 'HeraldTestConsoleController' => 'applications/herald/controller/HeraldTestConsoleController.php', 1541 + 'HeraldTestManagementWorkflow' => 'applications/herald/management/HeraldTestManagementWorkflow.php', 1540 1542 'HeraldTextFieldValue' => 'applications/herald/value/HeraldTextFieldValue.php', 1541 1543 'HeraldTokenizerFieldValue' => 'applications/herald/value/HeraldTokenizerFieldValue.php', 1542 1544 'HeraldTransactionQuery' => 'applications/herald/query/HeraldTransactionQuery.php', ··· 6975 6977 'HeraldInvalidConditionException' => 'Exception', 6976 6978 'HeraldMailableState' => 'HeraldState', 6977 6979 'HeraldManageGlobalRulesCapability' => 'PhabricatorPolicyCapability', 6980 + 'HeraldManagementWorkflow' => 'PhabricatorManagementWorkflow', 6978 6981 'HeraldManiphestTaskAdapter' => 'HeraldAdapter', 6979 6982 'HeraldNewController' => 'HeraldController', 6980 6983 'HeraldNewObjectField' => 'HeraldField', ··· 7031 7034 'HeraldSupportActionGroup' => 'HeraldActionGroup', 7032 7035 'HeraldSupportFieldGroup' => 'HeraldFieldGroup', 7033 7036 'HeraldTestConsoleController' => 'HeraldController', 7037 + 'HeraldTestManagementWorkflow' => 'HeraldManagementWorkflow', 7034 7038 'HeraldTextFieldValue' => 'HeraldFieldValue', 7035 7039 'HeraldTokenizerFieldValue' => 'HeraldFieldValue', 7036 7040 'HeraldTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+4
src/applications/herald/management/HeraldManagementWorkflow.php
··· 1 + <?php 2 + 3 + abstract class HeraldManagementWorkflow 4 + extends PhabricatorManagementWorkflow {}
+139
src/applications/herald/management/HeraldTestManagementWorkflow.php
··· 1 + <?php 2 + 3 + final class HeraldTestManagementWorkflow 4 + extends HeraldManagementWorkflow { 5 + 6 + protected function didConstruct() { 7 + $this 8 + ->setName('test') 9 + ->setExamples('**test** --object __object__ --type __type__') 10 + ->setSynopsis( 11 + pht( 12 + 'Test content rules for an object. Executes a dry run, like the '. 13 + 'web UI test console.')) 14 + ->setArguments( 15 + array( 16 + array( 17 + 'name' => 'object', 18 + 'param' => 'object', 19 + 'help' => pht('Run rules on this object.'), 20 + ), 21 + array( 22 + 'name' => 'type', 23 + 'param' => 'type', 24 + 'help' => pht('Run rules for this content type.'), 25 + ), 26 + )); 27 + } 28 + 29 + public function execute(PhutilArgumentParser $args) { 30 + $viewer = $this->getViewer(); 31 + 32 + $object_name = $args->getArg('object'); 33 + if (!strlen($object_name)) { 34 + throw new PhutilArgumentUsageException( 35 + pht('Specify an object to test rules for with "--object".')); 36 + } 37 + 38 + $objects = id(new PhabricatorObjectQuery()) 39 + ->setViewer($viewer) 40 + ->withNames(array($object_name)) 41 + ->execute(); 42 + if (!$objects) { 43 + throw new PhutilArgumentUsageException( 44 + pht( 45 + 'Unable to load specified object ("%s").', 46 + $object_name)); 47 + } 48 + $object = head($objects); 49 + 50 + $adapters = HeraldAdapter::getAllAdapters(); 51 + 52 + $can_select = array(); 53 + $display_adapters = array(); 54 + foreach ($adapters as $key => $adapter) { 55 + if (!$adapter->isTestAdapterForObject($object)) { 56 + continue; 57 + } 58 + 59 + if (!$adapter->isAvailableToUser($viewer)) { 60 + continue; 61 + } 62 + 63 + $display_adapters[$key] = $adapter; 64 + 65 + if ($adapter->canCreateTestAdapterForObject($object)) { 66 + $can_select[$key] = $adapter; 67 + } 68 + } 69 + 70 + 71 + $content_type = $args->getArg('type'); 72 + if (!strlen($content_type)) { 73 + throw new PhutilArgumentUsageException( 74 + pht( 75 + 'Specify a content type to run rules for. For this object, valid '. 76 + 'content types are: %s.', 77 + implode(', ', array_keys($can_select)))); 78 + } 79 + 80 + if (!isset($can_select[$content_type])) { 81 + if (!isset($display_adapters[$content_type])) { 82 + throw new PhutilArgumentUsageException( 83 + pht( 84 + 'Specify a content type to run rules for. The specified content '. 85 + 'type ("%s") is not valid. For this object, valid content types '. 86 + 'are: %s.', 87 + $content_type, 88 + implode(', ', array_keys($can_select)))); 89 + } else { 90 + throw new PhutilArgumentUsageException( 91 + pht( 92 + 'The specified content type ("%s") does not support dry runs. '. 93 + 'Choose a testable content type. For this object, valid content '. 94 + 'types are: %s.', 95 + $content_type, 96 + implode(', ', array_keys($can_select)))); 97 + } 98 + } 99 + 100 + $adapter = $can_select[$content_type]->newTestAdapter( 101 + $viewer, 102 + $object); 103 + 104 + $content_source = $this->newContentSource(); 105 + 106 + $adapter 107 + ->setContentSource($content_source) 108 + ->setIsNewObject(false) 109 + ->setViewer($viewer); 110 + 111 + $rules = id(new HeraldRuleQuery()) 112 + ->setViewer($viewer) 113 + ->withContentTypes(array($adapter->getAdapterContentType())) 114 + ->withDisabled(false) 115 + ->needConditionsAndActions(true) 116 + ->needAppliedToPHIDs(array($object->getPHID())) 117 + ->needValidateAuthors(true) 118 + ->execute(); 119 + 120 + $engine = id(new HeraldEngine()) 121 + ->setDryRun(true); 122 + 123 + $effects = $engine->applyRules($rules, $adapter); 124 + $engine->applyEffects($effects, $adapter, $rules); 125 + 126 + $xscript = $engine->getTranscript(); 127 + 128 + $uri = '/herald/transcript/'.$xscript->getID().'/'; 129 + $uri = PhabricatorEnv::getProductionURI($uri); 130 + 131 + echo tsprintf( 132 + "%s\n\n __%s__\n\n", 133 + pht('Test run complete. Transcript:'), 134 + $uri); 135 + 136 + return 0; 137 + } 138 + 139 + }
+16 -17
src/applications/herald/query/HeraldTranscriptQuery.php
··· 30 30 31 31 protected function loadPage() { 32 32 $transcript = new HeraldTranscript(); 33 - $conn_r = $transcript->establishConnection('r'); 33 + $conn = $transcript->establishConnection('r'); 34 34 35 35 // NOTE: Transcripts include a potentially enormous amount of serialized 36 36 // data, so we're loading only some of the fields here if the caller asked 37 37 // for partial records. 38 38 39 39 if ($this->needPartialRecords) { 40 - $fields = implode( 41 - ', ', 42 - array( 43 - 'id', 44 - 'phid', 45 - 'objectPHID', 46 - 'time', 47 - 'duration', 48 - 'dryRun', 49 - 'host', 50 - )); 40 + $fields = array( 41 + 'id', 42 + 'phid', 43 + 'objectPHID', 44 + 'time', 45 + 'duration', 46 + 'dryRun', 47 + 'host', 48 + ); 49 + $fields = qsprintf($conn, '%LC', $fields); 51 50 } else { 52 - $fields = '*'; 51 + $fields = qsprintf($conn, '*'); 53 52 } 54 53 55 54 $rows = queryfx_all( 56 - $conn_r, 55 + $conn, 57 56 'SELECT %Q FROM %T t %Q %Q %Q', 58 57 $fields, 59 58 $transcript->getTableName(), 60 - $this->buildWhereClause($conn_r), 61 - $this->buildOrderClause($conn_r), 62 - $this->buildLimitClause($conn_r)); 59 + $this->buildWhereClause($conn), 60 + $this->buildOrderClause($conn), 61 + $this->buildLimitClause($conn)); 63 62 64 63 $transcripts = $transcript->loadAllFromArray($rows); 65 64