Optionally limit github to protected branches

When using a branch and pull model on a shared repository there are
usually one or more protected branches which are gated and a dynamic
number of temporary personal/feature branches which are the source for
the pull requests. These temporary branches while ungated can
potentially include broken zuul config and therefore break the global
tenant wide configuration.

In order to deal with this model add support for excluding unprotected
branches. This can be configured on tenant level and overridden per
project.

Change-Id: I8a45fd41539a3c964a84142f04c1644585c0fdcf
This commit is contained in:
Tobias Henkel
2017-08-02 20:27:10 +02:00
parent 90b32ea925
commit eca4620efa
22 changed files with 163 additions and 22 deletions

View File

@@ -616,7 +616,7 @@ class GerritConnection(BaseConnection):
(record.get('number'),))
return changes
def getProjectBranches(self, project: Project) -> List[str]:
def getProjectBranches(self, project: Project, tenant) -> List[str]:
refs = self.getInfoRefs(project)
heads = [str(k[len('refs/heads/'):]) for k in refs.keys()
if k.startswith('refs/heads/')]

View File

@@ -54,8 +54,8 @@ class GerritSource(BaseSource):
def getProjectOpenChanges(self, project):
return self.connection.getProjectOpenChanges(project)
def getProjectBranches(self, project):
return self.connection.getProjectBranches(project)
def getProjectBranches(self, project, tenant):
return self.connection.getProjectBranches(project, tenant)
def getGitUrl(self, project):
return self.connection.getGitUrl(project)

View File

@@ -48,7 +48,7 @@ class GitConnection(BaseConnection):
def addProject(self, project):
self.projects[project.name] = project
def getProjectBranches(self, project):
def getProjectBranches(self, project, tenant):
# TODO(jeblair): implement; this will need to handle local or
# remote git urls.
raise NotImplemented()

View File

@@ -45,8 +45,8 @@ class GitSource(BaseSource):
self.connection.addProject(p)
return p
def getProjectBranches(self, project):
return self.connection.getProjectBranches(project)
def getProjectBranches(self, project, tenant):
return self.connection.getProjectBranches(project, tenant)
def getGitUrl(self, project):
return self.connection.getGitUrl(project)

View File

@@ -698,11 +698,21 @@ class GithubConnection(BaseConnection):
def addProject(self, project):
self.projects[project.name] = project
def getProjectBranches(self, project):
def getProjectBranches(self, project, tenant):
# Evaluate if unprotected branches should be excluded or not. The first
# match wins. The order is project -> tenant (default is false).
project_config = tenant.project_configs.get(project.canonical_name)
if project_config.exclude_unprotected_branches is not None:
exclude_unprotected = project_config.exclude_unprotected_branches
else:
exclude_unprotected = tenant.exclude_unprotected_branches
github = self.getGithubClient()
owner, proj = project.name.split('/')
repository = github.repository(owner, proj)
branches = [branch.name for branch in repository.branches()]
branches = [branch.name for branch in repository.branches(
protected=exclude_unprotected)]
log_rate_limit(self.log, github)
return branches

View File

@@ -68,8 +68,8 @@ class GithubSource(BaseSource):
self.connection.addProject(p)
return p
def getProjectBranches(self, project):
return self.connection.getProjectBranches(project)
def getProjectBranches(self, project, tenant):
return self.connection.getProjectBranches(project, tenant)
def getProjectOpenChanges(self, project):
"""Get the open changes for a project."""

View File

@@ -81,7 +81,7 @@ class TimerDriver(Driver, TriggerInterface):
def _onTrigger(self, tenant, pipeline_name, timespec):
for project_name in tenant.layout.project_configs.keys():
(trusted, project) = tenant.getProject(project_name)
for branch in project.source.getProjectBranches(project):
for branch in project.source.getProjectBranches(project, tenant):
event = TimerTriggerEvent()
event.type = 'timer'
event.timespec = timespec