Page MenuHomePhabricator

D7782.diff
No OneTemporary

D7782.diff

Index: src/__celerity_resource_map__.php
===================================================================
--- src/__celerity_resource_map__.php
+++ src/__celerity_resource_map__.php
@@ -1211,7 +1211,7 @@
),
'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(
Index: src/__phutil_library_map__.php
===================================================================
--- src/__phutil_library_map__.php
+++ src/__phutil_library_map__.php
@@ -756,6 +756,7 @@
'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 @@
'HeraldNewController' => 'HeraldController',
'HeraldPHIDTypeRule' => 'PhabricatorPHIDType',
'HeraldPholioMockAdapter' => 'HeraldAdapter',
+ 'HeraldPreCommitRefAdapter' => 'HeraldAdapter',
'HeraldRecursiveConditionsException' => 'Exception',
'HeraldRule' =>
array(
Index: src/applications/diffusion/engine/DiffusionCommitHookEngine.php
===================================================================
--- src/applications/diffusion/engine/DiffusionCommitHookEngine.php
+++ src/applications/diffusion/engine/DiffusionCommitHookEngine.php
@@ -26,6 +26,8 @@
private $transactionKey;
private $mercurialHook;
+ private $heraldViewerProjects;
+
/* -( Config )------------------------------------------------------------- */
@@ -130,7 +132,7 @@
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 @@
}
+/* -( 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 @@
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 @@
// 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())
Index: src/applications/diffusion/herald/HeraldPreCommitRefAdapter.php
===================================================================
--- /dev/null
+++ src/applications/diffusion/herald/HeraldPreCommitRefAdapter.php
@@ -0,0 +1,163 @@
+<?php
+
+final class HeraldPreCommitRefAdapter extends HeraldAdapter {
+
+ private $log;
+ private $hookEngine;
+
+ const FIELD_REF_TYPE = 'ref-type';
+ const FIELD_REF_NAME = 'ref-name';
+ const FIELD_REF_CHANGE = 'ref-change';
+
+ const VALUE_REF_TYPE = 'value-ref-type';
+ const VALUE_REF_CHANGE = 'value-ref-change';
+
+ public function setPushLog(PhabricatorRepositoryPushLog $log) {
+ $this->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;
+ }
+
+}
Index: src/applications/herald/adapter/HeraldAdapter.php
===================================================================
--- src/applications/herald/adapter/HeraldAdapter.php
+++ src/applications/herald/adapter/HeraldAdapter.php
@@ -25,6 +25,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
$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 @@
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 @@
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 @@
$target = PhabricatorFlagColor::COLOR_BLUE;
}
break;
+ case self::ACTION_BLOCK:
case self::ACTION_NOTHING:
break;
default:
@@ -607,6 +626,7 @@
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 @@
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}'.");
}
Index: src/applications/herald/controller/HeraldRuleController.php
===================================================================
--- src/applications/herald/controller/HeraldRuleController.php
+++ src/applications/herald/controller/HeraldRuleController.php
@@ -349,6 +349,7 @@
switch ($action->getAction()) {
case HeraldAdapter::ACTION_FLAG:
+ case HeraldAdapter::ACTION_BLOCK:
$current_value = $action->getTarget();
break;
default:
@@ -414,12 +415,42 @@
'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()),
Index: src/applications/herald/controller/HeraldTranscriptController.php
===================================================================
--- src/applications/herald/controller/HeraldTranscriptController.php
+++ src/applications/herald/controller/HeraldTranscriptController.php
@@ -293,6 +293,10 @@
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) {
Index: src/applications/herald/engine/HeraldEngine.php
===================================================================
--- src/applications/herald/engine/HeraldEngine.php
+++ src/applications/herald/engine/HeraldEngine.php
@@ -24,8 +24,8 @@
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 @@
->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);
Index: src/applications/herald/storage/HeraldRule.php
===================================================================
--- src/applications/herald/storage/HeraldRule.php
+++ src/applications/herald/storage/HeraldRule.php
@@ -16,9 +16,10 @@
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;
Index: src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php
===================================================================
--- src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php
+++ src/applications/repository/query/PhabricatorRepositoryPushLogQuery.php
@@ -4,6 +4,7 @@
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
+ private $phids;
private $repositoryPHIDs;
private $pusherPHIDs;
private $refTypes;
@@ -14,6 +15,11 @@
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 @@
$this->ids);
}
+ if ($this->phids) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'phid IN (%Ls)',
+ $this->phids);
+ }
+
if ($this->repositoryPHIDs) {
$where[] = qsprintf(
$conn_r,
Index: webroot/rsrc/js/application/herald/HeraldRuleEditor.js
===================================================================
--- webroot/rsrc/js/application/herald/HeraldRuleEditor.js
+++ webroot/rsrc/js/application/herald/HeraldRuleEditor.js
@@ -233,16 +233,13 @@
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'});

File Metadata

Mime Type
text/plain
Expires
Sat, Jun 15, 11:02 PM (1 w, 3 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6273708
Default Alt Text
D7782.diff (21 KB)

Event Timeline