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
diff --git a/tests/base.py b/tests/base.py
index c567b03..937d60f 100755
--- a/tests/base.py
+++ b/tests/base.py
@@ -547,7 +547,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
return data
+ def getPullFileNames(self, project, number):
+ pr = self.pull_requests[number - 1]
+ return pr.files
+
def getUser(self, login):
data = {
'username': login,
diff --git a/tests/fixtures/layouts/files-github.yaml b/tests/fixtures/layouts/files-github.yaml
new file mode 100644
index 0000000..734b945
--- /dev/null
+++ b/tests/fixtures/layouts/files-github.yaml
@@ -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
diff --git a/tests/unit/test_change_matcher.py b/tests/unit/test_change_matcher.py
index 0585322..6b161a1 100644
--- a/tests/unit/test_change_matcher.py
+++ b/tests/unit/test_change_matcher.py
@@ -125,12 +125,18 @@
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):
diff --git a/tests/unit/test_github_driver.py b/tests/unit/test_github_driver.py
index 7267b83..605a479 100644
--- a/tests/unit/test_github_driver.py
+++ b/tests/unit/test_github_driver.py
@@ -65,6 +65,22 @@
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')
diff --git a/tests/unit/test_model.py b/tests/unit/test_model.py
index d8480ea..5f968b4 100644
--- a/tests/unit/test_model.py
+++ b/tests/unit/test_model.py
@@ -73,11 +73,21 @@
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)