Merge "Log items in loops better" into feature/zuulv3
diff --git a/doc/source/user/jobs.rst b/doc/source/user/jobs.rst
index c2c376e..aad43d7 100644
--- a/doc/source/user/jobs.rst
+++ b/doc/source/user/jobs.rst
@@ -107,8 +107,178 @@
~~~~~~~~~~~~~~
Zuul supplies not only the variables specified by the job definition
-to Ansible, but also some variables from the executor itself. They
-are:
+to Ansible, but also some variables from the Zuul itself.
+
+When a pipeline is triggered by an action, it enqueues items which may
+vary based on the pipeline's configuration. For example, when a new
+change is created, that change may be enqueued into the pipeline,
+while a tag may be enqueued into the pipeline when it is pushed.
+
+Information about these items is available to jobs. All of the items
+enqueued in a pipeline are git references, and therefore share some
+attributes in common. But other attributes may vary based on the type
+of item.
+
+All items provide the following information as Ansible variables:
+
+**zuul.build**
+ The UUID of the build. A build is a single execution of a job.
+ When an item is enqueued into a pipeline, this usually results in
+ one build of each job configured for that item's project. However,
+ items may be re-enqueued in which case another build may run. In
+ dependent pipelines, the same job may run multiple times for the
+ same item as circumstances change ahead in the queue. Each time a
+ job is run, for whatever reason, it is acompanied with a new
+ unique id.
+
+**zuul.buildset**
+ The build set UUID. When Zuul runs jobs for an item, the collection
+ of those jobs is known as a buildset. If the configuration of items
+ ahead in a dependent pipeline changes, Zuul creates a new buildset
+ and restarts all of the jobs.
+
+**zuul.ref**
+ The git ref of the item. This will be the full path (e.g.,
+ 'refs/heads/master' or 'refs/changes/...').
+
+**zuul.pipeline**
+ The name of the pipeline in which the job is being run.
+
+**zuul.job**
+ The name of the job being run.
+
+**zuul.project**
+ The item's project. This is a data structure with the following
+ fields:
+
+**zuul.project.name**
+ The name of the project, excluding hostname. E.g., `org/project`.
+
+**zuul.project.canonical_hostname**
+ The canonical hostname where the project lives. E.g.,
+ `git.example.com`.
+
+**zuul.project.canonical_name**
+ The full canonical name of the project including hostname. E.g.,
+ `git.example.com/org/project`.
+
+**zuul.tenant**
+ The name of the current Zuul tenant.
+
+**zuul.jobtags**
+ A list of tags associated with the job. Not to be confused with git
+ tags, these are simply free-form text fields that can be used by the
+ job for reporting or classification purposes.
+
+**zuul.items**
+
+ A list of dictionaries, each representing an item being tested with
+ this change with the format:
+
+ **project.name**
+ The name of the project, excluding hostname. E.g., `org/project`.
+
+ **project.canonical_hostname**
+ The canonical hostname where the project lives. E.g.,
+ `git.example.com`.
+
+ **project.canonical_name**
+ The full canonical name of the project including hostname. E.g.,
+ `git.example.com/org/project`.
+
+ **branch**
+ The target branch of the change (without the `refs/heads/` prefix).
+
+ **change**
+ The identifier for the change.
+
+ **patchset**
+ The patchset identifier for the change. If a change is revised,
+ this will have a different value.
+
+Change Items
+++++++++++++
+
+A change to the repository. Most often, this will be a git reference
+which has not yet been merged into the repository (e.g., a gerrit
+change or a GitHub pull request). The following additional variables
+are available:
+
+**zuul.branch**
+ The target branch of the change (without the `refs/heads/` prefix).
+
+**zuul.change**
+ The identifier for the change.
+
+**zuul.patchset**
+ The patchset identifier for the change. If a change is revised,
+ this will have a different value.
+
+Branch Items
+++++++++++++
+
+This represents a branch tip. This item may have been enqueued
+because the branch was updated (via a change having merged, or a
+direct push). Or it may have been enqueued by a timer for the purpose
+of verifying the current condition of the branch. The following
+additional variables are available:
+
+**zuul.branch**
+ The name of the item's branch (without the `refs/heads/` prefix).
+
+**zuul.oldrev**
+ If the item was enqueued as the result of a change merging or being
+ pushed to the branch, the git sha of the old revision will be
+ included here. Otherwise, this value will not be present.
+
+**zuul.newrev**
+ If the item was enqueued as the result of a change merging or being
+ pushed to the branch, the git sha of the new revision will be
+ included here. Otherwise, this value will not be present.
+
+Tag Items
++++++++++
+
+This represents a git tag. The item may have been enqueued because a
+tag was created or deleted. The following additional variables are
+available:
+
+**zuul.tag**
+ The name of the item's tag (without the `refs/tags/` prefix).
+
+**zuul.oldrev**
+ If the item was enqueued as the result of a tag being created or
+ deleted the git sha of the old revision will be included here.
+ Otherwise, this value will not be present.
+
+**zuul.newrev**
+ If the item was enqueued as the result of a tag being created or
+ deleted the git sha of the new revision will be included here.
+ Otherwise, this value will not be present.
+
+Ref Items
++++++++++
+
+This represents a git reference that is neither a change, branch, or
+tag. Note that all items include a `ref` attribute which may be used
+to identify the ref. The following additional variables are
+available:
+
+**zuul.oldrev**
+ If the item was enqueued as the result of a ref being created,
+ deleted, or changed the git sha of the old revision will be included
+ here. Otherwise, this value will not be present.
+
+**zuul.newrev**
+ If the item was enqueued as the result of a ref being created,
+ deleted, or changed the git sha of the new revision will be included
+ here. Otherwise, this value will not be present.
+
+Working Directory
++++++++++++++++++
+
+Additionally, some information about the working directory and the
+executor running the job is available:
**zuul.executor.hostname**
The hostname of the executor.
diff --git a/etc/status/public_html/jquery.zuul.js b/etc/status/public_html/jquery.zuul.js
index aec7a46..1937cd5 100644
--- a/etc/status/public_html/jquery.zuul.js
+++ b/etc/status/public_html/jquery.zuul.js
@@ -96,7 +96,15 @@
job: function(job) {
var $job_line = $('<span />');
- if (job.url !== null) {
+ if (job.result !== null) {
+ $job_line.append(
+ $('<a />')
+ .addClass('zuul-job-name')
+ .attr('href', job.report_url)
+ .text(job.name)
+ );
+ }
+ else if (job.url !== null) {
$job_line.append(
$('<a />')
.addClass('zuul-job-name')
diff --git a/tests/base.py b/tests/base.py
index 2568188..2c478ad 100755
--- a/tests/base.py
+++ b/tests/base.py
@@ -1068,8 +1068,10 @@
self.__dict__.update(kw)
def __repr__(self):
- return ("<Completed build, result: %s name: %s uuid: %s changes: %s>" %
- (self.result, self.name, self.uuid, self.changes))
+ return ("<Completed build, result: %s name: %s uuid: %s "
+ "changes: %s ref: %s>" %
+ (self.result, self.name, self.uuid,
+ self.changes, self.ref))
class FakeStatsd(threading.Thread):
@@ -1344,6 +1346,7 @@
self.executor_server.build_history.append(
BuildHistory(name=build.name, result=result, changes=build.changes,
node=build.node, uuid=build.unique,
+ ref=build.parameters['zuul']['ref'],
parameters=build.parameters, jobdir=build.jobdir,
pipeline=build.parameters['ZUUL_PIPELINE'])
)
diff --git a/tests/fixtures/config/ansible/git/bare-role/tasks/main.yaml b/tests/fixtures/config/ansible/git/bare-role/tasks/main.yaml
index 75943b1..cd8917d 100644
--- a/tests/fixtures/config/ansible/git/bare-role/tasks/main.yaml
+++ b/tests/fixtures/config/ansible/git/bare-role/tasks/main.yaml
@@ -1,3 +1,3 @@
- file:
- path: "{{zuul._test.test_root}}/{{zuul.uuid}}.bare-role.flag"
+ path: "{{zuul._test.test_root}}/{{zuul.build}}.bare-role.flag"
state: touch
diff --git a/tests/fixtures/config/ansible/git/common-config/playbooks/post.yaml b/tests/fixtures/config/ansible/git/common-config/playbooks/post.yaml
index 2e512b1..7fd8a2b 100644
--- a/tests/fixtures/config/ansible/git/common-config/playbooks/post.yaml
+++ b/tests/fixtures/config/ansible/git/common-config/playbooks/post.yaml
@@ -1,5 +1,5 @@
- hosts: all
tasks:
- file:
- path: "{{zuul._test.test_root}}/{{zuul.uuid}}.post.flag"
+ path: "{{zuul._test.test_root}}/{{zuul.build}}.post.flag"
state: touch
diff --git a/tests/fixtures/config/ansible/git/common-config/playbooks/pre.yaml b/tests/fixtures/config/ansible/git/common-config/playbooks/pre.yaml
index f4222ff..268cd65 100644
--- a/tests/fixtures/config/ansible/git/common-config/playbooks/pre.yaml
+++ b/tests/fixtures/config/ansible/git/common-config/playbooks/pre.yaml
@@ -1,5 +1,5 @@
- hosts: all
tasks:
- file:
- path: "{{zuul._test.test_root}}/{{zuul.uuid}}.pre.flag"
+ path: "{{zuul._test.test_root}}/{{zuul.build}}.pre.flag"
state: touch
diff --git a/tests/fixtures/config/ansible/git/common-config/playbooks/python27.yaml b/tests/fixtures/config/ansible/git/common-config/playbooks/python27.yaml
index 3371a20..6669f23 100644
--- a/tests/fixtures/config/ansible/git/common-config/playbooks/python27.yaml
+++ b/tests/fixtures/config/ansible/git/common-config/playbooks/python27.yaml
@@ -4,10 +4,10 @@
path: "{{flagpath}}"
state: touch
- copy:
- src: "{{zuul._test.test_root}}/{{zuul.uuid}}.flag"
- dest: "{{zuul._test.test_root}}/{{zuul.uuid}}.copied"
+ src: "{{zuul._test.test_root}}/{{zuul.build}}.flag"
+ dest: "{{zuul._test.test_root}}/{{zuul.build}}.copied"
- copy:
content: "{{test_secret.username}} {{test_secret.password}}"
- dest: "{{zuul._test.test_root}}/{{zuul.uuid}}.secrets"
+ dest: "{{zuul._test.test_root}}/{{zuul.build}}.secrets"
roles:
- bare-role
diff --git a/tests/fixtures/config/ansible/git/common-config/zuul.yaml b/tests/fixtures/config/ansible/git/common-config/zuul.yaml
index aa57d08..1a1b22f 100644
--- a/tests/fixtures/config/ansible/git/common-config/zuul.yaml
+++ b/tests/fixtures/config/ansible/git/common-config/zuul.yaml
@@ -59,7 +59,7 @@
pre-run: playbooks/pre
post-run: playbooks/post
vars:
- flagpath: '{{zuul._test.test_root}}/{{zuul.uuid}}.flag'
+ flagpath: '{{zuul._test.test_root}}/{{zuul.build}}.flag'
roles:
- zuul: bare-role
auth:
diff --git a/tests/fixtures/config/ansible/git/org_project/playbooks/faillocal.yaml b/tests/fixtures/config/ansible/git/org_project/playbooks/faillocal.yaml
index 6689e18..5b0c18d 100644
--- a/tests/fixtures/config/ansible/git/org_project/playbooks/faillocal.yaml
+++ b/tests/fixtures/config/ansible/git/org_project/playbooks/faillocal.yaml
@@ -1,5 +1,5 @@
- hosts: all
tasks:
- copy:
- src: "{{zuul._test.test_root}}/{{zuul.uuid}}.flag"
- dest: "{{zuul._test.test_root}}/{{zuul.uuid}}.failed"
+ src: "{{zuul._test.test_root}}/{{zuul.build}}.flag"
+ dest: "{{zuul._test.test_root}}/{{zuul.build}}.failed"
diff --git a/tests/fixtures/config/pre-playbook/git/common-config/playbooks/post.yaml b/tests/fixtures/config/pre-playbook/git/common-config/playbooks/post.yaml
index 2e512b1..7fd8a2b 100644
--- a/tests/fixtures/config/pre-playbook/git/common-config/playbooks/post.yaml
+++ b/tests/fixtures/config/pre-playbook/git/common-config/playbooks/post.yaml
@@ -1,5 +1,5 @@
- hosts: all
tasks:
- file:
- path: "{{zuul._test.test_root}}/{{zuul.uuid}}.post.flag"
+ path: "{{zuul._test.test_root}}/{{zuul.build}}.post.flag"
state: touch
diff --git a/tests/fixtures/config/pre-playbook/git/common-config/playbooks/pre.yaml b/tests/fixtures/config/pre-playbook/git/common-config/playbooks/pre.yaml
index 13c2208..4875ad4 100644
--- a/tests/fixtures/config/pre-playbook/git/common-config/playbooks/pre.yaml
+++ b/tests/fixtures/config/pre-playbook/git/common-config/playbooks/pre.yaml
@@ -1,8 +1,8 @@
- hosts: all
tasks:
- copy:
- src: "{{zuul._test.test_root}}/{{zuul.uuid}}.flag"
- dest: "{{zuul._test.test_root}}/{{zuul.uuid}}.failed"
+ src: "{{zuul._test.test_root}}/{{zuul.build}}.flag"
+ dest: "{{zuul._test.test_root}}/{{zuul.build}}.failed"
- file:
- path: "{{zuul._test.test_root}}/{{zuul.uuid}}.pre.flag"
+ path: "{{zuul._test.test_root}}/{{zuul.build}}.pre.flag"
state: touch
diff --git a/tests/fixtures/config/pre-playbook/git/common-config/playbooks/python27.yaml b/tests/fixtures/config/pre-playbook/git/common-config/playbooks/python27.yaml
index dbb64a5..08c7bd3 100644
--- a/tests/fixtures/config/pre-playbook/git/common-config/playbooks/python27.yaml
+++ b/tests/fixtures/config/pre-playbook/git/common-config/playbooks/python27.yaml
@@ -1,5 +1,5 @@
- hosts: all
tasks:
- file:
- path: "{{zuul._test.test_root}}/{{zuul.uuid}}.main.flag"
+ path: "{{zuul._test.test_root}}/{{zuul.build}}.main.flag"
state: touch
diff --git a/tests/fixtures/config/roles/git/bare-role/tasks/main.yaml b/tests/fixtures/config/roles/git/bare-role/tasks/main.yaml
index 75943b1..cd8917d 100644
--- a/tests/fixtures/config/roles/git/bare-role/tasks/main.yaml
+++ b/tests/fixtures/config/roles/git/bare-role/tasks/main.yaml
@@ -1,3 +1,3 @@
- file:
- path: "{{zuul._test.test_root}}/{{zuul.uuid}}.bare-role.flag"
+ path: "{{zuul._test.test_root}}/{{zuul.build}}.bare-role.flag"
state: touch
diff --git a/tests/fixtures/config/streamer/git/common-config/zuul.yaml b/tests/fixtures/config/streamer/git/common-config/zuul.yaml
index d8df96a..6f4fa7e 100644
--- a/tests/fixtures/config/streamer/git/common-config/zuul.yaml
+++ b/tests/fixtures/config/streamer/git/common-config/zuul.yaml
@@ -14,4 +14,4 @@
- job:
name: python27
vars:
- waitpath: '{{zuul._test.test_root}}/{{zuul.uuid}}/test_wait'
+ waitpath: '{{zuul._test.test_root}}/{{zuul.build}}/test_wait'
diff --git a/tests/fixtures/layouts/idle.yaml b/tests/fixtures/layouts/idle.yaml
index 60f8ed1..49c45ac 100644
--- a/tests/fixtures/layouts/idle.yaml
+++ b/tests/fixtures/layouts/idle.yaml
@@ -6,20 +6,14 @@
- time: '* * * * * */1'
- job:
- name: project-bitrot-stable-old
+ name: project-bitrot
nodes:
- name: static
label: ubuntu-xenial
-- job:
- name: project-bitrot-stable-older
- nodes:
- - name: static
- label: ubuntu-trusty
-
- project:
name: org/project
periodic:
jobs:
- - project-bitrot-stable-old
- - project-bitrot-stable-older
+ - project-bitrot
+
diff --git a/tests/fixtures/layouts/no-timer.yaml b/tests/fixtures/layouts/no-timer.yaml
index 12eaa35..05f17d2 100644
--- a/tests/fixtures/layouts/no-timer.yaml
+++ b/tests/fixtures/layouts/no-timer.yaml
@@ -24,17 +24,11 @@
name: project-test1
- job:
- name: project-bitrot-stable-old
+ name: project-bitrot
nodes:
- name: static
label: ubuntu-xenial
-- job:
- name: project-bitrot-stable-older
- nodes:
- - name: static
- label: ubuntu-trusty
-
- project:
name: org/project
check:
@@ -42,5 +36,4 @@
- project-test1
periodic:
jobs:
- - project-bitrot-stable-old
- - project-bitrot-stable-older
+ - project-bitrot
diff --git a/tests/fixtures/layouts/repo-checkout-timer-override.yaml b/tests/fixtures/layouts/repo-checkout-timer-override.yaml
new file mode 100644
index 0000000..594d74c
--- /dev/null
+++ b/tests/fixtures/layouts/repo-checkout-timer-override.yaml
@@ -0,0 +1,19 @@
+- pipeline:
+ name: periodic
+ manager: independent
+ trigger:
+ timer:
+ - time: '* * * * * */1'
+
+- job:
+ name: integration
+ branches: master
+ override-branch: stable/havana
+ required-projects:
+ - org/project1
+
+- project:
+ name: org/project1
+ periodic:
+ jobs:
+ - integration
diff --git a/tests/fixtures/layouts/repo-checkout-timer.yaml b/tests/fixtures/layouts/repo-checkout-timer.yaml
index d5917d1..3c4d030 100644
--- a/tests/fixtures/layouts/repo-checkout-timer.yaml
+++ b/tests/fixtures/layouts/repo-checkout-timer.yaml
@@ -7,7 +7,6 @@
- job:
name: integration
- override-branch: stable/havana
required-projects:
- org/project1
diff --git a/tests/fixtures/layouts/timer.yaml b/tests/fixtures/layouts/timer.yaml
index 883c32e..dbce516 100644
--- a/tests/fixtures/layouts/timer.yaml
+++ b/tests/fixtures/layouts/timer.yaml
@@ -25,17 +25,11 @@
name: project-test2
- job:
- name: project-bitrot-stable-old
+ name: project-bitrot
nodes:
- name: static
label: ubuntu-xenial
-- job:
- name: project-bitrot-stable-older
- nodes:
- - name: static
- label: ubuntu-trusty
-
- project:
name: org/project
check:
@@ -44,5 +38,4 @@
- project-test2
periodic:
jobs:
- - project-bitrot-stable-old
- - project-bitrot-stable-older
+ - project-bitrot
diff --git a/tests/unit/test_executor.py b/tests/unit/test_executor.py
index 7b76802..4700bd1 100755
--- a/tests/unit/test_executor.py
+++ b/tests/unit/test_executor.py
@@ -248,6 +248,46 @@
self.assertBuildStates(states, projects)
+ def test_periodic_override(self):
+ # This test can not use simple_layout because it must start
+ # with a configuration which does not include a
+ # timer-triggered job so that we have an opportunity to set
+ # the hold flag before the first job.
+
+ # This tests that we can override the branch in a timer
+ # trigger (mostly to ensure backwards compatability for jobs).
+ self.executor_server.hold_jobs_in_build = True
+ # Start timer trigger - also org/project
+ self.commitConfigUpdate('common-config',
+ 'layouts/repo-checkout-timer-override.yaml')
+ self.sched.reconfigure(self.config)
+
+ p1 = 'review.example.com/org/project1'
+ projects = [p1]
+ self.create_branch('org/project1', 'stable/havana')
+
+ # The pipeline triggers every second, so we should have seen
+ # several by now.
+ time.sleep(5)
+ self.waitUntilSettled()
+
+ # Stop queuing timer triggered jobs so that the assertions
+ # below don't race against more jobs being queued.
+ self.commitConfigUpdate('common-config',
+ 'layouts/repo-checkout-no-timer.yaml')
+ self.sched.reconfigure(self.config)
+
+ self.assertEquals(1, len(self.builds), "One build is running")
+
+ upstream = self.getUpstreamRepos(projects)
+ states = [
+ {p1: dict(commit=str(upstream[p1].commit('stable/havana')),
+ branch='stable/havana'),
+ },
+ ]
+
+ self.assertBuildStates(states, projects)
+
def test_periodic(self):
# This test can not use simple_layout because it must start
# with a configuration which does not include a
@@ -274,14 +314,19 @@
'layouts/repo-checkout-no-timer.yaml')
self.sched.reconfigure(self.config)
- self.assertEquals(1, len(self.builds), "One build is running")
+ self.assertEquals(2, len(self.builds), "Two builds are running")
upstream = self.getUpstreamRepos(projects)
states = [
{p1: dict(commit=str(upstream[p1].commit('stable/havana')),
branch='stable/havana'),
},
+ {p1: dict(commit=str(upstream[p1].commit('master')),
+ branch='master'),
+ },
]
+ if self.builds[0].parameters['zuul']['ref'] == 'refs/heads/master':
+ states = list(reversed(states))
self.assertBuildStates(states, projects)
diff --git a/tests/unit/test_github_driver.py b/tests/unit/test_github_driver.py
index 0cfe3da..8493570 100644
--- a/tests/unit/test_github_driver.py
+++ b/tests/unit/test_github_driver.py
@@ -12,11 +12,14 @@
# License for the specific language governing permissions and limitations
# under the License.
+import os
import re
from testtools.matchers import MatchesRegex, StartsWith
import urllib
import time
+import git
+
from tests.base import ZuulTestCase, simple_layout, random_sha1
@@ -94,7 +97,16 @@
def test_tag_event(self):
self.executor_server.hold_jobs_in_build = True
- sha = random_sha1()
+ self.create_branch('org/project', 'tagbranch')
+ files = {'README.txt': 'test'}
+ self.addCommitToRepo('org/project', 'test tag',
+ files, branch='tagbranch', tag='newtag')
+ path = os.path.join(self.upstream_root, 'org/project')
+ repo = git.Repo(path)
+ tag = repo.tags['newtag']
+ sha = tag.commit.hexsha
+ del repo
+
self.fake_github.emitEvent(
self.fake_github.getPushEvent('org/project', 'refs/tags/newtag',
new_rev=sha))
diff --git a/tests/unit/test_scheduler.py b/tests/unit/test_scheduler.py
index 0229d65..d4290a9 100755
--- a/tests/unit/test_scheduler.py
+++ b/tests/unit/test_scheduler.py
@@ -1834,17 +1834,17 @@
self.commitConfigUpdate('common-config', 'layouts/no-timer.yaml')
self.sched.reconfigure(self.config)
- self.assertEqual(len(self.builds), 2, "Two timer jobs")
+ self.assertEqual(len(self.builds), 1, "One timer job")
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
- self.assertEqual(len(self.builds), 3, "One change plus two timer jobs")
+ self.assertEqual(len(self.builds), 2, "One change plus one timer job")
self.fake_gerrit.addEvent(A.getChangeAbandonedEvent())
self.waitUntilSettled()
- self.assertEqual(len(self.builds), 2, "Two timer jobs remain")
+ self.assertEqual(len(self.builds), 1, "One timer job remains")
self.executor_server.release()
self.waitUntilSettled()
@@ -2791,13 +2791,13 @@
self.assertEqual(len(self.history), 2)
results = {self.getJobFromHistory('merge',
- project='org/project1').uuid: 'extratag merge',
+ project='org/project1').uuid: ['extratag', 'merge'],
self.getJobFromHistory('merge',
- project='org/project2').uuid: 'merge'}
+ project='org/project2').uuid: ['merge']}
for build in self.history:
self.assertEqual(results.get(build.uuid, ''),
- build.parameters['zuul'].get('tags'))
+ build.parameters['zuul'].get('jobtags'))
def test_timer(self):
"Test that a periodic job is triggered"
@@ -2805,6 +2805,7 @@
# with a configuration which does not include a
# timer-triggered job so that we have an opportunity to set
# the hold flag before the first job.
+ self.create_branch('org/project', 'stable')
self.executor_server.hold_jobs_in_build = True
self.commitConfigUpdate('common-config', 'layouts/timer.yaml')
self.sched.reconfigure(self.config)
@@ -2831,10 +2832,12 @@
self.executor_server.release()
self.waitUntilSettled()
- self.assertEqual(self.getJobFromHistory(
- 'project-bitrot-stable-old').result, 'SUCCESS')
- self.assertEqual(self.getJobFromHistory(
- 'project-bitrot-stable-older').result, 'SUCCESS')
+ self.assertHistory([
+ dict(name='project-bitrot', result='SUCCESS',
+ ref='refs/heads/master'),
+ dict(name='project-bitrot', result='SUCCESS',
+ ref='refs/heads/stable'),
+ ], ordered=False)
data = json.loads(data)
status_jobs = set()
@@ -2844,8 +2847,7 @@
for change in head:
for job in change['jobs']:
status_jobs.add(job['name'])
- self.assertIn('project-bitrot-stable-old', status_jobs)
- self.assertIn('project-bitrot-stable-older', status_jobs)
+ self.assertIn('project-bitrot', status_jobs)
def test_idle(self):
"Test that frequent periodic jobs work"
@@ -2874,12 +2876,12 @@
'layouts/no-timer.yaml')
self.sched.reconfigure(self.config)
self.waitUntilSettled()
- self.assertEqual(len(self.builds), 2,
+ self.assertEqual(len(self.builds), 1,
'Timer builds iteration #%d' % x)
self.executor_server.release('.*')
self.waitUntilSettled()
self.assertEqual(len(self.builds), 0)
- self.assertEqual(len(self.history), x * 2)
+ self.assertEqual(len(self.history), x)
@simple_layout('layouts/smtp.yaml')
def test_check_smtp_pool(self):
diff --git a/tests/unit/test_v3.py b/tests/unit/test_v3.py
index b162469..fb80660 100644
--- a/tests/unit/test_v3.py
+++ b/tests/unit/test_v3.py
@@ -667,7 +667,8 @@
self.assertFalse(os.path.exists(pre_flag_path))
post_flag_path = os.path.join(self.test_root, build.uuid +
'.post.flag')
- self.assertTrue(os.path.exists(post_flag_path))
+ self.assertTrue(os.path.exists(post_flag_path),
+ "The file %s should exist" % post_flag_path)
class TestBrokenConfig(ZuulTestCase):
diff --git a/zuul/driver/gerrit/gerritconnection.py b/zuul/driver/gerrit/gerritconnection.py
index a5eb12e..8f8465a 100644
--- a/zuul/driver/gerrit/gerritconnection.py
+++ b/zuul/driver/gerrit/gerritconnection.py
@@ -26,7 +26,7 @@
import voluptuous as v
from zuul.connection import BaseConnection
-from zuul.model import Ref
+from zuul.model import Ref, Tag, Branch
from zuul import exceptions
from zuul.driver.gerrit.gerritmodel import GerritChange, GerritTriggerEvent
@@ -293,7 +293,34 @@
if event.change_number:
change = self._getChange(event.change_number, event.patch_number,
refresh=refresh)
+ elif event.ref and event.ref.startswith('refs/tags/'):
+ project = self.source.getProject(event.project_name)
+ change = Tag(project)
+ change.tag = event.ref[len('refs/tags/'):]
+ change.ref = event.ref
+ change.oldrev = event.oldrev
+ change.newrev = event.newrev
+ change.url = self._getGitwebUrl(project, sha=event.newrev)
+ elif event.ref and not event.ref.startswith('refs/'):
+ # Gerrit ref-updated events don't have branch prefixes.
+ project = self.source.getProject(event.project_name)
+ change = Branch(project)
+ change.branch = event.ref
+ change.ref = 'refs/heads/' + event.ref
+ change.oldrev = event.oldrev
+ change.newrev = event.newrev
+ change.url = self._getGitwebUrl(project, sha=event.newrev)
+ elif event.ref and event.ref.startswith('refs/heads/'):
+ # From the timer trigger
+ project = self.source.getProject(event.project_name)
+ change = Branch(project)
+ change.ref = event.ref
+ change.branch = event.branch
+ change.oldrev = event.oldrev
+ change.newrev = event.newrev
+ change.url = self._getGitwebUrl(project, sha=event.newrev)
elif event.ref:
+ # catch-all ref (ie, not a branch or head)
project = self.source.getProject(event.project_name)
change = Ref(project)
change.ref = event.ref
@@ -301,14 +328,8 @@
change.newrev = event.newrev
change.url = self._getGitwebUrl(project, sha=event.newrev)
else:
- project = self.source.getProject(event.project_name)
- change = Ref(project)
- branch = event.branch or 'master'
- change.ref = 'refs/heads/%s' % branch
- refs = self.getInfoRefs(project)
- change.oldrev = refs[change.ref]
- change.newrev = refs[change.ref]
- change.url = self._getGitwebUrl(project, sha=change.newrev)
+ self.log.warning("Unable to get change for %s" % (event,))
+ change = None
return change
def _getChange(self, number, patchset, refresh=False, history=None):
diff --git a/zuul/driver/github/githubconnection.py b/zuul/driver/github/githubconnection.py
index a4a4c12..ff113ce 100644
--- a/zuul/driver/github/githubconnection.py
+++ b/zuul/driver/github/githubconnection.py
@@ -32,7 +32,7 @@
from github3.exceptions import MethodNotAllowed
from zuul.connection import BaseConnection
-from zuul.model import Ref
+from zuul.model import Ref, Branch, Tag
from zuul.exceptions import MergeFailure
from zuul.driver.github.githubmodel import PullRequest, GithubTriggerEvent
@@ -506,16 +506,21 @@
change.source_event = event
change.is_current_patchset = (change.pr.get('head').get('sha') ==
event.patch_number)
- elif event.ref:
- change = Ref(project)
+ else:
+ if event.ref and event.ref.startswith('refs/tags/'):
+ change = Tag(project)
+ change.tag = event.ref[len('refs/tags/'):]
+ elif event.ref and event.ref.startswith('refs/heads/'):
+ change = Branch(project)
+ change.branch = event.ref[len('refs/heads/'):]
+ else:
+ change = Ref(project)
change.ref = event.ref
change.oldrev = event.oldrev
change.newrev = event.newrev
change.url = self.getGitwebUrl(project, sha=event.newrev)
change.source_event = event
change.files = self.getPushedFileNames(event)
- else:
- change = Ref(project)
return change
def _getChange(self, project, number, patchset=None, refresh=False,
diff --git a/zuul/driver/timer/__init__.py b/zuul/driver/timer/__init__.py
index cdaea74..4489808 100644
--- a/zuul/driver/timer/__init__.py
+++ b/zuul/driver/timer/__init__.py
@@ -80,15 +80,18 @@
def _onTrigger(self, tenant, pipeline_name, timespec):
for project_name in tenant.layout.project_configs.keys():
- project_hostname, project_name = project_name.split('/', 1)
- event = TimerTriggerEvent()
- event.type = 'timer'
- event.timespec = timespec
- event.forced_pipeline = pipeline_name
- event.project_hostname = project_hostname
- event.project_name = project_name
- self.log.debug("Adding event %s" % event)
- self.sched.addEvent(event)
+ (trusted, project) = tenant.getProject(project_name)
+ for branch in project.source.getProjectBranches(project):
+ event = TimerTriggerEvent()
+ event.type = 'timer'
+ event.timespec = timespec
+ event.forced_pipeline = pipeline_name
+ event.project_hostname = project.canonical_hostname
+ event.project_name = project.name
+ event.ref = 'refs/heads/%s' % branch
+ event.branch = branch
+ self.log.debug("Adding event %s" % event)
+ self.sched.addEvent(event)
def stop(self):
if self.apsched:
diff --git a/zuul/executor/client.py b/zuul/executor/client.py
index f764778..52cc403 100644
--- a/zuul/executor/client.py
+++ b/zuul/executor/client.py
@@ -155,20 +155,41 @@
canonical_hostname=item.change.project.canonical_hostname,
canonical_name=item.change.project.canonical_name)
- zuul_params = dict(uuid=uuid,
- ref=item.current_build_set.ref,
+ zuul_params = dict(build=uuid,
+ buildset=item.current_build_set.uuid,
+ ref=item.change.ref,
pipeline=pipeline.name,
job=job.name,
project=project,
tenant=tenant.name,
- tags=' '.join(sorted(job.tags)))
-
+ jobtags=sorted(job.tags))
if hasattr(item.change, 'branch'):
zuul_params['branch'] = item.change.branch
+ if hasattr(item.change, 'tag'):
+ zuul_params['tag'] = item.change.tag
if hasattr(item.change, 'number'):
zuul_params['change'] = item.change.number
if hasattr(item.change, 'patchset'):
zuul_params['patchset'] = item.change.patchset
+ if hasattr(item.change, 'oldrev'):
+ zuul_params['oldrev'] = item.change.oldrev
+ if hasattr(item.change, 'newrev'):
+ zuul_params['newrev'] = item.change.newrev
+ zuul_params['items'] = []
+ for i in all_items:
+ d = dict()
+ d['project'] = dict(
+ name=i.change.project.name,
+ canonical_hostname=i.change.project.canonical_hostname,
+ canonical_name=i.change.project.canonical_name)
+ if hasattr(i.change, 'number'):
+ d['change'] = i.change.number
+ if hasattr(i.change, 'patchset'):
+ d['patchset'] = i.change.number
+ if hasattr(i.change, 'branch'):
+ d['branch'] = i.change.branch
+ zuul_params['items'].append(d)
+
# Legacy environment variables
params = dict(ZUUL_UUID=uuid,
ZUUL_PROJECT=item.change.project.name)
diff --git a/zuul/executor/server.py b/zuul/executor/server.py
index 9b5ecc7..ef5363c 100644
--- a/zuul/executor/server.py
+++ b/zuul/executor/server.py
@@ -858,8 +858,18 @@
for project in args['projects']:
repo = repos[project['canonical_name']]
+ # If this project is the Zuul project and this is a ref
+ # rather than a change, checkout the ref.
+ if (project['canonical_name'] ==
+ args['zuul']['project']['canonical_name'] and
+ (not args['zuul'].get('branch')) and
+ args['zuul'].get('ref')):
+ ref = args['zuul']['ref']
+ else:
+ ref = None
self.checkoutBranch(repo,
project['name'],
+ ref,
args['branch'],
args['override_branch'],
project['override_branch'],
@@ -929,7 +939,7 @@
repo.setRef('refs/heads/' + branch, commit)
return True
- def checkoutBranch(self, repo, project_name, zuul_branch,
+ def checkoutBranch(self, repo, project_name, ref, zuul_branch,
job_branch, project_override_branch,
project_default_branch):
branches = repo.getBranches()
@@ -941,6 +951,16 @@
self.log.info("Checking out %s job branch %s",
project_name, job_branch)
repo.checkoutLocalBranch(job_branch)
+ elif ref and ref.startswith('refs/heads/'):
+ b = ref[len('refs/heads/'):]
+ self.log.info("Checking out %s branch ref %s",
+ project_name, b)
+ repo.checkoutLocalBranch(b)
+ elif ref and ref.startswith('refs/tags/'):
+ t = ref[len('refs/tags/'):]
+ self.log.info("Checking out %s tag ref %s",
+ project_name, t)
+ repo.checkout(t)
elif zuul_branch and zuul_branch in branches:
self.log.info("Checking out %s zuul branch %s",
project_name, zuul_branch)
diff --git a/zuul/model.py b/zuul/model.py
index bc9eeb7..ed77864 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -1842,19 +1842,17 @@
oldrev = None
newrev = None
refspec = None
+ branch = None
if hasattr(self.change, 'number'):
number = self.change.number
patchset = self.change.patchset
refspec = self.change.refspec
- branch = self.change.branch
- elif hasattr(self.change, 'newrev'):
+ if hasattr(self.change, 'newrev'):
oldrev = self.change.oldrev
newrev = self.change.newrev
- branch = self.change.ref
- else:
- oldrev = None
- newrev = None
- branch = None
+ if hasattr(self.change, 'branch'):
+ branch = self.change.branch
+
source = self.change.project.source
connection_name = source.connection.connection_name
project = self.change.project
@@ -1880,32 +1878,26 @@
self.ref = None
self.oldrev = None
self.newrev = None
-
self.files = []
- def getBasePath(self):
- base_path = ''
- if hasattr(self, 'ref'):
- base_path = "%s/%s" % (self.newrev[:2], self.newrev)
-
- return base_path
-
def _id(self):
return self.newrev
def __repr__(self):
rep = None
if self.newrev == '0000000000000000000000000000000000000000':
- rep = '<Ref 0x%x deletes %s from %s' % (
- id(self), self.ref, self.oldrev)
+ rep = '<%s 0x%x deletes %s from %s' % (
+ type(self).__name__,
+ id(self), self.ref, self.oldrev)
elif self.oldrev == '0000000000000000000000000000000000000000':
- rep = '<Ref 0x%x creates %s on %s>' % (
- id(self), self.ref, self.newrev)
+ rep = '<%s 0x%x creates %s on %s>' % (
+ type(self).__name__,
+ id(self), self.ref, self.newrev)
else:
# Catch all
- rep = '<Ref 0x%x %s updated %s..%s>' % (
- id(self), self.ref, self.oldrev, self.newrev)
-
+ rep = '<%s 0x%x %s updated %s..%s>' % (
+ type(self).__name__,
+ id(self), self.ref, self.oldrev, self.newrev)
return rep
def equals(self, other):
@@ -1938,11 +1930,24 @@
newrev=self.newrev)
-class Change(Ref):
+class Branch(Ref):
+ """An existing branch state for a Project."""
+ def __init__(self, project):
+ super(Branch, self).__init__(project)
+ self.branch = None
+
+
+class Tag(Ref):
+ """An existing tag state for a Project."""
+ def __init__(self, project):
+ super(Tag, self).__init__(project)
+ self.tag = None
+
+
+class Change(Branch):
"""A proposed new state for a Project."""
def __init__(self, project):
super(Change, self).__init__(project)
- self.branch = None
self.number = None
self.url = None
self.patchset = None
@@ -1966,12 +1971,6 @@
def __repr__(self):
return '<Change 0x%x %s>' % (id(self), self._id())
- def getBasePath(self):
- if hasattr(self, 'refspec'):
- return "%s/%s/%s" % (
- str(self.number)[-2:], self.number, self.patchset)
- return super(Change, self).getBasePath()
-
def equals(self, other):
if self.number == other.number and self.patchset == other.patchset:
return True