summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Fried <efried@us.ibm.com>2017-08-30 16:56:48 -0500
committerJohn L. Villalovos <openstack.org@sodarock.com>2018-02-17 11:35:39 -0800
commit981dfa50faf95f0248ca9490e05def3ab46a6309 (patch)
tree36ae847ea30084520b880856e9f62715e4f7a9ae
parentfc4041f6a305bd7bee76ad0700d665d87205f208 (diff)
Support regexes in channel config yaml0.4.0
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
Notes
Notes (review): Code-Review+2: Clark Boylan <cboylan@sapwetik.org> Code-Review+1: Sean McGinnis <sean.mcginnis@gmail.com> Code-Review+1: John L. Villalovos <openstack.org@sodarock.com> Code-Review+1: Jean-Philippe Evrard <jean-philippe@evrard.me> Code-Review+2: Paul Belanger <pabelanger@redhat.com> Workflow+1: Paul Belanger <pabelanger@redhat.com> Verified+2: Zuul Submitted-by: Zuul Submitted-at: Mon, 19 Feb 2018 18:31:17 +0000 Reviewed-on: https://review.openstack.org/499367 Project: openstack-infra/gerritbot Branch: refs/heads/master
-rw-r--r--doc/source/installation.rst19
-rwxr-xr-xgerritbot/bot.py80
-rw-r--r--gerritbot/tests/unit/test_bot.py26
3 files changed, 83 insertions, 42 deletions
diff --git a/doc/source/installation.rst b/doc/source/installation.rst
index 6a23604..1c4c5d7 100644
--- a/doc/source/installation.rst
+++ b/doc/source/installation.rst
@@ -19,36 +19,41 @@ when starting the bot. It should look like::
19 port=6667 19 port=6667
20 force_ssl=True or False (Defaults to False) 20 force_ssl=True or False (Defaults to False)
21 server_password=SERVERPASS 21 server_password=SERVERPASS
22 channel_config=/path/to/yaml/config 22 channel_config=/path/to/yaml/config (See below)
23 23
24 [gerrit] 24 [gerrit]
25 user=gerrit2 25 user=gerrit2
26 key=/path/to/id_rsa 26 key=/path/to/id_rsa
27 host=review.example.com 27 host=review.example.com
28 port=29418 28 port=29418
29 29
30The second configures the IRC channels and the events and projects that each 30The second, referenced by ``[ircbot]channel_config`` in the above, configures
31channel is interested in. This config file is written in yaml and should look 31the IRC channels and the events and projects that each channel is interested
32like:: 32in. This config file is written in yaml and should look like::
33 33
34 example-channel1: 34 example-channel1:
35 events: 35 events:
36 - patchset-created 36 - patchset-created
37 - change-merged 37 - change-merged
38 - ^x-(crvw|vrif)-(plus|minus)-2$
38 projects: 39 projects:
39 - example/project1 40 - example/project1
40 - example/project2 41 - example/project2
41 branches: 42 branches:
42 - master 43 - master
43 - development 44 - development
45
44 example-channel2: 46 example-channel2:
45 events: 47 events:
46 - change-merged 48 - change-merged
47 projects: 49 projects:
48 - example/project3 50 - ^example/project[34]$
49 - example/project4 51 - ^example/interesting-
50 branches: 52 branches:
51 - master 53 - master
54 - ^stable/(newton|ocata|pike)$
55
56Denote regular expressions using the prefix ``^``.
52 57
53Running 58Running
54======= 59=======
diff --git a/gerritbot/bot.py b/gerritbot/bot.py
index db3572c..fc38ac0 100755
--- a/gerritbot/bot.py
+++ b/gerritbot/bot.py
@@ -49,8 +49,10 @@ openstack-dev:
49 projects: 49 projects:
50 - openstack/nova 50 - openstack/nova
51 - openstack/swift 51 - openstack/swift
52 - ^openstack/fuel-.*
52 branches: 53 branches:
53 - master 54 - master
55 - ^stable/(newton|ocata|pike)
54""" 56"""
55 57
56import ConfigParser 58import ConfigParser
@@ -218,8 +220,8 @@ class Gerrit(threading.Thread):
218 220
219 for approval in data.get('approvals', []): 221 for approval in data.get('approvals', []):
220 if (approval['type'] == 'VRIF' and approval['value'] == '-2' 222 if (approval['type'] == 'VRIF' and approval['value'] == '-2'
221 and channel in self.channel_config.events.get( 223 and channel in self._channels_for('events',
222 'x-vrif-minus-2', set())): 224 'x-vrif-minus-2')):
223 msg = 'Verification of a change to %s failed: %s %s' % ( 225 msg = 'Verification of a change to %s failed: %s %s' % (
224 data['change']['project'], 226 data['change']['project'],
225 data['change']['subject'], 227 data['change']['subject'],
@@ -228,8 +230,8 @@ class Gerrit(threading.Thread):
228 self.ircbot.send(channel, msg) 230 self.ircbot.send(channel, msg)
229 231
230 if (approval['type'] == 'VRIF' and approval['value'] == '2' 232 if (approval['type'] == 'VRIF' and approval['value'] == '2'
231 and channel in self.channel_config.events.get( 233 and channel in self._channels_for('events',
232 'x-vrif-plus-2', set())): 234 'x-vrif-plus-2')):
233 msg = 'Verification of a change to %s succeeded: %s %s' % ( 235 msg = 'Verification of a change to %s succeeded: %s %s' % (
234 data['change']['project'], 236 data['change']['project'],
235 data['change']['subject'], 237 data['change']['subject'],
@@ -238,8 +240,8 @@ class Gerrit(threading.Thread):
238 self.ircbot.send(channel, msg) 240 self.ircbot.send(channel, msg)
239 241
240 if (approval['type'] == 'CRVW' and approval['value'] == '-2' 242 if (approval['type'] == 'CRVW' and approval['value'] == '-2'
241 and channel in self.channel_config.events.get( 243 and channel in self._channels_for('events',
242 'x-crvw-minus-2', set())): 244 'x-crvw-minus-2')):
243 msg = 'A change to %s has been rejected: %s %s' % ( 245 msg = 'A change to %s has been rejected: %s %s' % (
244 data['change']['project'], 246 data['change']['project'],
245 data['change']['subject'], 247 data['change']['subject'],
@@ -248,8 +250,8 @@ class Gerrit(threading.Thread):
248 self.ircbot.send(channel, msg) 250 self.ircbot.send(channel, msg)
249 251
250 if (approval['type'] == 'CRVW' and approval['value'] == '2' 252 if (approval['type'] == 'CRVW' and approval['value'] == '2'
251 and channel in self.channel_config.events.get( 253 and channel in self._channels_for('events',
252 'x-crvw-plus-2', set())): 254 'x-crvw-plus-2')):
253 msg = 'A change to %s has been approved: %s %s' % ( 255 msg = 'A change to %s has been approved: %s %s' % (
254 data['change']['project'], 256 data['change']['project'],
255 data['change']['subject'], 257 data['change']['subject'],
@@ -266,17 +268,63 @@ class Gerrit(threading.Thread):
266 self.log.info('Compiled Message %s: %s' % (channel, msg)) 268 self.log.info('Compiled Message %s: %s' % (channel, msg))
267 self.ircbot.send(channel, msg) 269 self.ircbot.send(channel, msg)
268 270
271 def _channels_for(self, section, datakey):
272 """Get a set of channel names for a given data value.
273
274 Finds all the channels that care about the specified datakey for a
275 given channel_config section. If the channel config key starts with
276 '^', datakey is matched by regex; otherwise it is matched by string
277 equality. For example, given input data:
278
279 openstack-dev:
280 projects:
281 - openstack/foo-bar
282
283 openstack-infra:
284 projects:
285 - ^openstack/foo-.*$
286
287 openstack-sdks:
288 projects:
289 - openstack/foo
290
291 ...the call:
292
293 _channels_for('projects', 'openstack/foo-bar')
294
295 ...will return the set:
296
297 {'#openstack-dev', '#openstack-infra'}
298
299 :param str section: The channel_config section to inspect ('projects',
300 'events', or 'branches')
301 :param str datakey: The key into the section, from the source data.
302 E.g. for section 'projects', the key would be the
303 project name (data['change']['project']).
304 """
305 ret = set()
306 for key, chanset in getattr(self.channel_config, section, {}).items():
307 for channel in chanset or set():
308 if key.startswith('^'):
309 if re.search(key, datakey):
310 ret.add(channel)
311 else:
312 if key == datakey:
313 ret.add(channel)
314 return ret
315
269 def _read(self, data): 316 def _read(self, data):
270 try: 317 try:
271 if data['type'] == 'ref-updated': 318 # We only consider event (not project/branch) filters for these.
272 channel_set = self.channel_config.events.get('ref-updated') 319 event_only_types = ('ref-updated',)
320 if data['type'] in event_only_types:
321 channel_set = self._channels_for('events', data['type'])
273 else: 322 else:
274 channel_set = (self.channel_config.projects.get( 323 channel_set = (
275 data['change']['project'], set()) & 324 self._channels_for('projects', data['change']['project']) &
276 self.channel_config.events.get( 325 self._channels_for('events', data['type']) &
277 data['type'], set()) & 326 self._channels_for('branches', data['change']['branch'])
278 self.channel_config.branches.get( 327 )
279 data['change']['branch'], set()))
280 except KeyError: 328 except KeyError:
281 # The data we care about was not present, no channels want 329 # The data we care about was not present, no channels want
282 # this event. 330 # this event.
diff --git a/gerritbot/tests/unit/test_bot.py b/gerritbot/tests/unit/test_bot.py
index b516f8b..1c14498 100644
--- a/gerritbot/tests/unit/test_bot.py
+++ b/gerritbot/tests/unit/test_bot.py
@@ -23,10 +23,7 @@ openstack-dev:
23 events: 23 events:
24 - patchset-created 24 - patchset-created
25 - change-merged 25 - change-merged
26 - x-vrif-minus-2 26 - ^x-(crvw|vrif)-(plus|minus)-2$
27 - x-vrif-plus-2
28 - x-crvw-minus-2
29 - x-crvw-plus-2
30 projects: 27 projects:
31 - openstack/nova 28 - openstack/nova
32 - openstack/swift 29 - openstack/swift
@@ -39,14 +36,9 @@ openstack-infra:
39 - change-merged 36 - change-merged
40 - comment-added 37 - comment-added
41 - ref-updated 38 - ref-updated
42 - x-vrif-minus-2 39 - ^x-(crvw|vrif)-(plus|minus)-2$
43 - x-vrif-plus-2
44 - x-crvw-minus-2
45 - x-crvw-plus-2
46 projects: 40 projects:
47 - openstack/gerritbot 41 - ^openstack/
48 - openstack/nova
49 - openstack/swift
50 branches: 42 branches:
51 - master 43 - master
52 - stable/queens 44 - stable/queens
@@ -80,21 +72,17 @@ class ChannelConfigTestCase(testtools.TestCase):
80 'comment-added': {'#openstack-infra'}, 72 'comment-added': {'#openstack-infra'},
81 'patchset-created': expected_channels, 73 'patchset-created': expected_channels,
82 'ref-updated': {'#openstack-infra'}, 74 'ref-updated': {'#openstack-infra'},
83 'x-crvw-minus-2': expected_channels, 75 '^x-(crvw|vrif)-(plus|minus)-2$': expected_channels,
84 'x-crvw-plus-2': expected_channels,
85 'x-vrif-minus-2': expected_channels,
86 'x-vrif-plus-2': expected_channels,
87 }, 76 },
88 channel_config.events) 77 channel_config.events)
89 78
90 def test_projects(self): 79 def test_projects(self):
91 channel_config = bot.ChannelConfig(yaml.load(CHANNEL_CONFIG_YAML)) 80 channel_config = bot.ChannelConfig(yaml.load(CHANNEL_CONFIG_YAML))
92 expected_channels = {'#openstack-dev', '#openstack-infra'}
93 self.assertEqual( 81 self.assertEqual(
94 { 82 {
95 'openstack/gerritbot': {'#openstack-infra'}, 83 '^openstack/': {'#openstack-infra'},
96 'openstack/nova': expected_channels, 84 'openstack/nova': {'#openstack-dev'},
97 'openstack/swift': expected_channels, 85 'openstack/swift': {'#openstack-dev'},
98 }, 86 },
99 channel_config.projects) 87 channel_config.projects)
100 88