Merge "Prune stale branches from mergers"
diff --git a/tests/base.py b/tests/base.py
index c449242..fe01399 100755
--- a/tests/base.py
+++ b/tests/base.py
@@ -606,7 +606,7 @@
return ret
def getGitUrl(self, project):
- return os.path.join(self.upstream_root, project.name)
+ return 'file://' + os.path.join(self.upstream_root, project.name)
class GithubChangeReference(git.Reference):
@@ -1794,18 +1794,6 @@
else:
self._log_stream = sys.stdout
- # NOTE(jeblair): this is temporary extra debugging to try to
- # track down a possible leak.
- orig_git_repo_init = git.Repo.__init__
-
- def git_repo_init(myself, *args, **kw):
- orig_git_repo_init(myself, *args, **kw)
- self.log.debug("Created git repo 0x%x %s" %
- (id(myself), repr(myself)))
-
- self.useFixture(fixtures.MonkeyPatch('git.Repo.__init__',
- git_repo_init))
-
handler = logging.StreamHandler(self._log_stream)
formatter = logging.Formatter('%(asctime)s %(name)-32s '
'%(levelname)-8s %(message)s')
@@ -1960,6 +1948,9 @@
self.config.set(
'executor', 'command_socket',
os.path.join(self.test_root, 'executor.socket'))
+ self.config.set(
+ 'merger', 'command_socket',
+ os.path.join(self.test_root, 'merger.socket'))
self.statsd = FakeStatsd()
if self.config.has_section('statsd'):
@@ -2016,6 +2007,7 @@
self.config, self.sched)
self.merge_client = zuul.merger.client.MergeClient(
self.config, self.sched)
+ self.merge_server = None
self.nodepool = zuul.nodepool.Nodepool(self.sched)
self.zk = zuul.zk.ZooKeeper()
self.zk.connect(self.zk_config)
@@ -2290,6 +2282,8 @@
self.executor_server.release()
self.executor_client.stop()
self.merge_client.stop()
+ if self.merge_server:
+ self.merge_server.stop()
self.executor_server.stop()
self.sched.stop()
self.sched.join()
@@ -2362,6 +2356,13 @@
zuul.merger.merger.reset_repo_to_head(repo)
repo.git.clean('-x', '-f', '-d')
+ def delete_branch(self, project, branch):
+ path = os.path.join(self.upstream_root, project)
+ repo = git.Repo(path)
+ repo.head.reference = repo.heads['master']
+ zuul.merger.merger.reset_repo_to_head(repo)
+ repo.delete_head(repo.heads[branch], force=True)
+
def create_commit(self, project):
path = os.path.join(self.upstream_root, project)
repo = git.Repo(path)
diff --git a/tests/fixtures/layouts/branch-deletion.yaml b/tests/fixtures/layouts/branch-deletion.yaml
new file mode 100644
index 0000000..f72902a
--- /dev/null
+++ b/tests/fixtures/layouts/branch-deletion.yaml
@@ -0,0 +1,34 @@
+- pipeline:
+ name: check
+ manager: independent
+ trigger:
+ gerrit:
+ - event: patchset-created
+ success:
+ gerrit:
+ Verified: 1
+ failure:
+ gerrit:
+ Verified: -1
+
+- job:
+ name: base
+ parent: null
+ run: playbooks/base.yaml
+
+- job:
+ name: project-test1
+ parent: base
+ branches: master
+
+- job:
+ name: project-test2
+ parent: base
+ branches: stable
+
+- project:
+ name: org/project
+ check:
+ jobs:
+ - project-test1
+ - project-test2
diff --git a/tests/unit/test_scheduler.py b/tests/unit/test_scheduler.py
index 5db20b3..3d76510 100755
--- a/tests/unit/test_scheduler.py
+++ b/tests/unit/test_scheduler.py
@@ -161,6 +161,44 @@
self.assertEqual(self.getJobFromHistory('project-test1').node,
'label2')
+ @simple_layout('layouts/branch-deletion.yaml')
+ def test_branch_deletion(self):
+ "Test the correct variant of a job runs on a branch"
+ self._startMerger()
+ for f in list(self.executor_server.merger_worker.functions.keys()):
+ f = str(f)
+ if f.startswith('merger:'):
+ self.executor_server.merger_worker.unRegisterFunction(f)
+
+ self.create_branch('org/project', 'stable')
+ A = self.fake_gerrit.addFakeChange('org/project', 'stable', 'A')
+ self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
+ self.waitUntilSettled()
+ self.assertEqual(self.getJobFromHistory('project-test2').result,
+ 'SUCCESS')
+
+ self.delete_branch('org/project', 'stable')
+ path = os.path.join(self.executor_src_root, 'review.example.com')
+ shutil.rmtree(path)
+
+ self.executor_server.hold_jobs_in_build = True
+ A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
+ self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
+ self.waitUntilSettled()
+ build = self.builds[0]
+
+ # Make sure there is no stable branch in the checked out git repo.
+ pname = 'review.example.com/org/project'
+ work = build.getWorkspaceRepos([pname])
+ work = work[pname]
+ heads = set([str(x) for x in work.heads])
+ self.assertEqual(heads, set(['master']))
+ self.executor_server.hold_jobs_in_build = False
+ build.release()
+ self.waitUntilSettled()
+ self.assertEqual(self.getJobFromHistory('project-test1').result,
+ 'SUCCESS')
+
def test_parallel_changes(self):
"Test that changes are tested in parallel and merged in series"
@@ -4934,7 +4972,7 @@
return repo_messages
def _test_merge(self, mode):
- us_path = os.path.join(
+ us_path = 'file://' + os.path.join(
self.upstream_root, 'org/project-%s' % mode)
expected_messages = [
'initial commit',
diff --git a/zuul/merger/merger.py b/zuul/merger/merger.py
index bd4ca58..035dbf5 100644
--- a/zuul/merger/merger.py
+++ b/zuul/merger/merger.py
@@ -143,19 +143,30 @@
self.update()
repo = self.createRepoObject()
origin = repo.remotes.origin
+ seen = set()
+ head = None
+ stale_refs = origin.stale_refs
+ # Update our local heads to match the remote, and pick one to
+ # reset the repo to. We don't delete anything at this point
+ # because we want to make sure the repo is in a state stable
+ # enough for git to operate.
for ref in origin.refs:
if ref.remote_head == 'HEAD':
continue
+ if ref in stale_refs:
+ continue
repo.create_head(ref.remote_head, ref, force=True)
-
- # try reset to remote HEAD (usually origin/master)
- # If it fails, pick the first reference
- try:
- repo.head.reference = origin.refs['HEAD']
- except IndexError:
- repo.head.reference = origin.refs[0]
+ seen.add(ref.remote_head)
+ if head is None:
+ head = ref.remote_head
+ self.log.debug("Reset to %s", head)
+ repo.head.reference = head
reset_repo_to_head(repo)
repo.git.clean('-x', '-f', '-d')
+ for ref in stale_refs:
+ self.log.debug("Delete stale ref %s", ref.remote_head)
+ repo.delete_head(ref.remote_head, force=True)
+ git.refs.RemoteReference.delete(repo, ref, force=True)
def prune(self):
repo = self.createRepoObject()
@@ -163,7 +174,7 @@
stale_refs = origin.stale_refs
if stale_refs:
self.log.debug("Pruning stale refs: %s", stale_refs)
- git.refs.RemoteReference.delete(repo, *stale_refs)
+ git.refs.RemoteReference.delete(repo, force=True, *stale_refs)
def getBranchHead(self, branch):
repo = self.createRepoObject()
@@ -193,11 +204,12 @@
return repo.refs
def setRef(self, path, hexsha, repo=None):
+ self.log.debug("Create reference %s at %s in %s",
+ path, hexsha, self.local_path)
if repo is None:
repo = self.createRepoObject()
binsha = gitdb.util.to_bin_sha(hexsha)
obj = git.objects.Object.new_from_sha(repo, binsha)
- self.log.debug("Create reference %s", path)
git.refs.Reference.create(repo, path, obj, force=True)
def setRefs(self, refs):