Add ZUUL_COMMIT.

To facilitate using zuul with just the jenkins git plugin, add
the ZUUL_COMMIT parameter.  Also, always include ZUUL_REF, whether
the job is pre- or post-commit.  If it's pre, it will be a
refs/zuul ref, if it's post, it will be refs/tags or a branch
name.

Change-Id: I88c38a28dcd552b2540095d36caacd10acf167b8
Reviewed-on: https://review.openstack.org/13934
Reviewed-by: Clark Boylan <clark.boylan@gmail.com>
Approved: James E. Blair <corvus@inaugust.com>
Tested-by: Jenkins
This commit is contained in:
James E. Blair 2012-10-01 18:29:08 -07:00 committed by Jenkins
parent 4aa1ad6a8e
commit 81515adbd6
6 changed files with 117 additions and 52 deletions

View File

@ -63,44 +63,71 @@ Zuul will pass some parameters to Jenkins for every job it launches.
Check **This build is parameterized**, and add the following fields Check **This build is parameterized**, and add the following fields
with the type **String Parameter**: with the type **String Parameter**:
**UUID** **ZUUL_UUID**
Zuul provided key to link builds with Gerrit events Zuul provided key to link builds with Gerrit events
**GERRIT_PROJECT** **ZUUL_REF**
Zuul provided project name Zuul provided ref that includes commit(s) to build
**GERRIT_BRANCH** **ZUUL_COMMIT**
Zuul provided branch name The commit SHA1 at the head of ZUUL_REF
**GERRIT_CHANGES**
Zuul provided list of dependent changes to merge
You may find it useful to use the ``GERRIT_*`` variables in your job. Those are the only required parameters. The UUID is needed for Zuul
In particular, ``GERRIT_CHANGES`` indicates the change or changes that to keep track of the build, and the REF and COMMIT parameters are for
should be tested. If Zuul has decided that more than one change use in preparing the git repo for the build. The following parameters
should be merged and tested together, they will all be listed in will be sent for all builds, but are not required so you do not need
``GERRIT_CHANGES``. The format for the description of one change is:: to configure Jenkins to accept them if you do not plan on using them:
project:branch:refspec **ZUUL_PROJECT**
The project that triggered this build
**ZUUL_PIPELINE**
The Zuul pipeline that is building this job
And multiple changes are separated by a carat ("^"). E.g.:: The following parameters are optional and will only be provided for
builds associated with changes (i.e., in response to patchset-created
or comment-added events):
testproject:master:refs/changes/20/420/1^testproject:master:refs/changes/21/421/1" **ZUUL_BRANCH**
The target branch for the change that triggered this build
**ZUUL_CHANGE**
The Gerrit change ID for the change that triggered this build
**ZUUL_CHANGE_IDS**
All of the Gerrit change IDs that are included in this build (useful
when the DependentPipelineManager combines changes for testing)
**ZUUL_PATCHSET**
The Gerrit patchset number for the change that triggered this build
The OpenStack project uses the following script to update the The following parameters are optional and will only be provided for
repository in a workspace and merge appropriate changes: post-merge (ref-updated) builds:
https://github.com/openstack/openstack-ci-puppet/blob/master/modules/jenkins/files/slave_scripts/gerrit-git-prep.sh **ZUUL_OLDREV**
The SHA1 of the old revision at this ref (recall the ref name is
in ZUUL_REF)
**ZUUL_NEWREV**
The SHA1 of the new revision at this ref (recall the ref name is
in ZUUL_REF)
**ZUUL_SHORT_OLDREV**
The shortened (7 character) SHA1 of the old revision
**ZUUL_SHORT_NEWREV**
The shortened (7 character) SHA1 of the new revision
Gerrit events that do not include a change (e.g., ref-updated events In order to test the correct build, configure the Jenkins Git SCM
which are emitted after a git ref is updated (i.e., a commit is merged plugin as follows::
to master)) require a slightly different set of parameters:
**UUID** Source Code Management:
Zuul provided key to link builds with Gerrit events Git
**GERRIT_PROJECT** Repositories:
Zuul provided project name Repository URL: <your Gerrit or Zuul repository URL>
**GERRIT_REFNAME** Advanced:
Zuul provided ref name Refspec: ${ZUUL_REF}
**GERRIT_OLDREV** Branches to build:
Zuul provided old reference for ref-updated Branch Specifier: ${ZUUL_COMMIT}
**GERRIT_NEWREV** Advanced:
Zuul provided new reference for ref-updated Clean after checkout: True
That should be sufficient for a job that only builds a single project.
If you have multiple interrelated projects (i.e., they share a Zuul
Change Queue) that are built together, you may be able to configure
the Git plugin to prepare them, or you may chose to use a shell script
instead. The OpenStack project uses the following script to prepare
the workspace for its integration testing:
https://github.com/openstack-ci/devstack-gate/blob/master/devstack-vm-gate-wrap.sh

View File

@ -128,11 +128,15 @@ def job_has_changes(*args):
path = os.path.join(GIT_ROOT, project) path = os.path.join(GIT_ROOT, project)
repo = git.Repo(path) repo = git.Repo(path)
ref = job.parameters['ZUUL_REF'] ref = job.parameters['ZUUL_REF']
sha = job.parameters['ZUUL_COMMIT']
repo_messages = [c.message.strip() for c in repo.iter_commits(ref)] repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
repo_shas = [c.hexsha for c in repo.iter_commits(ref)]
commit_messages = ['%s-1' % commit.subject for commit in commits] commit_messages = ['%s-1' % commit.subject for commit in commits]
for msg in commit_messages: for msg in commit_messages:
if msg not in repo_messages: if msg not in repo_messages:
return False return False
if repo_shas[0] != sha:
return False
return True return True

View File

@ -219,21 +219,23 @@ class Jenkins(object):
dependent_changes = dependent_changes[:] dependent_changes = dependent_changes[:]
dependent_changes.reverse() dependent_changes.reverse()
uuid = str(uuid4().hex) uuid = str(uuid4().hex)
params = dict(UUID=uuid, params = dict(UUID=uuid, # deprecated
GERRIT_PROJECT=change.project.name, ZUUL_UUID=uuid,
GERRIT_PROJECT=change.project.name, # deprecated
ZUUL_PROJECT=change.project.name) ZUUL_PROJECT=change.project.name)
params['ZUUL_PIPELINE'] = pipeline.name params['ZUUL_PIPELINE'] = pipeline.name
if hasattr(change, 'refspec'): if hasattr(change, 'refspec'):
changes_str = '^'.join( changes_str = '^'.join(
['%s:%s:%s' % (c.project.name, c.branch, c.refspec) ['%s:%s:%s' % (c.project.name, c.branch, c.refspec)
for c in dependent_changes + [change]]) for c in dependent_changes + [change]])
params['GERRIT_BRANCH'] = change.branch params['GERRIT_BRANCH'] = change.branch # deprecated
params['ZUUL_BRANCH'] = change.branch params['ZUUL_BRANCH'] = change.branch
params['GERRIT_CHANGES'] = changes_str params['GERRIT_CHANGES'] = changes_str # deprecated
params['ZUUL_CHANGES'] = changes_str params['ZUUL_CHANGES'] = changes_str
params['ZUUL_REF'] = ('refs/zuul/%s/%s' % params['ZUUL_REF'] = ('refs/zuul/%s/%s' %
(change.branch, (change.branch,
change.current_build_set.ref)) change.current_build_set.ref))
params['ZUUL_COMMIT'] = change.current_build_set.commit
zuul_changes = ' '.join(['%s,%s' % (c.number, c.patchset) zuul_changes = ' '.join(['%s,%s' % (c.number, c.patchset)
for c in dependent_changes + [change]]) for c in dependent_changes + [change]])
@ -241,14 +243,41 @@ class Jenkins(object):
params['ZUUL_CHANGE'] = str(change.number) params['ZUUL_CHANGE'] = str(change.number)
params['ZUUL_PATCHSET'] = str(change.patchset) params['ZUUL_PATCHSET'] = str(change.patchset)
if hasattr(change, 'ref'): if hasattr(change, 'ref'):
params['GERRIT_REFNAME'] = change.ref params['GERRIT_REFNAME'] = change.ref # deprecated
params['ZUUL_REFNAME'] = change.ref params['ZUUL_REFNAME'] = change.ref
params['GERRIT_OLDREV'] = change.oldrev params['GERRIT_OLDREV'] = change.oldrev # deprecated
params['ZUUL_OLDREV'] = change.oldrev params['ZUUL_OLDREV'] = change.oldrev
params['GERRIT_NEWREV'] = change.newrev params['GERRIT_NEWREV'] = change.newrev # deprecated
params['ZUUL_NEWREV'] = change.newrev params['ZUUL_NEWREV'] = change.newrev
params['ZUUL_SHORT_OLDREV'] = change.oldrev[:7]
params['ZUUL_SHORT_NEWREV'] = change.newrev[:7] params['ZUUL_SHORT_NEWREV'] = change.newrev[:7]
params['ZUUL_REF'] = change.ref
params['ZUUL_COMMIT'] = change.newrev
# This is what we should be heading toward for parameters:
# required:
# ZUUL_UUID
# ZUUL_REF (/refs/zuul/..., /refs/tags/foo, master)
# ZUUL_COMMIT
# optional:
# ZUUL_PROJECT
# ZUUL_PIPELINE
# optional (changes only):
# ZUUL_BRANCH
# ZUUL_CHANGE
# ZUUL_CHANGE_IDS
# ZUUL_PATCHSET
# optional (ref updated only):
# ZUUL_OLDREV
# ZUUL_NEWREV
# ZUUL_SHORT_NEWREV
# ZUUL_SHORT_OLDREV
if callable(job.parameter_function): if callable(job.parameter_function):
job.parameter_function(change, params) job.parameter_function(change, params)
self.log.debug("Custom parameter function used for job %s, " self.log.debug("Custom parameter function used for job %s, "

View File

@ -81,6 +81,7 @@ class Repo(object):
def setZuulRef(self, ref, commit): def setZuulRef(self, ref, commit):
self.repo.refs[ref].commit = commit self.repo.refs[ref].commit = commit
return self.repo.refs[ref].commit
def push(self, local, remote): def push(self, local, remote):
self.log.debug("Pushing %s:%s to %s " % (local, remote, self.log.debug("Pushing %s:%s to %s " % (local, remote,
@ -112,6 +113,7 @@ class Merger(object):
def mergeChanges(self, changes, target_ref=None, mode=None): def mergeChanges(self, changes, target_ref=None, mode=None):
projects = {} projects = {}
commit = None
# Reset all repos involved in the change set # Reset all repos involved in the change set
for change in changes: for change in changes:
branches = projects.get(change.project, []) branches = projects.get(change.project, [])
@ -148,7 +150,11 @@ class Merger(object):
repo.merge(change.refspec) repo.merge(change.refspec)
elif mode == model.CHERRY_PICK: elif mode == model.CHERRY_PICK:
repo.cherryPick(change.refspec) repo.cherryPick(change.refspec)
repo.setZuulRef(change.branch + '/' + target_ref, 'HEAD') # Keep track of the last commit, it's the commit that
# will be passed to jenkins because it's the commit
# for the triggering change
commit = repo.setZuulRef(change.branch + '/' + target_ref,
'HEAD').hexsha
except: except:
self.log.info("Unable to merge %s" % change) self.log.info("Unable to merge %s" % change)
return False return False
@ -169,4 +175,4 @@ class Merger(object):
self.log.error("Ref %s did not show up in repo" % ref) self.log.error("Ref %s did not show up in repo" % ref)
return False return False
return True return commit

View File

@ -402,6 +402,7 @@ class BuildSet(object):
self.next_build_set = None self.next_build_set = None
self.previous_build_set = None self.previous_build_set = None
self.ref = None self.ref = None
self.commit = None
self.unable_to_merge = False self.unable_to_merge = False
def setConfiguration(self): def setConfiguration(self):
@ -416,9 +417,6 @@ class BuildSet(object):
if not self.ref: if not self.ref:
self.ref = 'Z' + uuid4().hex self.ref = 'Z' + uuid4().hex
def getRef(self):
return self.ref
def addBuild(self, build): def addBuild(self, build):
self.builds[build.job.name] = build self.builds[build.job.name] = build
build.build_set = self build.build_set = self

View File

@ -531,18 +531,18 @@ class BasePipelineManager(object):
def _launchJobs(self, change, jobs): def _launchJobs(self, change, jobs):
self.log.debug("Launching jobs for change %s" % change) self.log.debug("Launching jobs for change %s" % change)
ref = change.current_build_set.getRef() ref = change.current_build_set.ref
if hasattr(change, 'refspec') and not ref: if hasattr(change, 'refspec') and not ref:
change.current_build_set.setConfiguration() change.current_build_set.setConfiguration()
ref = change.current_build_set.getRef() ref = change.current_build_set.ref
mode = model.MERGE_IF_NECESSARY mode = model.MERGE_IF_NECESSARY
merged = self.sched.merger.mergeChanges([change], ref, mode=mode) commit = self.sched.merger.mergeChanges([change], ref, mode=mode)
if not merged: if not commit:
self.log.info("Unable to merge change %s" % change) self.log.info("Unable to merge change %s" % change)
self.pipeline.setUnableToMerge(change) self.pipeline.setUnableToMerge(change)
self.possiblyReportChange(change) self.possiblyReportChange(change)
return return
change.current_build_set.commit = commit
for job in self.pipeline.findJobsToRun(change): for job in self.pipeline.findJobsToRun(change):
self.log.debug("Found job %s for change %s" % (job, change)) self.log.debug("Found job %s for change %s" % (job, change))
try: try:
@ -956,19 +956,20 @@ class DependentPipelineManager(BasePipelineManager):
def _launchJobs(self, change, jobs): def _launchJobs(self, change, jobs):
self.log.debug("Launching jobs for change %s" % change) self.log.debug("Launching jobs for change %s" % change)
ref = change.current_build_set.getRef() ref = change.current_build_set.ref
if hasattr(change, 'refspec') and not ref: if hasattr(change, 'refspec') and not ref:
change.current_build_set.setConfiguration() change.current_build_set.setConfiguration()
ref = change.current_build_set.getRef() ref = change.current_build_set.ref
dependent_changes = self._getDependentChanges(change) dependent_changes = self._getDependentChanges(change)
dependent_changes.reverse() dependent_changes.reverse()
all_changes = dependent_changes + [change] all_changes = dependent_changes + [change]
merged = self.sched.merger.mergeChanges(all_changes, ref) commit = self.sched.merger.mergeChanges(all_changes, ref)
if not merged: if not commit:
self.log.info("Unable to merge changes %s" % all_changes) self.log.info("Unable to merge changes %s" % all_changes)
self.pipeline.setUnableToMerge(change) self.pipeline.setUnableToMerge(change)
self.possiblyReportChange(change) self.possiblyReportChange(change)
return return
change.current_build_set.commit = commit
#TODO: remove this line after GERRIT_CHANGES is gone #TODO: remove this line after GERRIT_CHANGES is gone
dependent_changes = self._getDependentChanges(change) dependent_changes = self._getDependentChanges(change)
for job in jobs: for job in jobs: