diff --git a/tests/fixtures/config/zuultrigger/parent-change-enqueued-github/git/org_common-config/playbooks/project-check.yaml b/tests/fixtures/config/zuultrigger/parent-change-enqueued-github/git/org_common-config/playbooks/project-check.yaml new file mode 100644 index 0000000000..f679dceaef --- /dev/null +++ b/tests/fixtures/config/zuultrigger/parent-change-enqueued-github/git/org_common-config/playbooks/project-check.yaml @@ -0,0 +1,2 @@ +- hosts: all + tasks: [] diff --git a/tests/fixtures/config/zuultrigger/parent-change-enqueued-github/git/org_common-config/playbooks/project-gate.yaml b/tests/fixtures/config/zuultrigger/parent-change-enqueued-github/git/org_common-config/playbooks/project-gate.yaml new file mode 100644 index 0000000000..f679dceaef --- /dev/null +++ b/tests/fixtures/config/zuultrigger/parent-change-enqueued-github/git/org_common-config/playbooks/project-gate.yaml @@ -0,0 +1,2 @@ +- hosts: all + tasks: [] diff --git a/tests/fixtures/config/zuultrigger/parent-change-enqueued-github/git/org_common-config/zuul.yaml b/tests/fixtures/config/zuultrigger/parent-change-enqueued-github/git/org_common-config/zuul.yaml new file mode 100644 index 0000000000..4c358b4530 --- /dev/null +++ b/tests/fixtures/config/zuultrigger/parent-change-enqueued-github/git/org_common-config/zuul.yaml @@ -0,0 +1,72 @@ +- pipeline: + name: check + manager: independent + require: + github: + label: for-check + trigger: + github: + - event: pull_request + action: + - opened + zuul: + - event: parent-change-enqueued + pipeline: gate + success: + github: + comment: true + status: failure + failure: + github: + comment: true + status: failure + +- pipeline: + name: gate + manager: dependent + require: + github: + label: for-gate + trigger: + github: + - event: pull_request_review + action: submitted + state: approved + zuul: + - event: parent-change-enqueued + pipeline: gate + success: + github: + comment: true + status: success + merge: true + failure: + github: + comment: true + status: failure + start: + github: + comment: true + status: pending + precedence: high + +- job: + name: base + parent: null + +- job: + name: project-check + run: playbooks/project-check.yaml + +- job: + name: project-gate + run: playbooks/project-gate.yaml + +- project: + name: org/project + check: + jobs: + - project-check + gate: + jobs: + - project-gate diff --git a/tests/fixtures/config/zuultrigger/parent-change-enqueued-github/git/org_project/README b/tests/fixtures/config/zuultrigger/parent-change-enqueued-github/git/org_project/README new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/fixtures/config/zuultrigger/parent-change-enqueued-github/main.yaml b/tests/fixtures/config/zuultrigger/parent-change-enqueued-github/main.yaml new file mode 100644 index 0000000000..1aed49ed8d --- /dev/null +++ b/tests/fixtures/config/zuultrigger/parent-change-enqueued-github/main.yaml @@ -0,0 +1,8 @@ +- tenant: + name: tenant-one + source: + github: + config-projects: + - org/common-config + untrusted-projects: + - org/project diff --git a/tests/unit/test_zuultrigger.py b/tests/unit/test_zuultrigger.py index 97da74d8e2..a31a9990d6 100644 --- a/tests/unit/test_zuultrigger.py +++ b/tests/unit/test_zuultrigger.py @@ -14,7 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. -from tests.base import ZuulTestCase +from tests.base import ZuulTestCase, ZuulGithubAppTestCase from zuul.driver.zuul.zuulmodel import ZuulTriggerEvent @@ -89,6 +89,81 @@ class TestZuulTriggerParentChangeEnqueued(ZuulTestCase): self.assertEqual(zuultrigger_event_count, 0) +class TestZuulTriggerParentChangeEnqueuedGithub(ZuulGithubAppTestCase): + tenant_config_file = \ + 'config/zuultrigger/parent-change-enqueued-github/main.yaml' + config_file = 'zuul-github-driver.conf' + + def test_zuul_trigger_parent_change_enqueued(self): + "Test Zuul trigger event: parent-change-enqueued" + # This test has the following three changes: + # B1 -> A; B2 -> A + # When A is enqueued in the gate, B1 and B2 should both attempt + # to be enqueued in both pipelines. B1 should end up in check + # and B2 in gate because of differing pipeline requirements. + self.executor_server.hold_jobs_in_build = True + A = self.fake_github.openFakePullRequest('org/project', 'master', 'A') + msg = "Depends-On: https://github.com/org/project1/pull/%s" % A.number + B1 = self.fake_github.openFakePullRequest( + 'org/project', 'master', 'B1', body=msg) + B2 = self.fake_github.openFakePullRequest( + 'org/project', 'master', 'B2', body=msg) + A.addReview('derp', 'APPROVED') + B1.addReview('derp', 'APPROVED') + B2.addReview('derp', 'APPROVED') + A.addLabel('for-gate') # required by gate + B1.addLabel('for-check') # should go to check + B2.addLabel('for-gate') # should go to gate + + self.fake_github.emitEvent(A.getReviewAddedEvent('approved')) + # Jobs are being held in build to make sure that 3,1 has time + # to enqueue behind 1,1 so that the test is more + # deterministic. + self.waitUntilSettled() + self.executor_server.hold_jobs_in_build = False + self.executor_server.release() + self.waitUntilSettled() + + self.assertEqual(len(self.history), 3) + for job in self.history: + if job.changes == '1,{}'.format(A.head_sha): + self.assertEqual(job.name, 'project-gate') + elif job.changes == '1,{} 2,{}'.format(A.head_sha, B1.head_sha): + self.assertEqual(job.name, 'project-check') + elif job.changes == '1,{} 3,{}'.format(A.head_sha, B2.head_sha): + self.assertEqual(job.name, 'project-gate') + else: + raise Exception("Unknown job") + + # Now directly enqueue a change into the check. As no pipeline reacts + # on parent-change-enqueued from pipeline check no + # parent-change-enqueued event is expected. + zuultrigger_event_count = 0 + + def counting_put(*args, **kwargs): + nonlocal zuultrigger_event_count + if isinstance(args[0], ZuulTriggerEvent): + zuultrigger_event_count += 1 + self.sched.trigger_event_queue.put_orig(*args, **kwargs) + + self.sched.trigger_event_queue.put_orig = \ + self.sched.trigger_event_queue.put + self.sched.trigger_event_queue.put = counting_put + + C = self.fake_github.openFakePullRequest('org/project', 'master', 'C') + C.addLabel('for-check') # should go to check + + msg = "Depends-On: https://github.com/org/project1/pull/%s" % C.number + D = self.fake_github.openFakePullRequest( + 'org/project', 'master', 'D', body=msg) + D.addLabel('for-check') # should go to check + self.fake_github.emitEvent(C.getPullRequestOpenedEvent()) + + self.waitUntilSettled() + self.assertEqual(len(self.history), 4) + self.assertEqual(zuultrigger_event_count, 0) + + class TestZuulTriggerProjectChangeMerged(ZuulTestCase): tenant_config_file = 'config/zuultrigger/project-change-merged/main.yaml' diff --git a/zuul/driver/github/githubconnection.py b/zuul/driver/github/githubconnection.py index 8b07b67140..72bb800664 100644 --- a/zuul/driver/github/githubconnection.py +++ b/zuul/driver/github/githubconnection.py @@ -832,6 +832,11 @@ class GithubConnection(BaseConnection): change.message = change.pr.get('body') or '' change.updated_at = self._ghTimestampToDate( change.pr.get('updated_at')) + change.url = change.pr.get('url') + change.uris = [ + '%s/%s/pull/%s' % (self.server, change.project.name, + change.number), + ] if self.sched: self.sched.onChangeUpdated(change)