Merge "Support post jobs by supporting rev checkout"
diff --git a/tests/base.py b/tests/base.py
index 01097c1..2d7f918 100755
--- a/tests/base.py
+++ b/tests/base.py
@@ -1157,6 +1157,17 @@
zuul.merger.merger.reset_repo_to_head(repo)
repo.git.clean('-x', '-f', '-d')
+ def create_commit(self, project):
+ path = os.path.join(self.upstream_root, project)
+ repo = git.Repo(path)
+ repo.head.reference = repo.heads['master']
+ file_name = os.path.join(path, 'README')
+ with open(file_name, 'a') as f:
+ f.write('creating fake commit\n')
+ repo.index.add([file_name])
+ commit = repo.index.commit('Creating a fake commit')
+ return commit.hexsha
+
def ref_has_change(self, ref, change):
path = os.path.join(self.git_root, change.project)
repo = git.Repo(path)
diff --git a/tests/test_cloner.py b/tests/test_cloner.py
index 137c157..e3576bd 100644
--- a/tests/test_cloner.py
+++ b/tests/test_cloner.py
@@ -566,3 +566,57 @@
self.worker.hold_jobs_in_build = False
self.worker.release()
self.waitUntilSettled()
+
+ def test_post_checkout(self):
+ project = "org/project"
+ path = os.path.join(self.upstream_root, project)
+ repo = git.Repo(path)
+ repo.head.reference = repo.heads['master']
+ commits = []
+ for i in range(0, 3):
+ commits.append(self.create_commit(project))
+ newRev = commits[1]
+
+ cloner = zuul.lib.cloner.Cloner(
+ git_base_url=self.upstream_root,
+ projects=[project],
+ workspace=self.workspace_root,
+ zuul_branch=None,
+ zuul_ref='master',
+ zuul_url=self.git_root,
+ zuul_project=project,
+ zuul_newrev=newRev,
+ )
+ cloner.execute()
+ repos = self.getWorkspaceRepos([project])
+ cloned_sha = repos[project].rev_parse('HEAD').hexsha
+ self.assertEqual(newRev, cloned_sha)
+
+ def test_post_and_master_checkout(self):
+ project = "org/project1"
+ master_project = "org/project2"
+ path = os.path.join(self.upstream_root, project)
+ repo = git.Repo(path)
+ repo.head.reference = repo.heads['master']
+ commits = []
+ for i in range(0, 3):
+ commits.append(self.create_commit(project))
+ newRev = commits[1]
+
+ cloner = zuul.lib.cloner.Cloner(
+ git_base_url=self.upstream_root,
+ projects=[project, master_project],
+ workspace=self.workspace_root,
+ zuul_branch=None,
+ zuul_ref='master',
+ zuul_url=self.git_root,
+ zuul_project=project,
+ zuul_newrev=newRev
+ )
+ cloner.execute()
+ repos = self.getWorkspaceRepos([project, master_project])
+ cloned_sha = repos[project].rev_parse('HEAD').hexsha
+ self.assertEqual(newRev, cloned_sha)
+ self.assertEqual(
+ repos[master_project].rev_parse('HEAD').hexsha,
+ repos[master_project].rev_parse('master').hexsha)
diff --git a/zuul/cmd/cloner.py b/zuul/cmd/cloner.py
index c616aa1..4f8b9f4 100755
--- a/zuul/cmd/cloner.py
+++ b/zuul/cmd/cloner.py
@@ -27,6 +27,8 @@
'branch',
'ref',
'url',
+ 'project',
+ 'newrev',
)
@@ -98,6 +100,10 @@
parser.error("Specifying a Zuul ref requires a Zuul url. "
"Define Zuul arguments either via environment "
"variables or using options above.")
+ if 'zuul_newrev' in zuul_args and 'zuul_project' not in zuul_args:
+ parser.error("ZUUL_NEWREV has been specified without "
+ "ZUUL_PROJECT. Please define a ZUUL_PROJECT or do "
+ "not set ZUUL_NEWREV.")
self.args = args
@@ -145,6 +151,8 @@
clone_map_file=self.args.clone_map_file,
project_branches=project_branches,
cache_dir=self.args.cache_dir,
+ zuul_newrev=self.args.zuul_newrev,
+ zuul_project=self.args.zuul_project,
)
cloner.execute()
diff --git a/zuul/exceptions.py b/zuul/exceptions.py
index 2bd2c6b..40a1e40 100644
--- a/zuul/exceptions.py
+++ b/zuul/exceptions.py
@@ -22,5 +22,14 @@
super(ChangeNotFound, self).__init__(message)
+class RevNotFound(Exception):
+ def __init__(self, project, rev):
+ self.project = project
+ self.revision = rev
+ message = ("Failed to checkout project '%s' at revision '%s'"
+ % (self.project, self.revision))
+ super(RevNotFound, self).__init__(message)
+
+
class MergeFailure(Exception):
pass
diff --git a/zuul/lib/cloner.py b/zuul/lib/cloner.py
index 3155df6..3c7f04d 100644
--- a/zuul/lib/cloner.py
+++ b/zuul/lib/cloner.py
@@ -22,6 +22,7 @@
import six
from git import GitCommandError
+from zuul import exceptions
from zuul.lib.clonemapper import CloneMapper
from zuul.merger.merger import Repo
@@ -31,7 +32,8 @@
def __init__(self, git_base_url, projects, workspace, zuul_branch,
zuul_ref, zuul_url, branch=None, clone_map_file=None,
- project_branches=None, cache_dir=None):
+ project_branches=None, cache_dir=None, zuul_newrev=None,
+ zuul_project=None):
self.clone_map = []
self.dests = None
@@ -45,6 +47,10 @@
self.zuul_ref = zuul_ref or ''
self.zuul_url = zuul_url
self.project_branches = project_branches or {}
+ self.project_revisions = {}
+
+ if zuul_newrev and zuul_project:
+ self.project_revisions[zuul_project] = zuul_newrev
if clone_map_file:
self.readCloneMap(clone_map_file)
@@ -121,10 +127,15 @@
"""Clone a repository for project at dest and apply a reference
suitable for testing. The reference lookup is attempted in this order:
- 1) Zuul reference for the indicated branch
- 2) Zuul reference for the master branch
- 3) The tip of the indicated branch
- 4) The tip of the master branch
+ 1) The indicated revision for specific project
+ 2) Zuul reference for the indicated branch
+ 3) Zuul reference for the master branch
+ 4) The tip of the indicated branch
+ 5) The tip of the master branch
+
+ If an "indicated revision" is specified for this project, and we are
+ unable to meet this requirement, we stop attempting to check this
+ repo out and raise a zuul.exceptions.RevNotFound exception.
The "indicated branch" is one of the following:
@@ -144,6 +155,10 @@
# `git branch` is happy with.
repo.reset()
+ indicated_revision = None
+ if project in self.project_revisions:
+ indicated_revision = self.project_revisions[project]
+
indicated_branch = self.branch or self.zuul_branch
if project in self.project_branches:
indicated_branch = self.project_branches[project]
@@ -169,13 +184,26 @@
else:
fallback_zuul_ref = None
+ # If the user has requested an explicit revision to be checked out,
+ # we use it above all else, and if we cannot satisfy this requirement
+ # we raise an error and do not attempt to continue.
+ if indicated_revision:
+ self.log.info("Attempting to check out revision %s for "
+ "project %s", indicated_revision, project)
+ try:
+ self.fetchFromZuul(repo, project, self.zuul_ref)
+ commit = repo.checkout(indicated_revision)
+ except (ValueError, GitCommandError):
+ raise exceptions.RevNotFound(project, indicated_revision)
+ self.log.info("Prepared '%s' repo at revision '%s'", project,
+ indicated_revision)
# If we have a non empty zuul_ref to use, use it. Otherwise we fall
# back to checking out the branch.
- if ((override_zuul_ref and
- self.fetchFromZuul(repo, project, override_zuul_ref)) or
- (fallback_zuul_ref and
- fallback_zuul_ref != override_zuul_ref and
- self.fetchFromZuul(repo, project, fallback_zuul_ref))):
+ elif ((override_zuul_ref and
+ self.fetchFromZuul(repo, project, override_zuul_ref)) or
+ (fallback_zuul_ref and
+ fallback_zuul_ref != override_zuul_ref and
+ self.fetchFromZuul(repo, project, fallback_zuul_ref))):
# Work around a bug in GitPython which can not parse FETCH_HEAD
gitcmd = git.Git(dest)
fetch_head = gitcmd.rev_parse('FETCH_HEAD')