diff --git a/doc/source/tenants.rst b/doc/source/tenants.rst index c0131493b9..7eaf38f01a 100644 --- a/doc/source/tenants.rst +++ b/doc/source/tenants.rst @@ -307,6 +307,15 @@ configuration. Some examples of tenant definitions are: change as if it included a change to a ``zuul.yaml`` file, even if the change does not alter the configuration). + Even though branches specified by + `always-dynamic-branches` are not loaded into Zuul's + configuration, the fact that they may exist causes Zuul to + treat the project as a multi-branch project, and therefore + Zuul will use implied branch matchers even if the project + has only one permanent branch. This behavior may be + overridden with :attr:`pragma` or + :attr:`tenant.untrusted-projects..implied-branch-matchers`. + With all these caveats in mind, this can be useful for repos with large numbers of rarely used branches as it allows Zuul to omit their configuration in most diff --git a/releasenotes/notes/always-dynamic-implied-555f1dc564e0c0f0.yaml b/releasenotes/notes/always-dynamic-implied-555f1dc564e0c0f0.yaml new file mode 100644 index 0000000000..0a4d5fe1d9 --- /dev/null +++ b/releasenotes/notes/always-dynamic-implied-555f1dc564e0c0f0.yaml @@ -0,0 +1,17 @@ +--- +upgrade: + - | + The behavior of a single-branch project with + :attr:`tenant.untrusted-projects..always-dynamic-branches` + has changed. Previously, if (after applying includes and + excludes) a project had only one permanent branch it was treated + as a single-branch project even if it also had + `always-dynamic-branches` set. Now if a project has + `always-dynamic-branches` set, it will be treated as a + multi-branch project and therefore implied branch matchers are + enabled. + + To retain the previous behavior (or explicitly choose a behavior) + the + :attr:`tenant.untrusted-projects..implied-branch-matchers` + tenant configuration option may be used. diff --git a/tests/fixtures/config/dynamic-only-project/git/org_project/zuul.yaml b/tests/fixtures/config/dynamic-only-project/git/org_project/zuul.yaml index 9712c08280..fb361f2136 100644 --- a/tests/fixtures/config/dynamic-only-project/git/org_project/zuul.yaml +++ b/tests/fixtures/config/dynamic-only-project/git/org_project/zuul.yaml @@ -1,5 +1,6 @@ - job: name: project-test + tags: [master] # Note: this job is not expected to run - job: diff --git a/tests/unit/test_v3.py b/tests/unit/test_v3.py index 39c458c845..8c329942d1 100644 --- a/tests/unit/test_v3.py +++ b/tests/unit/test_v3.py @@ -11774,6 +11774,48 @@ class TestDynamicBranchesProject(IncludeBranchesTestCase): dict(name='project-dynamic', result='SUCCESS', changes='1,1'), ], ordered=False) + def test_new_dynamic_branch_single_branch(self): + # In this test, the repo has only a single branch ("master"), + # but since it has always-dynamic-branches set, we treat it + # like a normal repo, not a shared-jobs repo, and behave as if + # implied-branch-matchers is set to true. + + # Create a new dynamic-only branch. Since it's dynamic only, + # it won't trigger a reconfiguration. + self.create_branch('org/project', 'feature/foo') + self.fake_gerrit.addEvent( + self.fake_gerrit.getFakeBranchCreatedEvent( + 'org/project', 'feature/foo')) + self.waitUntilSettled() + + # Test that we load configuration from the dynamic branch even + # though the tenant layout doesn't know about it yet (since + # there hasn't been a reconfiguration since it was created). + conf = textwrap.dedent( + """ + - job: + name: project-test + tags: [feature] + + - project: + check: + jobs: + - project-test + """) + file_dict = {'zuul.yaml': conf} + A = self.fake_gerrit.addFakeChange('org/project', 'feature/foo', 'A', + files=file_dict) + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + self.assertHistory([ + dict(name='central-test', result='SUCCESS', changes='1,1'), + dict(name='project-test', result='SUCCESS', changes='1,1'), + ], ordered=False) + + job = self.getJobFromHistory('project-test') + self.assertEqual(set(['feature']), + set(job.parameters['zuul']['jobtags'])) + def test_post_jobs(self): self.create_branch('org/project', 'feature/bar') self.fake_gerrit.addEvent( diff --git a/zuul/model.py b/zuul/model.py index 9144f8cbc6..bbea83d226 100644 --- a/zuul/model.py +++ b/zuul/model.py @@ -5230,6 +5230,12 @@ class Job(ConfigObject): if source_tpc.trusted: return None + # If we have some always-dynamic-branches, then this is + # probably not a shared jobs repo, so we will use implied + # branches regardless of how many branches it has. + if source_tpc.always_dynamic_branches: + return self.implied_branch_matcher + # If this project only has one branch, don't use implied # branch matchers. This way central job repos can work. branches = tenant.getProjectBranches(