Support regexes in channel config yaml

With this change, the channel config yaml file can now be configured to
support regular expressions.

Any value in any section may be prefixed with `^` to denote that it is
to be treated as a regular expression [1].  Start and end ^anchors$ are
implicit (so add `.*` if needed).

For example, given the following paragraph in the channel config yaml:

openstack-foo:
  events:
    - patchset-created
    - change-merged
  projects:
    - openstack/foo
    - ^openstack/foo-.*
    - openstack/oslo.foo
  branches:
    - master
    - ^stable/(newton|ocata|pike)

...messages will be posted to #openstack-foo for events coming in from
project openstack/foo, openstack/foo-one, openstack/foo-bar, etc.; on
branches master, stable/newton, stable/ocata, or stable/pike.

Behavior is unchanged for values not prefixed with `^`.

[1] This paradigm cribbed from gerrit's search functionality:
https://review.openstack.org/Documentation/user-search.html#path

Change-Id: I97cb8faa72600bd1bd9792bb6bb59a3b652ec389
This commit is contained in:
Eric Fried 2017-08-30 16:56:48 -05:00 committed by John L. Villalovos
parent fc4041f6a3
commit 981dfa50fa
3 changed files with 83 additions and 42 deletions

View File

@ -19,36 +19,41 @@ when starting the bot. It should look like::
port=6667
force_ssl=True or False (Defaults to False)
server_password=SERVERPASS
channel_config=/path/to/yaml/config
channel_config=/path/to/yaml/config (See below)
[gerrit]
user=gerrit2
key=/path/to/id_rsa
host=review.example.com
port=29418
The second configures the IRC channels and the events and projects that each
channel is interested in. This config file is written in yaml and should look
like::
The second, referenced by ``[ircbot]channel_config`` in the above, configures
the IRC channels and the events and projects that each channel is interested
in. This config file is written in yaml and should look like::
example-channel1:
events:
- patchset-created
- change-merged
- ^x-(crvw|vrif)-(plus|minus)-2$
projects:
- example/project1
- example/project2
branches:
- master
- development
example-channel2:
events:
- change-merged
projects:
- example/project3
- example/project4
- ^example/project[34]$
- ^example/interesting-
branches:
- master
- ^stable/(newton|ocata|pike)$
Denote regular expressions using the prefix ``^``.
Running
=======

View File

@ -49,8 +49,10 @@ openstack-dev:
projects:
- openstack/nova
- openstack/swift
- ^openstack/fuel-.*
branches:
- master
- ^stable/(newton|ocata|pike)
"""
import ConfigParser
@ -218,8 +220,8 @@ class Gerrit(threading.Thread):
for approval in data.get('approvals', []):
if (approval['type'] == 'VRIF' and approval['value'] == '-2'
and channel in self.channel_config.events.get(
'x-vrif-minus-2', set())):
and channel in self._channels_for('events',
'x-vrif-minus-2')):
msg = 'Verification of a change to %s failed: %s %s' % (
data['change']['project'],
data['change']['subject'],
@ -228,8 +230,8 @@ class Gerrit(threading.Thread):
self.ircbot.send(channel, msg)
if (approval['type'] == 'VRIF' and approval['value'] == '2'
and channel in self.channel_config.events.get(
'x-vrif-plus-2', set())):
and channel in self._channels_for('events',
'x-vrif-plus-2')):
msg = 'Verification of a change to %s succeeded: %s %s' % (
data['change']['project'],
data['change']['subject'],
@ -238,8 +240,8 @@ class Gerrit(threading.Thread):
self.ircbot.send(channel, msg)
if (approval['type'] == 'CRVW' and approval['value'] == '-2'
and channel in self.channel_config.events.get(
'x-crvw-minus-2', set())):
and channel in self._channels_for('events',
'x-crvw-minus-2')):
msg = 'A change to %s has been rejected: %s %s' % (
data['change']['project'],
data['change']['subject'],
@ -248,8 +250,8 @@ class Gerrit(threading.Thread):
self.ircbot.send(channel, msg)
if (approval['type'] == 'CRVW' and approval['value'] == '2'
and channel in self.channel_config.events.get(
'x-crvw-plus-2', set())):
and channel in self._channels_for('events',
'x-crvw-plus-2')):
msg = 'A change to %s has been approved: %s %s' % (
data['change']['project'],
data['change']['subject'],
@ -266,17 +268,63 @@ class Gerrit(threading.Thread):
self.log.info('Compiled Message %s: %s' % (channel, msg))
self.ircbot.send(channel, msg)
def _channels_for(self, section, datakey):
"""Get a set of channel names for a given data value.
Finds all the channels that care about the specified datakey for a
given channel_config section. If the channel config key starts with
'^', datakey is matched by regex; otherwise it is matched by string
equality. For example, given input data:
openstack-dev:
projects:
- openstack/foo-bar
openstack-infra:
projects:
- ^openstack/foo-.*$
openstack-sdks:
projects:
- openstack/foo
...the call:
_channels_for('projects', 'openstack/foo-bar')
...will return the set:
{'#openstack-dev', '#openstack-infra'}
:param str section: The channel_config section to inspect ('projects',
'events', or 'branches')
:param str datakey: The key into the section, from the source data.
E.g. for section 'projects', the key would be the
project name (data['change']['project']).
"""
ret = set()
for key, chanset in getattr(self.channel_config, section, {}).items():
for channel in chanset or set():
if key.startswith('^'):
if re.search(key, datakey):
ret.add(channel)
else:
if key == datakey:
ret.add(channel)
return ret
def _read(self, data):
try:
if data['type'] == 'ref-updated':
channel_set = self.channel_config.events.get('ref-updated')
# We only consider event (not project/branch) filters for these.
event_only_types = ('ref-updated',)
if data['type'] in event_only_types:
channel_set = self._channels_for('events', data['type'])
else:
channel_set = (self.channel_config.projects.get(
data['change']['project'], set()) &
self.channel_config.events.get(
data['type'], set()) &
self.channel_config.branches.get(
data['change']['branch'], set()))
channel_set = (
self._channels_for('projects', data['change']['project']) &
self._channels_for('events', data['type']) &
self._channels_for('branches', data['change']['branch'])
)
except KeyError:
# The data we care about was not present, no channels want
# this event.

View File

@ -23,10 +23,7 @@ openstack-dev:
events:
- patchset-created
- change-merged
- x-vrif-minus-2
- x-vrif-plus-2
- x-crvw-minus-2
- x-crvw-plus-2
- ^x-(crvw|vrif)-(plus|minus)-2$
projects:
- openstack/nova
- openstack/swift
@ -39,14 +36,9 @@ openstack-infra:
- change-merged
- comment-added
- ref-updated
- x-vrif-minus-2
- x-vrif-plus-2
- x-crvw-minus-2
- x-crvw-plus-2
- ^x-(crvw|vrif)-(plus|minus)-2$
projects:
- openstack/gerritbot
- openstack/nova
- openstack/swift
- ^openstack/
branches:
- master
- stable/queens
@ -80,21 +72,17 @@ class ChannelConfigTestCase(testtools.TestCase):
'comment-added': {'#openstack-infra'},
'patchset-created': expected_channels,
'ref-updated': {'#openstack-infra'},
'x-crvw-minus-2': expected_channels,
'x-crvw-plus-2': expected_channels,
'x-vrif-minus-2': expected_channels,
'x-vrif-plus-2': expected_channels,
'^x-(crvw|vrif)-(plus|minus)-2$': expected_channels,
},
channel_config.events)
def test_projects(self):
channel_config = bot.ChannelConfig(yaml.load(CHANNEL_CONFIG_YAML))
expected_channels = {'#openstack-dev', '#openstack-infra'}
self.assertEqual(
{
'openstack/gerritbot': {'#openstack-infra'},
'openstack/nova': expected_channels,
'openstack/swift': expected_channels,
'^openstack/': {'#openstack-infra'},
'openstack/nova': {'#openstack-dev'},
'openstack/swift': {'#openstack-dev'},
},
channel_config.projects)