Merge branch 'master' into feature/gearman
Change-Id: I9494196710a96414573b11788ed6007817c9f774
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 2e05cc2..6943bd4 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -216,7 +216,7 @@
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
- ('index', 'zuul', u'Zuul Documentation',
+ ('index', 'zuul-server', u'Zuul Documentation',
[u'OpenStack'], 1)
]
diff --git a/doc/source/zuul.rst b/doc/source/zuul.rst
index 4b61507..4877b30 100644
--- a/doc/source/zuul.rst
+++ b/doc/source/zuul.rst
@@ -91,6 +91,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/status/public_html/app.js b/etc/status/public_html/app.js
index f7de2cc..8ac6108 100644
--- a/etc/status/public_html/app.js
+++ b/etc/status/public_html/app.js
@@ -22,7 +22,7 @@
demo = location.search.match(/[?&]demo=([^?&]*)/),
source = demo ?
'./status-' + (demo[1] || 'basic') + '.json-sample' :
- '/zuul/status.json';
+ 'status.json';
zuul = {
enabled: true,
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/layout.yaml b/tests/fixtures/layout.yaml
index 8d6c852..2695719 100644
--- a/tests/fixtures/layout.yaml
+++ b/tests/fixtures/layout.yaml
@@ -40,6 +40,25 @@
approval:
- approved: 1
+ - name: dup1
+ manager: IndependentPipelineManager
+ trigger:
+ - event: change-restored
+ success:
+ verified: 1
+ failure:
+ verified: -1
+
+ - name: dup2
+ manager: IndependentPipelineManager
+ trigger:
+ - event: change-restored
+ success:
+ verified: 1
+ failure:
+ verified: -1
+
+
jobs:
- name: ^.*-merge$
failure-message: Unable to merge change
@@ -73,6 +92,10 @@
- project-testfile
post:
- project-post
+ dup1:
+ - project-test1
+ dup2:
+ - project-test1
- name: org/project1
check:
diff --git a/tests/fixtures/zuul.conf b/tests/fixtures/zuul.conf
index 8948ae8..7c61de1 100644
--- a/tests/fixtures/zuul.conf
+++ b/tests/fixtures/zuul.conf
@@ -9,5 +9,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 4d3351e..b633ef6 100644
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -78,6 +78,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")
@@ -180,6 +184,7 @@
self.depends_on_change = None
self.needed_by_changes = []
self.fail_merge = False
+ self.messages = []
self.data = {
'branch': branch,
'comments': [],
@@ -245,6 +250,19 @@
"uploader": {"name": "User Name"}}
return event
+ def getChangeRestoredEvent(self):
+ event = {"type": "change-restored",
+ "change": {"project": self.project,
+ "branch": self.branch,
+ "id": "I5459869c07352a31bfb1e7a8cac379cabfcb25af",
+ "number": str(self.number),
+ "subject": self.subject,
+ "owner": {"name": "User Name"},
+ "url": "https://hostname/3"},
+ "restorer": {"name": "User Name"},
+ "reason": ""}
+ return event
+
def addApproval(self, category, value):
approval = {'description': self.categories[category][0],
'type': category,
@@ -376,6 +394,7 @@
def review(self, project, changeid, message, action):
number, ps = changeid.split(',')
change = self.changes[int(number)]
+ change.messages.append(message)
if 'submit' in action:
change.setMerged()
if message:
@@ -956,6 +975,36 @@
self.assertReportedStat(
'zuul.pipeline.gate.org.project.total_changes', '1|c')
+ def test_duplicate_pipelines(self):
+ "Test that a change matching multiple pipelines works"
+ builds = self.worker.running_builds
+ history = self.worker.build_history
+
+ A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
+ self.fake_gerrit.addEvent(A.getChangeRestoredEvent())
+ self.waitUntilSettled()
+
+ print builds
+ print A.messages
+
+ self.assertEmptyQueues()
+
+ assert len(history) == 2
+ history[0].name == 'project-test1'
+ history[1].name == 'project-test1'
+
+ assert len(A.messages) == 2
+ if 'dup1/project-test1' in A.messages[0]:
+ assert 'dup1/project-test1' in A.messages[0]
+ assert 'dup2/project-test1' not in A.messages[0]
+ assert 'dup1/project-test1' not in A.messages[1]
+ assert 'dup2/project-test1' in A.messages[1]
+ else:
+ assert 'dup1/project-test1' in A.messages[1]
+ assert 'dup2/project-test1' not in A.messages[1]
+ assert 'dup1/project-test1' not in A.messages[0]
+ assert 'dup2/project-test1' in A.messages[0]
+
def test_parallel_changes(self):
"Test that changes are tested in parallel and merged in series"
builds = self.worker.running_builds
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)