276 lines
11 KiB
Python
276 lines
11 KiB
Python
# Copyright 2015 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 vs
|
|
from zuul.trigger import BaseTrigger
|
|
from zuul.driver.github.githubmodel import GithubEventFilter
|
|
from zuul.driver.github import githubsource
|
|
from zuul.driver.util import scalar_or_list, to_list, make_regex, ZUUL_REGEX
|
|
from zuul.configloader import DeprecationWarning
|
|
|
|
|
|
class GithubUnlabelDeprecation(DeprecationWarning):
|
|
zuul_error_name = 'Github unlabel Deprecation'
|
|
zuul_error_message = """The 'unlabel' trigger attribute
|
|
is deprecated. Use 'label' instead."""
|
|
|
|
|
|
class GithubEventListDeprecation(DeprecationWarning):
|
|
zuul_error_name = 'Github event list Deprecation'
|
|
zuul_error_message = """Specifying the 'event' trigger attribute
|
|
as a list is deprecated. Use a single item instead."""
|
|
|
|
|
|
class GithubRequireStatusDeprecation(DeprecationWarning):
|
|
zuul_error_name = 'Github require-status Deprecation'
|
|
zuul_error_message = """The 'require-status' trigger attribute
|
|
is deprecated. Use 'require' instead."""
|
|
|
|
|
|
class GithubRequestedActionDeprecation(DeprecationWarning):
|
|
zuul_error_name = 'Github requested action Deprecation'
|
|
zuul_error_message = """The 'requested' value for the 'action'
|
|
trigger attribute is deprecated. Use 'rerequested' instead."""
|
|
|
|
|
|
class GithubTriggerConfigurationWarning(DeprecationWarning):
|
|
zuul_error_name = 'Github Trigger Configuration Warning'
|
|
|
|
|
|
class GithubTrigger(BaseTrigger):
|
|
name = 'github'
|
|
log = logging.getLogger("zuul.trigger.GithubTrigger")
|
|
|
|
def getEventFilters(self, connection_name, trigger_config,
|
|
parse_context):
|
|
efilters = []
|
|
pcontext = parse_context
|
|
new_schema = getNewSchema()
|
|
|
|
for trigger in to_list(trigger_config):
|
|
|
|
# Deprecated in 8.3.0 but warning added in 10.0
|
|
if 'require-status' in trigger:
|
|
with pcontext.confAttr(trigger, 'require-status'):
|
|
pcontext.accumulator.addError(
|
|
GithubRequireStatusDeprecation())
|
|
# Deprecated with warning in 10.0
|
|
if 'unlabel' in trigger:
|
|
with pcontext.confAttr(trigger, 'unlabel'):
|
|
pcontext.accumulator.addError(
|
|
GithubUnlabelDeprecation())
|
|
# Deprecated with warning in 10.0
|
|
if isinstance(trigger.get('event', None), list):
|
|
with pcontext.confAttr(trigger, 'event'):
|
|
pcontext.accumulator.addError(
|
|
GithubEventListDeprecation())
|
|
|
|
try:
|
|
new_schema(trigger)
|
|
except vs.Invalid as e:
|
|
pcontext.accumulator.addError(
|
|
GithubTriggerConfigurationWarning(
|
|
"The trigger configuration as supplied "
|
|
f"has an error: {e}"))
|
|
|
|
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, 'comment') as attr:
|
|
comments = [make_regex(x, pcontext)
|
|
for x in to_list(attr)]
|
|
|
|
# This is a compatibility layer to map the action 'requested' back
|
|
# to the original action 'rerequested'.
|
|
# TODO: Remove after zuul 5.0
|
|
# Note the original backwards compat handling for this did
|
|
# not allow 'requested' as a list, so we don't do that
|
|
# here either.
|
|
if trigger.get('action') == 'requested':
|
|
trigger['action'] = 'rerequested'
|
|
with pcontext.confAttr(trigger, 'action'):
|
|
pcontext.accumulator.addError(
|
|
GithubRequestedActionDeprecation())
|
|
|
|
f = GithubEventFilter(
|
|
connection_name=connection_name,
|
|
trigger=self,
|
|
types=types,
|
|
actions=to_list(trigger.get('action')),
|
|
branches=branches,
|
|
refs=refs,
|
|
comments=comments,
|
|
check_runs=to_list(trigger.get('check')),
|
|
labels=to_list(trigger.get('label')),
|
|
unlabels=to_list(trigger.get('unlabel')),
|
|
states=to_list(trigger.get('state')),
|
|
statuses=to_list(trigger.get('status')),
|
|
required_statuses=to_list(trigger.get('require-status')),
|
|
require=trigger.get('require'),
|
|
reject=trigger.get('reject'),
|
|
)
|
|
efilters.append(f)
|
|
|
|
return efilters
|
|
|
|
def onPullRequest(self, payload):
|
|
pass
|
|
|
|
|
|
def getNewSchema():
|
|
# For now, this is only used to raise deprecation errors if the
|
|
# syntax does not match. This was added in 10.0. When we're
|
|
# ready to enforce this, rename this method getSchema.
|
|
base_schema = vs.Schema({
|
|
vs.Required('event'): vs.Any('pull_request',
|
|
'pull_request_review',
|
|
'push',
|
|
'check_run'),
|
|
'require': githubsource.getRequireSchema(),
|
|
'reject': githubsource.getRejectSchema(),
|
|
})
|
|
|
|
# Pull request
|
|
pull_request_base_schema = base_schema.extend({
|
|
vs.Required('event'): 'pull_request',
|
|
'branch': scalar_or_list(vs.Any(ZUUL_REGEX, str)),
|
|
})
|
|
|
|
pull_request_default_schema = pull_request_base_schema.extend({
|
|
'action': scalar_or_list(vs.Any(
|
|
'opened', 'changed', 'closed', 'reopened')),
|
|
})
|
|
|
|
pull_request_comment_schema = pull_request_base_schema.extend({
|
|
vs.Required('action'): 'comment',
|
|
'comment': scalar_or_list(vs.Any(ZUUL_REGEX, str)),
|
|
})
|
|
|
|
pull_request_labeled_schema = pull_request_base_schema.extend({
|
|
vs.Required('action'): scalar_or_list(vs.Any('labeled', 'unlabeled')),
|
|
'label': scalar_or_list(str),
|
|
})
|
|
|
|
pull_request_status_schema = pull_request_base_schema.extend({
|
|
vs.Required('action'): 'status',
|
|
'status': scalar_or_list(str),
|
|
})
|
|
|
|
# Pull request review
|
|
pull_request_review_base_schema = base_schema.extend({
|
|
vs.Required('event'): 'pull_request_review',
|
|
'branch': scalar_or_list(vs.Any(ZUUL_REGEX, str)),
|
|
})
|
|
|
|
# The docs for this are at:
|
|
# https://docs.github.com/en/webhooks/webhook-events-and-payloads?actionType=dismissed#pull_request_review
|
|
# They specify "dismissed, approved, changes_requested"
|
|
# But we have also experimentally seen "commented"
|
|
# The graphql docs are at:
|
|
# https://docs.github.com/en/enterprise-server@3.12/graphql/reference/enums#pullrequestreviewstate
|
|
# And they additionally specify "commented" and "pending".
|
|
# It's unclear whether "pending" can show up in the webhook (it
|
|
# may only appear in a graphql query of a user's own unsaved
|
|
# reviews) but since we've seen the extra "commented" value, let's
|
|
# be generous and accept "pending" as well.
|
|
pull_request_review_schema = pull_request_review_base_schema.extend({
|
|
'action': scalar_or_list(vs.Any('submitted', 'dismissed')),
|
|
'state': scalar_or_list(vs.Any(
|
|
'approved', 'commented', 'changes_requested',
|
|
'dismissed', 'pending')),
|
|
})
|
|
|
|
# Push
|
|
push_schema = base_schema.extend({
|
|
vs.Required('event'): 'push',
|
|
'ref': scalar_or_list(vs.Any(ZUUL_REGEX, str)),
|
|
})
|
|
|
|
# Check run
|
|
check_run_schema = base_schema.extend({
|
|
vs.Required('event'): 'check_run',
|
|
# The 'requested' action is deprecated, but we have a separate,
|
|
# more specific deprecation for this.
|
|
'action': scalar_or_list(vs.Any('rerequested', 'completed')),
|
|
'check': scalar_or_list(str),
|
|
})
|
|
|
|
init_schema = base_schema.extend({}, extra=vs.ALLOW_EXTRA)
|
|
|
|
def validate(data):
|
|
# This method could be a simple vs.Any() across all the
|
|
# possibilities, but sometimes the error messages are less
|
|
# intuitive (e.g., indicating that the action is wrong rather
|
|
# than that a user has added an attribute that is incompatible
|
|
# with the action). Instead, we assume the user got the event
|
|
# and/or action right first, then apply the appropriate schema
|
|
# for what they selected.
|
|
|
|
event = data.get('event')
|
|
# TODO: run this unconditionally after
|
|
# GithubEventListDeprecation is complete
|
|
if isinstance(event, str):
|
|
# First, make sure the common items are correct.
|
|
init_schema(data)
|
|
action = data.get('action')
|
|
if event == 'pull_request':
|
|
if action == 'comment':
|
|
pull_request_comment_schema(data)
|
|
elif action in ('labeled', 'unlabeled',):
|
|
pull_request_labeled_schema(data)
|
|
elif action == 'status':
|
|
pull_request_status_schema(data)
|
|
else:
|
|
pull_request_default_schema(data)
|
|
elif event == 'pull_request_review':
|
|
pull_request_review_schema(data)
|
|
elif event == 'push':
|
|
push_schema(data)
|
|
elif event == 'check_run':
|
|
check_run_schema(data)
|
|
|
|
return validate
|
|
|
|
|
|
def getSchema():
|
|
old_schema = vs.Schema({
|
|
vs.Required('event'):
|
|
scalar_or_list(vs.Any('pull_request',
|
|
'pull_request_review',
|
|
'push',
|
|
'check_run')),
|
|
'action': scalar_or_list(str),
|
|
'branch': scalar_or_list(vs.Any(ZUUL_REGEX, str)),
|
|
'ref': scalar_or_list(vs.Any(ZUUL_REGEX, str)),
|
|
'comment': scalar_or_list(vs.Any(ZUUL_REGEX, str)),
|
|
'label': scalar_or_list(str),
|
|
'unlabel': scalar_or_list(str),
|
|
'state': scalar_or_list(str),
|
|
'require-status': scalar_or_list(str),
|
|
'require': githubsource.getRequireSchema(),
|
|
'reject': githubsource.getRejectSchema(),
|
|
'status': scalar_or_list(str),
|
|
'check': scalar_or_list(str),
|
|
})
|
|
|
|
return old_schema
|