Files
zuul/zuul/driver/gerrit/gerrittrigger.py
Joshua Watt ffb615e6c7 gerrit: Add approval-change trigger
Adds a new type of trigger to the Gerrit driver that only triggers if
the approval value was changed by the user in the comment. This is
useful if Zuul is configured to allow many different scores to trigger a
pipeline (with an additional requirement on all of them), but arbitrary
comments made while the scores are present should _not_ trigger (or
potentially re-trigger) the pipeline. This can happen because Gerrit
sends all approvals by a user on all comments, regardless of if they
were changed by the comment.

The new `approval-change` trigger requirement inspects the `oldValue`
field in the Gerrit event. The pipeline will only trigger if this value
is present and not equal to the new approval value (thus, only when the
user actually changed it).

`oldValue` has been present since at least Gerrit 3.4

Change-Id: I88cf840ae8b4e63c77f10ee68b6901e85f7c5fb1
2024-05-03 15:39:46 -07:00

171 lines
6.9 KiB
Python

# Copyright 2012 Hewlett-Packard Development Company, L.P.
# Copyright 2023 Acme Gating, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
import voluptuous as v
from zuul.trigger import BaseTrigger
from zuul.driver.gerrit.gerritmodel import GerritEventFilter
from zuul.driver.gerrit import gerritsource
from zuul.driver.util import (
scalar_or_list,
to_list,
make_regex,
ZUUL_REGEX,
)
from zuul.exceptions import DeprecationWarning
class GerritRequireApprovalDeprecation(DeprecationWarning):
zuul_error_name = 'Gerrit require-approval Deprecation'
zuul_error_message = """The 'require-approval' trigger attribute
is deprecated. Use 'require' instead."""
class GerritRejectApprovalDeprecation(DeprecationWarning):
zuul_error_name = 'Gerrit reject-approval Deprecation'
zuul_error_message = """The 'reject-approval' trigger attribute
is deprecated. Use 'reject' instead."""
class GerritTrigger(BaseTrigger):
name = 'gerrit'
log = logging.getLogger("zuul.GerritTrigger")
def getEventFilters(self, connection_name, trigger_conf,
parse_context):
pcontext = parse_context
efilters = []
for trigger in to_list(trigger_conf):
approvals = {}
for approval_dict in to_list(trigger.get('approval')):
for key, val in approval_dict.items():
approvals[key] = val
approval_changes = {}
for approval_dict in to_list(trigger.get('approval-change')):
for key, val in approval_dict.items():
approval_changes[key] = val
# Backwards compat for *_filter versions of these args
attrname = 'comment' if 'comment' in trigger else 'comment_filter'
with pcontext.confAttr(trigger, attrname) as attr:
comments = [make_regex(x, pcontext)
for x in to_list(attr)]
attrname = 'email' if 'email' in trigger else 'email_filter'
with pcontext.confAttr(trigger, attrname) as attr:
emails = [make_regex(x, pcontext)
for x in to_list(attr)]
attrname = ('username' if 'username' in trigger
else 'username_filter')
with pcontext.confAttr(trigger, attrname) as attr:
usernames = [make_regex(x, pcontext)
for x in to_list(attr)]
with pcontext.confAttr(trigger, 'event') as attr:
types = [make_regex(x, pcontext) for x in to_list(attr)]
with pcontext.confAttr(trigger, 'branch') as attr:
branches = [make_regex(x, pcontext) for x in to_list(attr)]
with pcontext.confAttr(trigger, 'ref') as attr:
refs = [make_regex(x, pcontext) for x in to_list(attr)]
with pcontext.confAttr(trigger, 'added') as attr:
added = [make_regex(x, pcontext) for x in to_list(attr)]
with pcontext.confAttr(trigger, 'removed') as attr:
removed = [make_regex(x, pcontext) for x in to_list(attr)]
ignore_deletes = trigger.get('ignore-deletes', True)
if 'require-approval' in trigger:
with pcontext.confAttr(trigger, 'require-approval'):
pcontext.accumulator.addError(
GerritRequireApprovalDeprecation())
if 'reject-approval' in trigger:
with pcontext.confAttr(trigger, 'reject-approval'):
pcontext.accumulator.addError(
GerritRejectApprovalDeprecation())
f = GerritEventFilter(
connection_name=connection_name,
trigger=self,
types=types,
branches=branches,
refs=refs,
event_approvals=approvals,
event_approval_changes=approval_changes,
comments=comments,
emails=emails,
usernames=usernames,
required_approvals=(
to_list(trigger.get('require-approval'))
),
reject_approvals=to_list(
trigger.get('reject-approval')
),
added=added,
removed=removed,
uuid=trigger.get('uuid'),
scheme=trigger.get('scheme'),
ignore_deletes=ignore_deletes,
require=trigger.get('require'),
reject=trigger.get('reject'),
parse_context=parse_context,
)
efilters.append(f)
return efilters
def getSchema():
variable_dict = v.Schema(dict)
approval = v.Schema({'username': str,
'email': str,
'older-than': str,
'newer-than': str,
}, extra=v.ALLOW_EXTRA)
gerrit_trigger = {
v.Required('event'):
scalar_or_list(v.Any('patchset-created',
'draft-published',
'change-abandoned',
'change-restored',
'change-merged',
'comment-added',
'ref-updated',
'pending-check',
'vote-deleted',
'hashtags-changed',
'wip-state-changed')),
'uuid': str,
'scheme': str,
'comment_filter': scalar_or_list(str),
'comment': scalar_or_list(v.Any(ZUUL_REGEX, str)),
'email_filter': scalar_or_list(str),
'email': scalar_or_list(v.Any(ZUUL_REGEX, str)),
'username_filter': scalar_or_list(str),
'username': scalar_or_list(v.Any(ZUUL_REGEX, str)),
'branch': scalar_or_list(v.Any(ZUUL_REGEX, str)),
'ref': scalar_or_list(v.Any(ZUUL_REGEX, str)),
'ignore-deletes': bool,
'approval': scalar_or_list(variable_dict),
'approval-change': scalar_or_list(variable_dict),
'require-approval': scalar_or_list(approval),
'reject-approval': scalar_or_list(approval),
'added': scalar_or_list(v.Any(ZUUL_REGEX, str)),
'removed': scalar_or_list(v.Any(ZUUL_REGEX, str)),
'require': gerritsource.getRequireSchema(),
'reject': gerritsource.getRejectSchema(),
}
return gerrit_trigger