From 3386920971b859b7a933940f712d96a45f699f50 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 17 Dec 2013 15:23:55 -0800 Subject: [PATCH] Add Herald support for blocking ref changes Summary: Ref T4195. Allows users to write Herald rules which block ref changes. For example, you can write a rule like `alincoln can not create branches`, or `no one can push to the branch "frozen"`. Test Plan: This covers a lot of ground. I created and pushed a bunch of rules, then looked at transcripts, in general. Here are some bits in detail: Here's a hook-based reject message: >>> orbital ~/repos/POEMS $ git push Counting objects: 5, done. Delta compression using up to 8 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 274 bytes, done. Total 3 (delta 2), reused 0 (delta 0) remote: +---------------------------------------------------------------+ remote: | * * * PUSH REJECTED BY EVIL DRAGON BUREAUCRATS * * * | remote: +---------------------------------------------------------------+ remote: \ remote: \ ^ /^ remote: \ / \ // \ remote: \ |\___/| / \// .\ remote: \ /V V \__ / // | \ \ *----* remote: / / \/_/ // | \ \ \ | remote: @___@` \/_ // | \ \ \/\ \ remote: 0/0/| \/_ // | \ \ \ \ remote: 0/0/0/0/| \/// | \ \ | | remote: 0/0/0/0/0/_|_ / ( // | \ _\ | / remote: 0/0/0/0/0/0/`/,_ _ _/ ) ; -. | _ _\.-~ / / remote: ,-} _ *-.|.-~-. .~ ~ remote: \ \__/ `/\ / ~-. _ .-~ / remote: \____(Oo) *. } { / remote: ( (--) .----~-.\ \-` .~ remote: //__\\ \ DENIED! ///.----..< \ _ -~ remote: // \\ ///-._ _ _ _ _ _ _{^ - - - - ~ remote: remote: remote: This commit was rejected by Herald pre-commit rule H24. remote: Rule: No Branches Called Blarp remote: Reason: "blarp" is a bad branch name remote: To ssh://dweller@localhost/diffusion/POEMS/ ! [remote rejected] blarp -> blarp (pre-receive hook declined) error: failed to push some refs to 'ssh://dweller@localhost/diffusion/POEMS/' Here's a transcript, showing that all the field values populate sensibly: {F90453} Here's a rule: {F90454} Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T4195 Differential Revision: https://secure.phabricator.com/D7782 --- src/__celerity_resource_map__.php | 2 +- src/__phutil_library_map__.php | 2 + .../engine/DiffusionCommitHookEngine.php | 89 +++++++++- .../herald/HeraldPreCommitRefAdapter.php | 163 ++++++++++++++++++ .../herald/adapter/HeraldAdapter.php | 22 +++ .../controller/HeraldRuleController.php | 39 ++++- .../controller/HeraldTranscriptController.php | 4 + .../herald/engine/HeraldEngine.php | 8 +- .../herald/storage/HeraldRule.php | 5 +- .../PhabricatorRepositoryPushLogQuery.php | 13 ++ .../js/application/herald/HeraldRuleEditor.js | 11 +- 11 files changed, 341 insertions(+), 17 deletions(-) create mode 100644 src/applications/diffusion/herald/HeraldPreCommitRefAdapter.php diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 018cd7d882..5e42569d58 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -1211,7 +1211,7 @@ celerity_register_resource_map(array( ), 'herald-rule-editor' => array( - 'uri' => '/res/928275b4/rsrc/js/application/herald/HeraldRuleEditor.js', + 'uri' => '/res/92c05b75/rsrc/js/application/herald/HeraldRuleEditor.js', 'type' => 'js', 'requires' => array( diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index bfd60ed692..5262157a91 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -756,6 +756,7 @@ phutil_register_library_map(array( 'HeraldObjectTranscript' => 'applications/herald/storage/transcript/HeraldObjectTranscript.php', 'HeraldPHIDTypeRule' => 'applications/herald/phid/HeraldPHIDTypeRule.php', 'HeraldPholioMockAdapter' => 'applications/herald/adapter/HeraldPholioMockAdapter.php', + 'HeraldPreCommitRefAdapter' => 'applications/diffusion/herald/HeraldPreCommitRefAdapter.php', 'HeraldRecursiveConditionsException' => 'applications/herald/engine/exception/HeraldRecursiveConditionsException.php', 'HeraldRepetitionPolicyConfig' => 'applications/herald/config/HeraldRepetitionPolicyConfig.php', 'HeraldRule' => 'applications/herald/storage/HeraldRule.php', @@ -3177,6 +3178,7 @@ phutil_register_library_map(array( 'HeraldNewController' => 'HeraldController', 'HeraldPHIDTypeRule' => 'PhabricatorPHIDType', 'HeraldPholioMockAdapter' => 'HeraldAdapter', + 'HeraldPreCommitRefAdapter' => 'HeraldAdapter', 'HeraldRecursiveConditionsException' => 'Exception', 'HeraldRule' => array( diff --git a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php index 0cafe3c30b..d45a9c336c 100644 --- a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php +++ b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php @@ -26,6 +26,8 @@ final class DiffusionCommitHookEngine extends Phobject { private $transactionKey; private $mercurialHook; + private $heraldViewerProjects; + /* -( Config )------------------------------------------------------------- */ @@ -130,7 +132,7 @@ final class DiffusionCommitHookEngine extends Phobject { throw $ex; } - // TODO: Fire ref herald rules. + $this->applyHeraldRefRules($ref_updates); $content_updates = $this->findContentUpdates($ref_updates); $all_updates = array_merge($all_updates, $content_updates); @@ -221,6 +223,84 @@ final class DiffusionCommitHookEngine extends Phobject { } +/* -( Herald )------------------------------------------------------------- */ + + + private function applyHeraldRefRules(array $ref_updates) { + if (!$ref_updates) { + return; + } + + $engine = new HeraldEngine(); + $rules = null; + $blocking_effect = null; + foreach ($ref_updates as $ref_update) { + $adapter = id(new HeraldPreCommitRefAdapter()) + ->setPushLog($ref_update) + ->setHookEngine($this); + + if ($rules === null) { + $rules = $engine->loadRulesForAdapter($adapter); + } + + $effects = $engine->applyRules($rules, $adapter); + $engine->applyEffects($effects, $adapter, $rules); + $xscript = $engine->getTranscript(); + + if ($blocking_effect === null) { + foreach ($effects as $effect) { + if ($effect->getAction() == HeraldAdapter::ACTION_BLOCK) { + $blocking_effect = $effect; + break; + } + } + } + } + + if ($blocking_effect) { + foreach ($ref_updates as $ref_update) { + $ref_update->setRejectCode(PhabricatorRepositoryPushLog::REJECT_HERALD); + $ref_update->setRejectDetails($blocking_effect->getRulePHID()); + } + + $message = $blocking_effect->getTarget(); + if (!strlen($message)) { + $message = pht('(None.)'); + } + + $rules = mpull($rules, null, 'getID'); + $rule = idx($rules, $effect->getRuleID()); + if ($rule && strlen($rule->getName())) { + $rule_name = $rule->getName(); + } else { + $rule_name = pht('Unnamed Herald Rule'); + } + + throw new DiffusionCommitHookRejectException( + pht( + "This commit was rejected by Herald pre-commit rule %s.\n". + "Rule: %s\n". + "Reason: %s", + 'H'.$blocking_effect->getRuleID(), + $rule_name, + $message)); + } + } + + public function loadViewerProjectPHIDsForHerald() { + // This just caches the viewer's projects so we don't need to load them + // over and over again when applying Herald rules. + if ($this->heraldViewerProjects === null) { + $this->heraldViewerProjects = id(new PhabricatorProjectQuery()) + ->setViewer($this->getViewer()) + ->withMemberPHIDs(array($this->getViewer()->getPHID())) + ->execute(); + } + + return mpull($this->heraldViewerProjects, 'getPHID'); + } + + /* -( Git )---------------------------------------------------------------- */ @@ -246,8 +326,10 @@ final class DiffusionCommitHookEngine extends Phobject { if (preg_match('(^refs/heads/)', $ref_raw)) { $ref_type = PhabricatorRepositoryPushLog::REFTYPE_BRANCH; + $ref_raw = substr($ref_raw, strlen('refs/heads/')); } else if (preg_match('(^refs/tags/)', $ref_raw)) { $ref_type = PhabricatorRepositoryPushLog::REFTYPE_TAG; + $ref_raw = substr($ref_raw, strlen('refs/tags/')); } else { throw new Exception( pht( @@ -764,7 +846,12 @@ final class DiffusionCommitHookEngine extends Phobject { // code. This indicates a broken hook, and covers the case where we // encounter some unexpected exception and consequently reject the changes. + // NOTE: We generate PHIDs up front so the Herald transcripts can pick them + // up. + $phid = id(new PhabricatorRepositoryPushLog())->generatePHID(); + return PhabricatorRepositoryPushLog::initializeNewLog($this->getViewer()) + ->setPHID($phid) ->attachRepository($this->getRepository()) ->setRepositoryPHID($this->getRepository()->getPHID()) ->setEpoch(time()) diff --git a/src/applications/diffusion/herald/HeraldPreCommitRefAdapter.php b/src/applications/diffusion/herald/HeraldPreCommitRefAdapter.php new file mode 100644 index 0000000000..adfe5951fe --- /dev/null +++ b/src/applications/diffusion/herald/HeraldPreCommitRefAdapter.php @@ -0,0 +1,163 @@ +log = $log; + return $this; + } + + public function setHookEngine(DiffusionCommitHookEngine $engine) { + $this->hookEngine = $engine; + return $this; + } + + public function getAdapterApplicationClass() { + return 'PhabricatorApplicationDiffusion'; + } + + public function getObject() { + return $this->log; + } + + public function getAdapterContentName() { + return pht('Commit Hook: Branches/Tags/Bookmarks'); + } + + public function getFieldNameMap() { + return array( + self::FIELD_REF_TYPE => pht('Ref type'), + self::FIELD_REF_NAME => pht('Ref name'), + self::FIELD_REF_CHANGE => pht('Ref change type'), + ) + parent::getFieldNameMap(); + } + + public function getFields() { + return array_merge( + array( + self::FIELD_REF_TYPE, + self::FIELD_REF_NAME, + self::FIELD_REF_CHANGE, + self::FIELD_REPOSITORY, + self::FIELD_PUSHER, + self::FIELD_PUSHER_PROJECTS, + self::FIELD_RULE, + ), + parent::getFields()); + } + + public function getConditionsForField($field) { + switch ($field) { + case self::FIELD_REF_NAME: + return array( + self::CONDITION_IS, + self::CONDITION_IS_NOT, + self::CONDITION_CONTAINS, + self::CONDITION_REGEXP, + ); + case self::FIELD_REF_TYPE: + return array( + self::CONDITION_IS, + self::CONDITION_IS_NOT, + ); + case self::FIELD_REF_CHANGE: + return array( + self::CONDITION_HAS_BIT, + self::CONDITION_NOT_BIT, + ); + } + return parent::getConditionsForField($field); + } + + public function getActions($rule_type) { + switch ($rule_type) { + case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL: + return array( + self::ACTION_BLOCK, + self::ACTION_NOTHING + ); + case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: + return array( + self::ACTION_NOTHING, + ); + } + } + + public function getValueTypeForFieldAndCondition($field, $condition) { + switch ($field) { + case self::FIELD_REF_TYPE: + return self::VALUE_REF_TYPE; + case self::FIELD_REF_CHANGE: + return self::VALUE_REF_CHANGE; + } + + return parent::getValueTypeForFieldAndCondition($field, $condition); + } + + public function getPHID() { + return $this->getObject()->getPHID(); + } + + public function getHeraldName() { + return pht('Push Log'); + } + + public function getHeraldField($field) { + $log = $this->getObject(); + switch ($field) { + case self::FIELD_REF_TYPE: + return $log->getRefType(); + case self::FIELD_REF_NAME: + return $log->getRefName(); + case self::FIELD_REF_CHANGE: + return $log->getChangeFlags(); + case self::FIELD_REPOSITORY: + return $this->hookEngine->getRepository()->getPHID(); + case self::FIELD_PUSHER: + return $this->hookEngine->getViewer()->getPHID(); + case self::FIELD_PUSHER_PROJECTS: + return $this->hookEngine->loadViewerProjectPHIDsForHerald(); + } + + return parent::getHeraldField($field); + } + + + public function applyHeraldEffects(array $effects) { + assert_instances_of($effects, 'HeraldEffect'); + + $result = array(); + foreach ($effects as $effect) { + $action = $effect->getAction(); + switch ($action) { + case self::ACTION_NOTHING: + $result[] = new HeraldApplyTranscript( + $effect, + true, + pht('Did nothing.')); + break; + case self::ACTION_BLOCK: + $result[] = new HeraldApplyTranscript( + $effect, + true, + pht('Blocked push.')); + break; + default: + throw new Exception(pht('No rules to handle action "%s"!', $action)); + } + } + + return $result; + } + +} diff --git a/src/applications/herald/adapter/HeraldAdapter.php b/src/applications/herald/adapter/HeraldAdapter.php index fb205a6867..f9167b09e6 100644 --- a/src/applications/herald/adapter/HeraldAdapter.php +++ b/src/applications/herald/adapter/HeraldAdapter.php @@ -25,6 +25,8 @@ abstract class HeraldAdapter { const FIELD_ALWAYS = 'always'; const FIELD_AUTHOR_PROJECTS = 'authorprojects'; const FIELD_PROJECTS = 'projects'; + const FIELD_PUSHER = 'pusher'; + const FIELD_PUSHER_PROJECTS = 'pusher-projects'; const CONDITION_CONTAINS = 'contains'; const CONDITION_NOT_CONTAINS = '!contains'; @@ -44,6 +46,8 @@ abstract class HeraldAdapter { const CONDITION_NOT_EXISTS = '!exists'; const CONDITION_UNCONDITIONALLY = 'unconditionally'; const CONDITION_REGEXP_PAIR = 'regexp-pair'; + const CONDITION_HAS_BIT = 'bit'; + const CONDITION_NOT_BIT = '!bit'; const ACTION_ADD_CC = 'addcc'; const ACTION_REMOVE_CC = 'remcc'; @@ -56,6 +60,7 @@ abstract class HeraldAdapter { const ACTION_ADD_REVIEWERS = 'addreviewers'; const ACTION_ADD_BLOCKING_REVIEWERS = 'addblockingreviewers'; const ACTION_APPLY_BUILD_PLANS = 'applybuildplans'; + const ACTION_BLOCK = 'block'; const VALUE_TEXT = 'text'; const VALUE_NONE = 'none'; @@ -156,6 +161,8 @@ abstract class HeraldAdapter { self::FIELD_ALWAYS => pht('Always'), self::FIELD_AUTHOR_PROJECTS => pht("Author's projects"), self::FIELD_PROJECTS => pht("Projects"), + self::FIELD_PUSHER => pht('Pusher'), + self::FIELD_PUSHER_PROJECTS => pht("Pusher's projects"), ); } @@ -183,6 +190,8 @@ abstract class HeraldAdapter { self::CONDITION_NOT_EXISTS => pht('does not exist'), self::CONDITION_UNCONDITIONALLY => '', // don't show anything! self::CONDITION_REGEXP_PAIR => pht('matches regexp pair'), + self::CONDITION_HAS_BIT => pht('has bit'), + self::CONDITION_NOT_BIT => pht('lacks bit'), ); } @@ -200,6 +209,7 @@ abstract class HeraldAdapter { case self::FIELD_AUTHOR: case self::FIELD_COMMITTER: case self::FIELD_REVIEWER: + case self::FIELD_PUSHER: return array( self::CONDITION_IS_ANY, self::CONDITION_IS_NOT_ANY, @@ -218,6 +228,7 @@ abstract class HeraldAdapter { case self::FIELD_PROJECTS: case self::FIELD_AFFECTED_PACKAGE: case self::FIELD_AFFECTED_PACKAGE_OWNER: + case self::FIELD_PUSHER_PROJECTS: return array( self::CONDITION_INCLUDE_ALL, self::CONDITION_INCLUDE_ANY, @@ -392,6 +403,10 @@ abstract class HeraldAdapter { $result = !$result; } return $result; + case self::CONDITION_HAS_BIT: + return (($condition_value & $field_value) === $condition_value); + case self::CONDITION_NOT_BIT: + return (($condition_value & $field_value) !== $condition_value); default: throw new HeraldInvalidConditionException( "Unknown condition '{$condition_type}'."); @@ -469,6 +484,8 @@ abstract class HeraldAdapter { case self::CONDITION_EXISTS: case self::CONDITION_NOT_EXISTS: case self::CONDITION_UNCONDITIONALLY: + case self::CONDITION_HAS_BIT: + case self::CONDITION_NOT_BIT: // No explicit validation for these types, although there probably // should be in some cases. break; @@ -500,6 +517,7 @@ abstract class HeraldAdapter { self::ACTION_ADD_REVIEWERS => pht('Add reviewers'), self::ACTION_ADD_BLOCKING_REVIEWERS => pht('Add blocking reviewers'), self::ACTION_APPLY_BUILD_PLANS => pht('Apply build plans'), + self::ACTION_BLOCK => pht('Block change with message'), ); case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: return array( @@ -551,6 +569,7 @@ abstract class HeraldAdapter { $target = PhabricatorFlagColor::COLOR_BLUE; } break; + case self::ACTION_BLOCK: case self::ACTION_NOTHING: break; default: @@ -607,6 +626,7 @@ abstract class HeraldAdapter { case self::FIELD_AFFECTED_PACKAGE: return self::VALUE_OWNERS_PACKAGE; case self::FIELD_AUTHOR_PROJECTS: + case self::FIELD_PUSHER_PROJECTS: case self::FIELD_PROJECTS: return self::VALUE_PROJECT; case self::FIELD_REVIEWERS: @@ -670,6 +690,8 @@ abstract class HeraldAdapter { return self::VALUE_USER_OR_PROJECT; case self::ACTION_APPLY_BUILD_PLANS: return self::VALUE_BUILD_PLAN; + case self::ACTION_BLOCK: + return self::VALUE_TEXT; default: throw new Exception("Unknown or invalid action '{$action}'."); } diff --git a/src/applications/herald/controller/HeraldRuleController.php b/src/applications/herald/controller/HeraldRuleController.php index b9e46d58a6..81810f7b96 100644 --- a/src/applications/herald/controller/HeraldRuleController.php +++ b/src/applications/herald/controller/HeraldRuleController.php @@ -349,6 +349,7 @@ final class HeraldRuleController extends HeraldController { switch ($action->getAction()) { case HeraldAdapter::ACTION_FLAG: + case HeraldAdapter::ACTION_BLOCK: $current_value = $action->getTarget(); break; default: @@ -414,12 +415,42 @@ final class HeraldRuleController extends HeraldController { 'root' => 'herald-rule-edit-form', 'conditions' => (object)$serial_conditions, 'actions' => (object)$serial_actions, + 'select' => array( + HeraldAdapter::VALUE_CONTENT_SOURCE => array( + 'options' => PhabricatorContentSource::getSourceNameMap(), + 'default' => PhabricatorContentSource::SOURCE_WEB, + ), + HeraldAdapter::VALUE_FLAG_COLOR => array( + 'options' => PhabricatorFlagColor::getColorNameMap(), + 'default' => PhabricatorFlagColor::COLOR_BLUE, + ), + HeraldPreCommitRefAdapter::VALUE_REF_TYPE => array( + 'options' => array( + PhabricatorRepositoryPushLog::REFTYPE_BRANCH + => pht('branch (git/hg)'), + PhabricatorRepositoryPushLog::REFTYPE_TAG + => pht('tag (git)'), + PhabricatorRepositoryPushLog::REFTYPE_BOOKMARK + => pht('bookmark (hg)'), + ), + 'default' => PhabricatorRepositoryPushLog::REFTYPE_BRANCH, + ), + HeraldPreCommitRefAdapter::VALUE_REF_CHANGE => array( + 'options' => array( + PhabricatorRepositoryPushLog::CHANGEFLAG_ADD => + pht('change creates ref'), + PhabricatorRepositoryPushLog::CHANGEFLAG_DELETE => + pht('change deletes ref'), + PhabricatorRepositoryPushLog::CHANGEFLAG_REWRITE => + pht('change rewrites ref'), + PhabricatorRepositoryPushLog::CHANGEFLAG_DANGEROUS => + pht('dangerous change'), + ), + 'default' => PhabricatorRepositoryPushLog::CHANGEFLAG_ADD, + ), + ), 'template' => $this->buildTokenizerTemplates() + array( 'rules' => $all_rules, - 'colors' => PhabricatorFlagColor::getColorNameMap(), - 'defaultColor' => PhabricatorFlagColor::COLOR_BLUE, - 'contentSources' => PhabricatorContentSource::getSourceNameMap(), - 'defaultSource' => PhabricatorContentSource::SOURCE_WEB ), 'author' => array($rule->getAuthorPHID() => $handles[$rule->getAuthorPHID()]->getName()), diff --git a/src/applications/herald/controller/HeraldTranscriptController.php b/src/applications/herald/controller/HeraldTranscriptController.php index e3297c0d55..82413b41e2 100644 --- a/src/applications/herald/controller/HeraldTranscriptController.php +++ b/src/applications/herald/controller/HeraldTranscriptController.php @@ -293,6 +293,10 @@ final class HeraldTranscriptController extends HeraldController { case HeraldAdapter::ACTION_FLAG: $target = PhabricatorFlagColor::getColorName($target); break; + case HeraldAdapter::ACTION_BLOCK: + // Target is a text string. + $target = $target; + break; default: if ($target) { foreach ($target as $k => $phid) { diff --git a/src/applications/herald/engine/HeraldEngine.php b/src/applications/herald/engine/HeraldEngine.php index 4ff6f034fd..04627b4eaf 100644 --- a/src/applications/herald/engine/HeraldEngine.php +++ b/src/applications/herald/engine/HeraldEngine.php @@ -24,8 +24,8 @@ final class HeraldEngine { return idx($this->rules, $id); } - public static function loadAndApplyRules(HeraldAdapter $adapter) { - $rules = id(new HeraldRuleQuery()) + public function loadRulesForAdapter(HeraldAdapter $adapter) { + return id(new HeraldRuleQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withDisabled(false) ->withContentTypes(array($adapter->getAdapterContentType())) @@ -33,8 +33,12 @@ final class HeraldEngine { ->needAppliedToPHIDs(array($adapter->getPHID())) ->needValidateAuthors(true) ->execute(); + } + public static function loadAndApplyRules(HeraldAdapter $adapter) { $engine = new HeraldEngine(); + + $rules = $engine->loadRulesForAdapter($adapter); $effects = $engine->applyRules($rules, $adapter); $engine->applyEffects($effects, $adapter, $rules); diff --git a/src/applications/herald/storage/HeraldRule.php b/src/applications/herald/storage/HeraldRule.php index 55b0bbdd46..140f57ee8d 100644 --- a/src/applications/herald/storage/HeraldRule.php +++ b/src/applications/herald/storage/HeraldRule.php @@ -16,9 +16,10 @@ final class HeraldRule extends HeraldDAO protected $ruleType; protected $isDisabled = 0; - protected $configVersion = 15; + protected $configVersion = 16; - private $ruleApplied = self::ATTACHABLE; // phids for which this rule has been applied + // phids for which this rule has been applied + private $ruleApplied = self::ATTACHABLE; private $validAuthor = self::ATTACHABLE; private $author = self::ATTACHABLE; private $conditions; diff --git a/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php b/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php index e2e640f3cc..b259b59adc 100644 --- a/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php @@ -4,6 +4,7 @@ final class PhabricatorRepositoryPushLogQuery extends PhabricatorCursorPagedPolicyAwareQuery { private $ids; + private $phids; private $repositoryPHIDs; private $pusherPHIDs; private $refTypes; @@ -14,6 +15,11 @@ final class PhabricatorRepositoryPushLogQuery return $this; } + public function withPHIDs(array $phids) { + $this->phids = $phids; + return $this; + } + public function withRepositoryPHIDs(array $repository_phids) { $this->repositoryPHIDs = $repository_phids; return $this; @@ -84,6 +90,13 @@ final class PhabricatorRepositoryPushLogQuery $this->ids); } + if ($this->phids) { + $where[] = qsprintf( + $conn_r, + 'phid IN (%Ls)', + $this->phids); + } + if ($this->repositoryPHIDs) { $where[] = qsprintf( $conn_r, diff --git a/webroot/rsrc/js/application/herald/HeraldRuleEditor.js b/webroot/rsrc/js/application/herald/HeraldRuleEditor.js index 7d8cde287e..ee3a597fb3 100644 --- a/webroot/rsrc/js/application/herald/HeraldRuleEditor.js +++ b/webroot/rsrc/js/application/herald/HeraldRuleEditor.js @@ -233,16 +233,13 @@ JX.install('HeraldRuleEditor', { set_fn = JX.bag; break; case 'contentsource': - input = this._renderSelect(this._config.template.contentSources); - get_fn = function() { return input.value; }; - set_fn = function(v) { input.value = v; }; - set_fn(this._config.template.defaultSource); - break; case 'flagcolor': - input = this._renderSelect(this._config.template.colors); + case 'value-ref-type': + case 'value-ref-change': + input = this._renderSelect(this._config.select[type].options); get_fn = function() { return input.value; }; set_fn = function(v) { input.value = v; }; - set_fn(this._config.template.defaultColor); + set_fn(this._config.select[type]['default']); break; default: input = JX.$N('input', {type: 'text'});