GitHub file matching support
Allow to configure jobs to run only when certain files are changed. Github does not list the /COMMIT_MSG in the changed files as gerrit does. Therefore the matcher now returns False only if the single file is the /COMMIT_MSG one. Change-Id: I4fa8a328f2ba430c25377e50e1eff7c45829eba6
This commit is contained in:
parent
3b415926d2
commit
570d01c5fb
|
@ -547,7 +547,7 @@ class GithubChangeReference(git.Reference):
|
|||
class FakeGithubPullRequest(object):
|
||||
|
||||
def __init__(self, github, number, project, branch,
|
||||
subject, upstream_root, number_of_commits=1):
|
||||
subject, upstream_root, files=[], number_of_commits=1):
|
||||
"""Creates a new PR with several commits.
|
||||
Sends an event about opened PR."""
|
||||
self.github = github
|
||||
|
@ -558,6 +558,7 @@ class FakeGithubPullRequest(object):
|
|||
self.subject = subject
|
||||
self.number_of_commits = 0
|
||||
self.upstream_root = upstream_root
|
||||
self.files = []
|
||||
self.comments = []
|
||||
self.labels = []
|
||||
self.statuses = {}
|
||||
|
@ -566,18 +567,18 @@ class FakeGithubPullRequest(object):
|
|||
self.is_merged = False
|
||||
self.merge_message = None
|
||||
self._createPRRef()
|
||||
self._addCommitToRepo()
|
||||
self._addCommitToRepo(files=files)
|
||||
self._updateTimeStamp()
|
||||
|
||||
def addCommit(self):
|
||||
def addCommit(self, files=[]):
|
||||
"""Adds a commit on top of the actual PR head."""
|
||||
self._addCommitToRepo()
|
||||
self._addCommitToRepo(files=files)
|
||||
self._updateTimeStamp()
|
||||
self._clearStatuses()
|
||||
|
||||
def forcePush(self):
|
||||
def forcePush(self, files=[]):
|
||||
"""Clears actual commits and add a commit on top of the base."""
|
||||
self._addCommitToRepo(reset=True)
|
||||
self._addCommitToRepo(files=files, reset=True)
|
||||
self._updateTimeStamp()
|
||||
self._clearStatuses()
|
||||
|
||||
|
@ -690,7 +691,7 @@ class FakeGithubPullRequest(object):
|
|||
GithubChangeReference.create(
|
||||
repo, self._getPRReference(), 'refs/tags/init')
|
||||
|
||||
def _addCommitToRepo(self, reset=False):
|
||||
def _addCommitToRepo(self, files=[], reset=False):
|
||||
repo = self._getRepo()
|
||||
ref = repo.references[self._getPRReference()]
|
||||
if reset:
|
||||
|
@ -701,7 +702,12 @@ class FakeGithubPullRequest(object):
|
|||
zuul.merger.merger.reset_repo_to_head(repo)
|
||||
repo.git.clean('-x', '-f', '-d')
|
||||
|
||||
fn = '%s-%s' % (self.branch.replace('/', '_'), self.number)
|
||||
if files:
|
||||
fn = files[0]
|
||||
self.files = files
|
||||
else:
|
||||
fn = '%s-%s' % (self.branch.replace('/', '_'), self.number)
|
||||
self.files = [fn]
|
||||
msg = self.subject + '-' + str(self.number_of_commits)
|
||||
fn = os.path.join(repo.working_dir, fn)
|
||||
f = open(fn, 'w')
|
||||
|
@ -776,10 +782,11 @@ class FakeGithubConnection(githubconnection.GithubConnection):
|
|||
self.merge_failure = False
|
||||
self.merge_not_allowed_count = 0
|
||||
|
||||
def openFakePullRequest(self, project, branch, subject):
|
||||
def openFakePullRequest(self, project, branch, subject, files=[]):
|
||||
self.pr_number += 1
|
||||
pull_request = FakeGithubPullRequest(
|
||||
self, self.pr_number, project, branch, subject, self.upstream_root)
|
||||
self, self.pr_number, project, branch, subject, self.upstream_root,
|
||||
files=files)
|
||||
self.pull_requests.append(pull_request)
|
||||
return pull_request
|
||||
|
||||
|
@ -830,6 +837,10 @@ class FakeGithubConnection(githubconnection.GithubConnection):
|
|||
}
|
||||
return data
|
||||
|
||||
def getPullFileNames(self, project, number):
|
||||
pr = self.pull_requests[number - 1]
|
||||
return pr.files
|
||||
|
||||
def getUser(self, login):
|
||||
data = {
|
||||
'username': login,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
- pipeline:
|
||||
name: check
|
||||
manager: independent
|
||||
trigger:
|
||||
github:
|
||||
- event: pull_request
|
||||
action: opened
|
||||
|
||||
- job:
|
||||
name: project-test1
|
||||
files:
|
||||
- '.*-requires'
|
||||
|
||||
- project:
|
||||
name: org/project
|
||||
check:
|
||||
jobs:
|
||||
- project-test1
|
|
@ -125,12 +125,18 @@ class TestMatchAllFiles(BaseTestMatcher):
|
|||
def test_matches_returns_false_when_not_all_files_match(self):
|
||||
self._test_matches(False, files=['/COMMIT_MSG', 'docs/foo', 'foo/bar'])
|
||||
|
||||
def test_matches_returns_true_when_single_file_does_not_match(self):
|
||||
self._test_matches(True, files=['docs/foo'])
|
||||
|
||||
def test_matches_returns_false_when_commit_message_matches(self):
|
||||
self._test_matches(False, files=['/COMMIT_MSG'])
|
||||
|
||||
def test_matches_returns_true_when_all_files_match(self):
|
||||
self._test_matches(True, files=['/COMMIT_MSG', 'docs/foo'])
|
||||
|
||||
def test_matches_returns_true_when_single_file_matches(self):
|
||||
self._test_matches(True, files=['docs/foo'])
|
||||
|
||||
|
||||
class TestMatchAll(BaseTestMatcher):
|
||||
|
||||
|
|
|
@ -65,6 +65,22 @@ class TestGithubDriver(ZuulTestCase):
|
|||
|
||||
self.assertEqual(2, len(self.history))
|
||||
|
||||
@simple_layout('layouts/files-github.yaml', driver='github')
|
||||
def test_pull_matched_file_event(self):
|
||||
A = self.fake_github.openFakePullRequest(
|
||||
'org/project', 'master', 'A',
|
||||
files=['random.txt', 'build-requires'])
|
||||
self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(1, len(self.history))
|
||||
|
||||
# test_pull_unmatched_file_event
|
||||
B = self.fake_github.openFakePullRequest('org/project', 'master', 'B',
|
||||
files=['random.txt'])
|
||||
self.fake_github.emitEvent(B.getPullRequestOpenedEvent())
|
||||
self.waitUntilSettled()
|
||||
self.assertEqual(1, len(self.history))
|
||||
|
||||
@simple_layout('layouts/basic-github.yaml', driver='github')
|
||||
def test_comment_event(self):
|
||||
A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
|
||||
|
|
|
@ -73,11 +73,21 @@ class TestJob(BaseTestCase):
|
|||
change.files = ['/COMMIT_MSG', 'docs/foo']
|
||||
self.assertFalse(self.job.changeMatches(change))
|
||||
|
||||
def test_change_matches_returns_false_for_single_matched_skip_if(self):
|
||||
change = model.Change('project')
|
||||
change.files = ['docs/foo']
|
||||
self.assertFalse(self.job.changeMatches(change))
|
||||
|
||||
def test_change_matches_returns_true_for_unmatched_skip_if(self):
|
||||
change = model.Change('project')
|
||||
change.files = ['/COMMIT_MSG', 'foo']
|
||||
self.assertTrue(self.job.changeMatches(change))
|
||||
|
||||
def test_change_matches_returns_true_for_single_unmatched_skip_if(self):
|
||||
change = model.Change('project')
|
||||
change.files = ['foo']
|
||||
self.assertTrue(self.job.changeMatches(change))
|
||||
|
||||
def test_job_sets_defaults_for_boolean_attributes(self):
|
||||
self.assertIsNotNone(self.job.voting)
|
||||
|
||||
|
|
|
@ -108,7 +108,9 @@ class MatchAllFiles(AbstractMatcherCollection):
|
|||
yield self.commit_regex
|
||||
|
||||
def matches(self, change):
|
||||
if not (hasattr(change, 'files') and len(change.files) > 1):
|
||||
if not (hasattr(change, 'files') and change.files):
|
||||
return False
|
||||
if len(change.files) == 1 and self.commit_regex.match(change.files[0]):
|
||||
return False
|
||||
for file_ in change.files:
|
||||
matched_file = False
|
||||
|
|
|
@ -288,6 +288,7 @@ class GithubConnection(BaseConnection):
|
|||
change.url = event.change_url
|
||||
change.updated_at = self._ghTimestampToDate(event.updated_at)
|
||||
change.patchset = event.patch_number
|
||||
change.files = self.getPullFileNames(project, change.number)
|
||||
change.title = event.title
|
||||
change.source_event = event
|
||||
elif event.ref:
|
||||
|
@ -347,6 +348,11 @@ class GithubConnection(BaseConnection):
|
|||
# For now, just send back a True value.
|
||||
return True
|
||||
|
||||
def getPullFileNames(self, project, number):
|
||||
owner, proj = project.name.split('/')
|
||||
return [f.filename for f in
|
||||
self.github.pull_request(owner, proj, number).files()]
|
||||
|
||||
def getUser(self, login):
|
||||
return GithubUser(self.github, login)
|
||||
|
||||
|
|
|
@ -84,5 +84,9 @@ class GithubSource(BaseSource):
|
|||
"""Get the git-web url for a project."""
|
||||
return self.connection.getGitwebUrl(project, sha)
|
||||
|
||||
def getPullFiles(self, project, number):
|
||||
"""Get filenames of the pull request"""
|
||||
return self.connection.getPullFileNames(project, number)
|
||||
|
||||
def _ghTimestampToDate(self, timestamp):
|
||||
return time.strptime(timestamp, '%Y-%m-%dT%H:%M:%SZ')
|
||||
|
|
Loading…
Reference in New Issue