Merge "Pagure - handle Pull Request tags (labels) metadata"
This commit is contained in:
commit
68ffec0668
|
@ -109,6 +109,10 @@ the following options.
|
|||
|
||||
Status set on pull request.
|
||||
|
||||
.. value:: tagged
|
||||
|
||||
Tag metadata set on pull request.
|
||||
|
||||
A :value:`pipeline.trigger.<pagure
|
||||
source>.event.pg_pull_request_review` event will have associated
|
||||
action(s) to trigger from. The supported actions are:
|
||||
|
@ -123,7 +127,7 @@ the following options.
|
|||
|
||||
.. attr:: comment
|
||||
|
||||
This is only used for ``pg_pull_request`` ``comment`` actions. It
|
||||
This is only used for ``pg_pull_request`` and ``comment`` actions. It
|
||||
accepts a list of regexes that are searched for in the comment
|
||||
string. If any of these regexes matches a portion of the comment
|
||||
string the trigger is matched. ``comment: retrigger`` will
|
||||
|
@ -137,6 +141,12 @@ the following options.
|
|||
the status, the status context, and the status itself in the
|
||||
format of ``status``. For example, ``success`` or ``failure``.
|
||||
|
||||
.. attr:: tag
|
||||
|
||||
This is used for ``pg_pull_request`` and ``tagged`` actions. It
|
||||
accepts a list of strings and if one of them is part of the
|
||||
event tags metadata then the trigger is matched.
|
||||
|
||||
.. attr:: ref
|
||||
|
||||
This is only used for ``pg_push`` events. This field is treated as
|
||||
|
@ -199,6 +209,8 @@ configuration such as the following:
|
|||
score: 1
|
||||
merged: false
|
||||
status: success
|
||||
tags:
|
||||
- gateit
|
||||
|
||||
This indicates that changes originating from the Pagure connection
|
||||
must have a score of *1*, a CI status *success* and not being already merged.
|
||||
|
@ -226,6 +238,11 @@ must have a score of *1*, a CI status *success* and not being already merged.
|
|||
A boolean value (``true`` or ``false``) that indicates whether
|
||||
the Pull Request must be open or closed in order to be enqueued.
|
||||
|
||||
.. attr:: tags
|
||||
|
||||
if present, the list of tags a Pull Request must have.
|
||||
|
||||
|
||||
Reference pipelines configuration
|
||||
---------------------------------
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
pagure.io:
|
||||
score: 1
|
||||
merged: False
|
||||
tags: gateit
|
||||
status: success
|
||||
sqlreporter:
|
||||
trigger:
|
||||
|
@ -44,6 +45,9 @@
|
|||
status: success
|
||||
- event: pg_pull_request_review
|
||||
action: thumbsup
|
||||
- event: pg_pull_request
|
||||
action: tagged
|
||||
tag: gateit
|
||||
start:
|
||||
pagure.io:
|
||||
status: 'pending'
|
||||
|
|
|
@ -1108,6 +1108,7 @@ class FakePagurePullRequest(object):
|
|||
self.comments = []
|
||||
self.flags = []
|
||||
self.files = {}
|
||||
self.tags = []
|
||||
self.cached_merge_status = ''
|
||||
self.threshold_reached = False
|
||||
self.commit_stop = None
|
||||
|
@ -1122,16 +1123,17 @@ class FakePagurePullRequest(object):
|
|||
self._addCommitInPR(files=files)
|
||||
self._updateTimeStamp()
|
||||
|
||||
def _getPullRequestEvent(self, action):
|
||||
def _getPullRequestEvent(self, action, pull_data_field='pullrequest'):
|
||||
name = 'pg_pull_request'
|
||||
data = {
|
||||
'msg': {
|
||||
'pullrequest': {
|
||||
pull_data_field: {
|
||||
'branch': self.branch,
|
||||
'comments': self.comments,
|
||||
'commit_start': self.commit_start,
|
||||
'commit_stop': self.commit_stop,
|
||||
'date_created': '0',
|
||||
'tags': self.tags,
|
||||
'initial_comment': self.initial_comment,
|
||||
'id': self.number,
|
||||
'project': {
|
||||
|
@ -1148,6 +1150,8 @@ class FakePagurePullRequest(object):
|
|||
}
|
||||
if action == 'pull-request.flag.added':
|
||||
data['msg']['flag'] = self.flags[0]
|
||||
if action == 'pull-request.tag.added':
|
||||
data['msg']['tags'] = self.tags
|
||||
return (name, data)
|
||||
|
||||
def getPullRequestOpenedEvent(self):
|
||||
|
@ -1169,6 +1173,20 @@ class FakePagurePullRequest(object):
|
|||
self._updateTimeStamp()
|
||||
return self._getPullRequestEvent('pull-request.initial_comment.edited')
|
||||
|
||||
def getPullRequestTagAddedEvent(self, tags, reset=True):
|
||||
if reset:
|
||||
self.tags = []
|
||||
_tags = set(self.tags)
|
||||
_tags.update(set(tags))
|
||||
self.tags = list(_tags)
|
||||
self.addComment(
|
||||
"**Metadata Update from @pingou**:\n- " +
|
||||
"Pull-request tagged with: %s" % ', '.join(tags),
|
||||
True)
|
||||
self._updateTimeStamp()
|
||||
return self._getPullRequestEvent(
|
||||
'pull-request.tag.added', pull_data_field='pull_request')
|
||||
|
||||
def getPullRequestStatusSetEvent(self, status):
|
||||
self.addFlag(
|
||||
status, "https://url", "Build %s" % status)
|
||||
|
@ -1295,7 +1313,8 @@ class FakePagureAPIClient(pagureconnection.PagureAPIClient):
|
|||
'comments': pr.comments,
|
||||
'commit_stop': pr.commit_stop,
|
||||
'threshold_reached': pr.threshold_reached,
|
||||
'cached_merge_status': pr.cached_merge_status
|
||||
'cached_merge_status': pr.cached_merge_status,
|
||||
'tags': pr.tags,
|
||||
}, 200, "", "GET"
|
||||
|
||||
match = re.match(r'.+/api/0/(.+)/pull-request/(\d+)/flag$', url)
|
||||
|
|
|
@ -38,6 +38,34 @@
|
|||
pagure:
|
||||
status: 'success'
|
||||
|
||||
- pipeline:
|
||||
name: trigger-tag
|
||||
manager: independent
|
||||
trigger:
|
||||
pagure:
|
||||
- event: pg_pull_request
|
||||
action: tagged
|
||||
tag:
|
||||
- gateit
|
||||
- mergeit
|
||||
success:
|
||||
pagure:
|
||||
status: 'success'
|
||||
|
||||
- pipeline:
|
||||
name: require-tag
|
||||
manager: independent
|
||||
require:
|
||||
pagure:
|
||||
tags: gateit
|
||||
trigger:
|
||||
pagure:
|
||||
- event: pg_pull_request
|
||||
action: changed
|
||||
success:
|
||||
pagure:
|
||||
status: 'success'
|
||||
|
||||
- job:
|
||||
name: base
|
||||
parent: null
|
||||
|
@ -64,3 +92,15 @@
|
|||
trigger-flag:
|
||||
jobs:
|
||||
- project-test
|
||||
|
||||
- project:
|
||||
name: org/project4
|
||||
trigger-tag:
|
||||
jobs:
|
||||
- project-test
|
||||
|
||||
- project:
|
||||
name: org/project5
|
||||
require-tag:
|
||||
jobs:
|
||||
- project-test
|
|
@ -360,6 +360,52 @@ class TestPagureDriver(ZuulTestCase):
|
|||
self.waitUntilSettled()
|
||||
self.assertEqual(1, len(self.history))
|
||||
|
||||
@simple_layout('layouts/requirements-pagure.yaml', driver='pagure')
|
||||
def test_tag_trigger(self):
|
||||
|
||||
A = self.fake_pagure.openFakePullRequest(
|
||||
'org/project4', 'master', 'A')
|
||||
|
||||
self.fake_pagure.emitEvent(
|
||||
A.getPullRequestTagAddedEvent(["lambda"]))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(0, len(self.history))
|
||||
|
||||
self.fake_pagure.emitEvent(
|
||||
A.getPullRequestTagAddedEvent(["gateit", "lambda"]))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(1, len(self.history))
|
||||
|
||||
self.fake_pagure.emitEvent(
|
||||
A.getPullRequestTagAddedEvent(["mergeit"]))
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(2, len(self.history))
|
||||
|
||||
@simple_layout('layouts/requirements-pagure.yaml', driver='pagure')
|
||||
def test_tag_require(self):
|
||||
|
||||
A = self.fake_pagure.openFakePullRequest(
|
||||
'org/project5', 'master', 'A')
|
||||
|
||||
self.fake_pagure.emitEvent(A.getPullRequestUpdatedEvent())
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(0, len(self.history))
|
||||
|
||||
A.tags = ["lambda"]
|
||||
self.fake_pagure.emitEvent(A.getPullRequestUpdatedEvent())
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(0, len(self.history))
|
||||
|
||||
A.tags = ["lambda", "gateit"]
|
||||
self.fake_pagure.emitEvent(A.getPullRequestUpdatedEvent())
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(1, len(self.history))
|
||||
|
||||
A.tags = []
|
||||
self.fake_pagure.emitEvent(A.getPullRequestUpdatedEvent())
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(1, len(self.history))
|
||||
|
||||
@simple_layout('layouts/merging-pagure.yaml', driver='pagure')
|
||||
def test_merge_action_in_independent(self):
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import hashlib
|
|||
import queue
|
||||
import threading
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
import requests
|
||||
import cherrypy
|
||||
|
@ -194,13 +195,17 @@ class PagureEventConnector(threading.Thread):
|
|||
self.daemon = True
|
||||
self.connection = connection
|
||||
self._stopped = False
|
||||
self.metadata_notif = re.compile(
|
||||
r"^\*\*Metadata Update", re.MULTILINE)
|
||||
self.event_handler_mapping = {
|
||||
'pull-request.comment.added': self._event_issue_comment,
|
||||
'pull-request.new': self._event_pull_request,
|
||||
'pull-request.flag.added': self._event_flag_added,
|
||||
'git.receive': self._event_ref_updated,
|
||||
'pull-request.initial_comment.edited':
|
||||
self._event_issue_initial_comment
|
||||
self._event_issue_initial_comment,
|
||||
'pull-request.tag.added':
|
||||
self._event_pull_request_tags_changed
|
||||
}
|
||||
|
||||
def stop(self):
|
||||
|
@ -244,16 +249,19 @@ class PagureEventConnector(threading.Thread):
|
|||
self.connection.logEvent(event)
|
||||
self.connection.sched.addEvent(event)
|
||||
|
||||
def _event_base(self, body):
|
||||
def _event_base(self, body, pull_data_field='pullrequest'):
|
||||
event = PagureTriggerEvent()
|
||||
if 'pullrequest' in body['msg']:
|
||||
data = body['msg']['pullrequest']
|
||||
|
||||
if pull_data_field in body['msg']:
|
||||
data = body['msg'][pull_data_field]
|
||||
data['tags'] = body['msg'].get('tags', [])
|
||||
data['flag'] = body['msg'].get('flag')
|
||||
event.title = data.get('title')
|
||||
event.project_name = data.get('project', {}).get('fullname')
|
||||
event.change_number = data.get('id')
|
||||
event.updated_at = data.get('date_created')
|
||||
event.branch = data.get('branch')
|
||||
event.tags = data.get('tags', [])
|
||||
event.change_url = self.connection.getPullUrl(event.project_name,
|
||||
event.change_number)
|
||||
event.ref = "refs/pull/%s/head" % event.change_number
|
||||
|
@ -271,12 +279,21 @@ class PagureEventConnector(threading.Thread):
|
|||
event.action = 'changed'
|
||||
return event
|
||||
|
||||
def _event_pull_request_tags_changed(self, body):
|
||||
""" Handles pull request metadata change """
|
||||
# pull-request.tag.added/removed use pull_request in payload body
|
||||
event, _ = self._event_base(body, pull_data_field='pull_request')
|
||||
event.action = 'tagged'
|
||||
return event
|
||||
|
||||
def _event_issue_comment(self, body):
|
||||
""" Handles pull request comments """
|
||||
# https://fedora-fedmsg.readthedocs.io/en/latest/topics.html#pagure-pull-request-comment-added
|
||||
event, data = self._event_base(body)
|
||||
last_comment = data.get('comments', [])[-1]
|
||||
if last_comment.get('notification') is True:
|
||||
if (last_comment.get('notification') is True and
|
||||
not self.metadata_notif.match(
|
||||
last_comment.get('comment', ''))):
|
||||
# An updated PR (new commits) triggers the comment.added
|
||||
# event. A message is added by pagure on the PR but notification
|
||||
# is set to true.
|
||||
|
@ -556,7 +573,8 @@ class PagureConnection(BaseConnection):
|
|||
self.connectors = {}
|
||||
self.source = driver.getSource(self)
|
||||
self.event_queue = queue.Queue()
|
||||
|
||||
self.metadata_notif = re.compile(
|
||||
r"^\*\*Metadata Update", re.MULTILINE)
|
||||
self.sched = None
|
||||
|
||||
def onLoad(self):
|
||||
|
@ -793,6 +811,9 @@ class PagureConnection(BaseConnection):
|
|||
for comment in pr.get('comments', []):
|
||||
# PR updated are reported as comment but with the notification flag
|
||||
if comment['notification']:
|
||||
# Ignore metadata update such as assignee and tags
|
||||
if self.metadata_notif.match(comment.get('comment', '')):
|
||||
continue
|
||||
date = int(comment['date_created'])
|
||||
if date > last_pr_code_updated:
|
||||
last_pr_code_updated = date
|
||||
|
@ -822,6 +843,7 @@ class PagureConnection(BaseConnection):
|
|||
change.patchset = change.pr.get('commit_stop')
|
||||
change.files = change.pr.get('files')
|
||||
change.title = change.pr.get('title')
|
||||
change.tags = change.pr.get('tags')
|
||||
change.open = change.pr.get('status') == 'Open'
|
||||
change.is_merged = change.pr.get('status') == 'Merged'
|
||||
change.status = self.getStatus(change.project, change.number)
|
||||
|
|
|
@ -27,6 +27,7 @@ class PullRequest(Change):
|
|||
self.title = None
|
||||
self.score = 0
|
||||
self.files = []
|
||||
self.tags = []
|
||||
|
||||
def __repr__(self):
|
||||
r = ['<Change 0x%x' % id(self)]
|
||||
|
@ -42,6 +43,8 @@ class PullRequest(Change):
|
|||
r.append('status: %s' % self.status)
|
||||
if self.score:
|
||||
r.append('score: %s' % self.score)
|
||||
if self.tags:
|
||||
r.append('tags: %s' % ', '.join(self.tags))
|
||||
if self.is_merged:
|
||||
r.append('state: merged')
|
||||
if self.open:
|
||||
|
@ -64,6 +67,7 @@ class PagureTriggerEvent(TriggerEvent):
|
|||
self.title = None
|
||||
self.action = None
|
||||
self.status = None
|
||||
self.tags = []
|
||||
|
||||
def _repr(self):
|
||||
r = [super(PagureTriggerEvent, self)._repr()]
|
||||
|
@ -74,6 +78,8 @@ class PagureTriggerEvent(TriggerEvent):
|
|||
r.append("project:%s" % self.canonical_project_name)
|
||||
if self.change_number:
|
||||
r.append("pr:%s" % self.change_number)
|
||||
if self.tags:
|
||||
r.append("tags:%s" % ', '.join(self.tags))
|
||||
return ' '.join(r)
|
||||
|
||||
def isPatchsetCreated(self):
|
||||
|
@ -84,7 +90,7 @@ class PagureTriggerEvent(TriggerEvent):
|
|||
|
||||
class PagureEventFilter(EventFilter):
|
||||
def __init__(self, trigger, types=[], refs=[], statuses=[],
|
||||
comments=[], actions=[], ignore_deletes=True):
|
||||
comments=[], actions=[], tags=[], ignore_deletes=True):
|
||||
|
||||
EventFilter.__init__(self, trigger)
|
||||
|
||||
|
@ -96,6 +102,7 @@ class PagureEventFilter(EventFilter):
|
|||
self.comments = [re.compile(x) for x in comments]
|
||||
self.actions = actions
|
||||
self.statuses = statuses
|
||||
self.tags = tags
|
||||
self.ignore_deletes = ignore_deletes
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -113,6 +120,8 @@ class PagureEventFilter(EventFilter):
|
|||
ret += ' actions: %s' % ', '.join(self.actions)
|
||||
if self.statuses:
|
||||
ret += ' statuses: %s' % ', '.join(self.statuses)
|
||||
if self.tags:
|
||||
ret += ' tags: %s' % ', '.join(self.tags)
|
||||
ret += '>'
|
||||
|
||||
return ret
|
||||
|
@ -159,6 +168,10 @@ class PagureEventFilter(EventFilter):
|
|||
if self.statuses and not matches_status:
|
||||
return False
|
||||
|
||||
if self.tags:
|
||||
if not set(event.tags).intersection(set(self.tags)):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
@ -166,12 +179,13 @@ class PagureEventFilter(EventFilter):
|
|||
# pipeline requires definition)
|
||||
class PagureRefFilter(RefFilter):
|
||||
def __init__(self, connection_name, score=None,
|
||||
open=None, merged=None, status=None):
|
||||
open=None, merged=None, status=None, tags=[]):
|
||||
RefFilter.__init__(self, connection_name)
|
||||
self.score = score
|
||||
self.open = open
|
||||
self.merged = merged
|
||||
self.status = status
|
||||
self.tags = tags
|
||||
|
||||
def __repr__(self):
|
||||
ret = '<PagureRefFilter connection_name: %s ' % self.connection_name
|
||||
|
@ -183,6 +197,8 @@ class PagureRefFilter(RefFilter):
|
|||
ret += ' merged: %s' % self.merged
|
||||
if self.status is not None:
|
||||
ret += ' status: %s' % self.status
|
||||
if self.tags:
|
||||
ret += ' tags: %s' % ', '.join(self.tags)
|
||||
ret += '>'
|
||||
return ret
|
||||
|
||||
|
@ -203,4 +219,8 @@ class PagureRefFilter(RefFilter):
|
|||
if change.status != self.status:
|
||||
return False
|
||||
|
||||
if self.tags:
|
||||
if not set(change.tags).intersection(set(self.tags)):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
|
@ -20,6 +20,7 @@ from zuul.source import BaseSource
|
|||
from zuul.model import Project
|
||||
|
||||
from zuul.driver.pagure.paguremodel import PagureRefFilter
|
||||
from zuul.driver.util import scalar_or_list, to_list
|
||||
|
||||
|
||||
class PagureSource(BaseSource):
|
||||
|
@ -117,7 +118,6 @@ class PagureSource(BaseSource):
|
|||
"""Get the git-web url for a project."""
|
||||
raise NotImplementedError()
|
||||
|
||||
# This driver does not implement pipeline requirements.
|
||||
def getRequireFilters(self, config):
|
||||
f = PagureRefFilter(
|
||||
connection_name=self.connection.connection_name,
|
||||
|
@ -125,6 +125,7 @@ class PagureSource(BaseSource):
|
|||
open=config.get('open'),
|
||||
merged=config.get('merged'),
|
||||
status=config.get('status'),
|
||||
tags=to_list(config.get('tags'))
|
||||
)
|
||||
return [f]
|
||||
|
||||
|
@ -142,6 +143,7 @@ def getRequireSchema():
|
|||
'open': bool,
|
||||
'merged': bool,
|
||||
'status': str,
|
||||
'tags': scalar_or_list(str)
|
||||
}
|
||||
return require
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ class PagureTrigger(BaseTrigger):
|
|||
refs=to_list(trigger.get('ref')),
|
||||
comments=to_list(trigger.get('comment')),
|
||||
statuses=to_list(trigger.get('status')),
|
||||
tags=to_list(trigger.get('tag')),
|
||||
)
|
||||
efilters.append(f)
|
||||
|
||||
|
@ -56,6 +57,7 @@ def getSchema():
|
|||
'ref': scalar_or_list(str),
|
||||
'comment': scalar_or_list(str),
|
||||
'status': scalar_or_list(str),
|
||||
'tag': scalar_or_list(str)
|
||||
}
|
||||
|
||||
return pagure_trigger
|
||||
|
|
Loading…
Reference in New Issue