Add the ability to set git user.email and user.name

It is possible the host system does not have git properly configured,
which results in merge failures because the git client is complain. For
example:

  GitCommandError: 'git merge FETCH_HEAD' returned exit status 128:
  *** Please tell me who you are.

  Run

    git config --global user.email "you@example.com"
    git config --global user.name "Your Name"

  to set your account's default identity.
  Omit --global to set the identity only in this repository.

Now we can pass user.name and user.email settings to git, if configured
to do so.

Change-Id: I896194d8d1f5334026954b02f3a1a8dd82bed2ac
Signed-off-by: Paul Belanger <paul.belanger@polybeacon.com>
Reviewed-on: https://review.openstack.org/29015
Reviewed-by: James E. Blair <corvus@inaugust.com>
Approved: Clark Boylan <clark.boylan@gmail.com>
Reviewed-by: Clark Boylan <clark.boylan@gmail.com>
Tested-by: Jenkins
diff --git a/doc/source/zuul.rst b/doc/source/zuul.rst
index 96faf86..2899fdd 100644
--- a/doc/source/zuul.rst
+++ b/doc/source/zuul.rst
@@ -94,6 +94,14 @@
   Directory that Zuul should clone local git repositories to.
   ``git_dir=/var/lib/zuul/git``
 
+**git_user_email**
+  Optional: Value to pass to `git config user.email`.
+  ``git_user_email=zuul@example.com``
+
+**git_user_name**
+  Optional: Value to pass to `git config user.name`.
+  ``git_user_name=zuul``
+
 **push_change_refs**
   Boolean value (``true`` or ``false``) that determines if Zuul should
   push change refs to the git origin server for the git repositories in
diff --git a/etc/zuul.conf-sample b/etc/zuul.conf-sample
index 75d1ca5..47e1d7e 100644
--- a/etc/zuul.conf-sample
+++ b/etc/zuul.conf-sample
@@ -15,4 +15,6 @@
 pidfile=/var/run/zuul/zuul.pid
 state_dir=/var/lib/zuul
 git_dir=/var/lib/zuul/git
+;git_user_email=zuul@example.com
+;git_user_name=zuul
 status_url=https://jenkins.example.com/zuul/status
diff --git a/tests/fixtures/zuul.conf b/tests/fixtures/zuul.conf
index c031ae3..3054c23 100644
--- a/tests/fixtures/zuul.conf
+++ b/tests/fixtures/zuul.conf
@@ -11,5 +11,7 @@
 [zuul]
 layout_config=layout.yaml
 git_dir=/tmp/zuul-test/git
+git_user_email=zuul@example.com
+git_user_name=zuul
 push_change_refs=true
 url_pattern=http://logs.example.com/{change.number}/{change.patchset}/{pipeline.name}/{job.name}/{build.number}
diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py
index 508af42..2d5fff3 100644
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -74,6 +74,10 @@
     path = os.path.join(UPSTREAM_ROOT, project)
     repo = git.Repo.init(path)
 
+    repo.config_writer().set_value('user', 'email', 'user@example.com')
+    repo.config_writer().set_value('user', 'name', 'User Name')
+    repo.config_writer().write()
+
     fn = os.path.join(path, 'README')
     f = open(fn, 'w')
     f.write("test\n")
diff --git a/zuul/merger.py b/zuul/merger.py
index 7ad7eed..aaffa43 100644
--- a/zuul/merger.py
+++ b/zuul/merger.py
@@ -26,11 +26,16 @@
 class Repo(object):
     log = logging.getLogger("zuul.Repo")
 
-    def __init__(self, remote, local):
+    def __init__(self, remote, local, email, username):
         self.remote_url = remote
         self.local_path = local
         self._ensure_cloned()
         self.repo = git.Repo(self.local_path)
+        if email:
+            self.repo.config_writer().set_value('user', 'email', email)
+        if username:
+            self.repo.config_writer().set_value('user', 'name', username)
+        self.repo.config_writer().write()
 
     def _ensure_cloned(self):
         if not os.path.exists(self.local_path):
@@ -117,7 +122,8 @@
 class Merger(object):
     log = logging.getLogger("zuul.Merger")
 
-    def __init__(self, trigger, working_root, push_refs, sshkey):
+    def __init__(self, trigger, working_root, push_refs, sshkey, email,
+            username):
         self.trigger = trigger
         self.repos = {}
         self.working_root = working_root
@@ -126,6 +132,8 @@
         self.push_refs = push_refs
         if sshkey:
             self._makeSSHWrapper(sshkey)
+        self.email = email
+        self.username = username
 
     def _makeSSHWrapper(self, key):
         name = os.path.join(self.working_root, '.ssh_wrapper')
@@ -139,7 +147,8 @@
     def addProject(self, project, url):
         try:
             path = os.path.join(self.working_root, project.name)
-            repo = Repo(url, path)
+            repo = Repo(url, path, self.email, self.username)
+
             self.repos[project] = repo
         except:
             self.log.exception("Unable to initialize repo for %s" % project)
diff --git a/zuul/scheduler.py b/zuul/scheduler.py
index 8ff5a27..cf862e3 100644
--- a/zuul/scheduler.py
+++ b/zuul/scheduler.py
@@ -246,6 +246,16 @@
         else:
             merge_root = '/var/lib/zuul/git'
 
+        if self.config.has_option('zuul', 'git_user_email'):
+            merge_email = self.config.get('zuul', 'git_user_email')
+        else:
+            merge_email = None
+
+        if self.config.has_option('zuul', 'git_user_name'):
+            merge_name = self.config.get('zuul', 'git_user_name')
+        else:
+            merge_name = None
+
         if self.config.has_option('zuul', 'push_change_refs'):
             push_refs = self.config.getboolean('zuul', 'push_change_refs')
         else:
@@ -257,7 +267,7 @@
             sshkey = None
 
         self.merger = merger.Merger(self.trigger, merge_root, push_refs,
-                                    sshkey)
+                                    sshkey, merge_email, merge_name)
         for project in self.projects.values():
             url = self.trigger.getGitUrl(project)
             self.merger.addProject(project, url)