cloner: make more zuul args optional

When running in a timer job ZUUL_BRANCH and ZUUL_REF are not
provided.  Rather than having to set those in the job definition,
just make them optional for zuul-cloner.  The prepare logic is
updated to avoid incorrect string substitutions in case they are
not supplied.

With those two optional, the only remaining required argument is
ZUUL_URL.  This is only required if ZUUL_REF is set.

Also, enhance debugability by always printing the commit sha, even
if cloner has simply checked out a branch.

Change-Id: I8f88cb8d1e49255d38c2c75dd226b3027779aa9e
diff --git a/zuul/cmd/cloner.py b/zuul/cmd/cloner.py
index 5bbe3a0..187b6b0 100755
--- a/zuul/cmd/cloner.py
+++ b/zuul/cmd/cloner.py
@@ -88,17 +88,14 @@
             )
 
         args = parser.parse_args()
+        # Validate ZUUL_* arguments. If ref is provided then URL is required.
+        zuul_args = [zuul_opt for zuul_opt, val in vars(args).items()
+                     if zuul_opt.startswith('zuul') and val is not None]
+        if 'zuul_ref' in zuul_args and 'zuul_url' not in zuul_args:
+            parser.error("Specifying a Zuul ref requires a Zuul url. "
+                         "Define Zuul arguments either via environment "
+                         "variables or using options above.")
 
-        # Validate ZUUL_* arguments. If any ZUUL_* argument is set they
-        # must all be set, otherwise fallback to defaults.
-        zuul_missing = [zuul_opt for zuul_opt, val in vars(args).items()
-                        if zuul_opt.startswith('zuul') and val is None]
-        if (len(zuul_missing) > 0 and
-            len(zuul_missing) < len(ZUUL_ENV_SUFFIXES)):
-            parser.error(("Some Zuul parameters are not set:\n\t%s\n"
-                          "Define them either via environment variables or "
-                          "using options above." %
-                          "\n\t".join(sorted(zuul_missing))))
         self.args = args
 
     def setup_logging(self, color=False, verbose=False):
diff --git a/zuul/lib/cloner.py b/zuul/lib/cloner.py
index d697648..0ac7f0f 100644
--- a/zuul/lib/cloner.py
+++ b/zuul/lib/cloner.py
@@ -138,8 +138,11 @@
         if project in self.project_branches:
             indicated_branch = self.project_branches[project]
 
-        override_zuul_ref = re.sub(self.zuul_branch, indicated_branch,
-                                   self.zuul_ref)
+        if indicated_branch:
+            override_zuul_ref = re.sub(self.zuul_branch, indicated_branch,
+                                       self.zuul_ref)
+        else:
+            override_zuul_ref = None
 
         if indicated_branch and repo.hasBranch(indicated_branch):
             self.log.info("upstream repo has branch %s", indicated_branch)
@@ -150,14 +153,18 @@
             # FIXME should be origin HEAD branch which might not be 'master'
             fallback_branch = 'master'
 
-        fallback_zuul_ref = re.sub(self.zuul_branch, fallback_branch,
-                                   self.zuul_ref)
+        if self.zuul_branch:
+            fallback_zuul_ref = re.sub(self.zuul_branch, fallback_branch,
+                                       self.zuul_ref)
+        else:
+            fallback_zuul_ref = None
 
         # 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 != override_zuul_ref and
+            (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)
@@ -169,9 +176,9 @@
             # Checkout branch
             self.log.info("Falling back to branch %s", fallback_branch)
             try:
-                repo.checkout('remotes/origin/%s' % fallback_branch)
+                commit = repo.checkout('remotes/origin/%s' % fallback_branch)
             except (ValueError, GitCommandError):
                 self.log.exception("Fallback branch not found: %s",
                                    fallback_branch)
-            self.log.info("Prepared %s repo with branch %s",
-                          project, fallback_branch)
+            self.log.info("Prepared %s repo with branch %s at commit %s",
+                          project, fallback_branch, commit)
diff --git a/zuul/merger/merger.py b/zuul/merger/merger.py
index f98fd3e..c84d042 100644
--- a/zuul/merger/merger.py
+++ b/zuul/merger/merger.py
@@ -130,6 +130,7 @@
         self.log.debug("Checking out %s" % ref)
         repo.head.reference = ref
         reset_repo_to_head(repo)
+        return repo.head.commit
 
     def cherryPick(self, ref):
         repo = self.createRepoObject()