Jenkins Job builder 2.0

This uses a python script with modules for parts of the XML.  The parameters for the projects are provided using YAML scripts.

It also includes a Jenkins API module to directly inject jobs into Jenkins without requiring a restart/reload as well as a memory of which jobs have been pushed to Jenkins.

It is currently configured to replace the original Jenkins Jobs in StackForge.

What it won't yet do:
1. Delete jobs (although it isn't far off being able to)
2. check-* jobs (need to modify the trigger_gerrit module to support that)

Documentation to follow

Fixes bug #995599

Change-Id: I2a67ee2d9e8f43cbced56425ef7f80dc6a30a814
This commit is contained in:
Andrew Hutchings 2012-05-09 14:28:53 +01:00
commit 1216b8ec00
20 changed files with 1338 additions and 0 deletions

173
jenkins_jobs.py Normal file
View File

@ -0,0 +1,173 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Manage jobs in Jenkins server
import os
import argparse
import hashlib
import yaml
import sys
import xml.etree.ElementTree as XML
import pycurl
import jenkins_talker
import ConfigParser
from xml.dom.ext import PrettyPrint
from StringIO import StringIO
from xml.dom.ext.reader import Sax2
parser = argparse.ArgumentParser()
subparser = parser.add_subparsers(help='update or delete job', dest='command')
parser_update = subparser.add_parser('update')
parser_update.add_argument('file', help='YAML file for update', type=file)
parser_delete = subparser.add_parser('delete')
parser_delete.add_argument('name', help='name of job')
parser.add_argument('--conf', dest='conf', help='Configuration file')
options = parser.parse_args()
if options.conf:
conf = options.conf
else:
conf = 'jenkins_jobs.ini'
conffp = open(conf, 'r')
config = ConfigParser.ConfigParser()
config.readfp(conffp)
class YamlParser(object):
def __init__(self, yfile):
self.data = yaml.load_all(yfile)
self.it = self.data.__iter__()
self.current = ''
def get_next_xml(self):
self.current = self.it.next()
return XmlParser(self.current)
def get_name(self):
return self.current['main']['name']
class XmlParser(object):
def __init__(self, data):
self.data = data
self.xml = XML.Element('project')
self.modules = []
self._load_modules()
self._build()
def _load_modules(self):
for modulename in self.data['modules']:
modulename = 'modules.{name}'.format(name=modulename)
self._register_module(modulename)
def _register_module(self, modulename):
classname = modulename.rsplit('.', 1)[1]
module = __import__(modulename, fromlist=[classname])
cla = getattr(module, classname)
self.modules.append(cla(self.data))
def _build(self):
XML.SubElement(self.xml, 'actions')
description = XML.SubElement(self.xml, 'description')
description.text = "THIS JOB IS MANAGED BY PUPPET AND WILL BE OVERWRITTEN.\n\n\
DON'T EDIT THIS JOB THROUGH THE WEB\n\n\
If you would like to make changes to this job, please see:\n\n\
https://github.com/openstack/openstack-ci-puppet\n\n\
In modules/jenkins_jobs"
XML.SubElement(self.xml, 'keepDependencies').text = 'false'
XML.SubElement(self.xml, 'disabled').text = self.data['main']['disabled']
XML.SubElement(self.xml, 'blockBuildWhenDownstreamBuilding').text = 'false'
XML.SubElement(self.xml, 'blockBuildWhenUpstreamBuilding').text = 'false'
XML.SubElement(self.xml, 'concurrentBuild').text = 'false'
XML.SubElement(self.xml, 'buildWrappers')
self._insert_modules()
def _insert_modules(self):
for module in self.modules:
module.gen_xml(self.xml)
def md5(self):
return hashlib.md5(self.output()).hexdigest()
def output(self):
reader = Sax2.Reader()
docNode = reader.fromString(XML.tostring(self.xml))
tmpStream = StringIO()
PrettyPrint(docNode, stream=tmpStream)
return tmpStream.getvalue()
class CacheStorage(object):
def __init__(self):
self.cachefilename = os.path.expanduser('~/.jenkins_jobs_cache.yml')
try:
yfile = file(self.cachefilename, 'r')
except IOError:
self.data = {}
return
self.data = yaml.load(yfile)
yfile.close()
def set(self, job, md5):
self.data[job] = md5
yfile = file(self.cachefilename, 'w')
yaml.dump(self.data, yfile)
yfile.close()
def is_cached(self, job):
if self.data.has_key(job):
return True
return False
def has_changed(self, job, md5):
if self.data.has_key(job) and self.data[job] == md5:
return False
return True
class Jenkins(object):
def __init__(self, url, user, password):
self.jenkins = jenkins_talker.JenkinsTalker(url, user, password)
def update_job(self, job_name, xml):
if self.jenkins.is_job(job_name):
self.jenkins.update_job(job_name, xml)
else:
self.jenkins.create_job(job_name, xml)
def is_job(self, job_name):
return self.jenkins.is_job(job_name)
def get_job_md5(self, job_name):
xml = self.jenkins.get_job_config(job_name)
return hashlib.md5(xml).hexdigest()
yparse = YamlParser(options.file)
cache = CacheStorage()
remote_jenkins = Jenkins(config.get('jenkins','url'), config.get('jenkins','user'), config.get('jenkins','password'))
while True:
try:
xml = yparse.get_next_xml()
job = yparse.get_name()
md5 = xml.md5()
if remote_jenkins.is_job(job) and not cache.is_cached(job):
old_md5 = remote_jenkins.get_job_md5(job)
cache.set(job, old_md5)
if cache.has_changed(job, md5):
remote_jenkins.update_job(job, xml.output())
cache.set(job, md5)
except StopIteration:
break

83
jenkins_talker.py Normal file
View File

@ -0,0 +1,83 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# A basic API class to talk to a Jenkins Server
import pycurl
from StringIO import StringIO
class JenkinsTalkerException(Exception): pass
class JenkinsTalker(object):
def __init__(self, url, user, password):
self.url = url
self.user = user
self.password = password
def _post_xml(self, path, xml, pass_codes):
curl = pycurl.Curl()
response = StringIO()
curl.setopt(pycurl.URL, self.url + path)
curl.setopt(pycurl.USERPWD, self.user + ":" + self.password)
curl.setopt(pycurl.POST, 1)
curl.setopt(pycurl.POSTFIELDS, xml)
curl.setopt(pycurl.HTTPHEADER, [ "Content-Type: text/xml" ])
curl.setopt(pycurl.POSTFIELDSIZE, len(xml))
# should probably shove this response into a debug output somewhere
curl.setopt(pycurl.WRITEFUNCTION, response.write)
curl.perform()
if curl.getinfo(pycurl.RESPONSE_CODE) not in pass_codes:
raise JenkinsTalkerException('error posting XML')
curl.close()
def _get_request(self, path, pass_codes):
curl = pycurl.Curl()
response = StringIO()
curl.setopt(pycurl.URL, self.url + path)
curl.setopt(pycurl.USERPWD, self.user + ":" + self.password)
curl.setopt(pycurl.WRITEFUNCTION, response.write)
curl.perform()
if curl.getinfo(pycurl.RESPONSE_CODE) not in pass_codes:
raise JenkinsTalkerException('error getting response')
curl.close()
return response.getvalue()
def create_job(self, job_name, xml):
path = 'createItem?name=' + job_name
pass_codes = [ 200 ]
self._post_xml(path, xml, pass_codes)
def update_job(self, job_name, xml):
path = 'job/' + job_name + '/config.xml'
pass_codes = [ 200 ]
self._post_xml(path, xml, pass_codes)
def delete_job(self, job_name):
path = 'job/' + job_name + '/doDelete'
pass_codes = [ 302 ]
self._get_request(path, pass_codes)
def get_job_config(self, job_name):
path = 'job/' + job_name + '/config.xml'
pass_codes = [ 200 ]
return self._get_request(path, pass_codes)
def is_job(self, job_name):
try:
self.get_job_config(job_name)
except JenkinsTalkerException:
return False
return True

0
modules/__init__.py Normal file
View File

31
modules/assignednode.py Normal file
View File

@ -0,0 +1,31 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for assigned nodes
# To use add the folowing into your YAML:
# assignednode:
# - node: 'oneiric'
import xml.etree.ElementTree as XML
class assignednode(object):
def __init__(self, data):
self.data = data
def gen_xml(self, xml_parent):
node = self.data['assignednode']['node']
XML.SubElement(xml_parent, 'assignedNode').text = node
XML.SubElement(xml_parent, 'canRoam').text = 'false'

83
modules/builders.py Normal file
View File

@ -0,0 +1,83 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for builders
# To use add the folowing into your YAML:
# builders:
# - 'gerrit_git_prep'
# - 'python26'
import xml.etree.ElementTree as XML
class builders(object):
def __init__(self, data):
self.data = data
def gen_xml(self, xml_parent):
builders = XML.SubElement(xml_parent, 'builders')
for builder in self.data['builders']:
getattr(self, '_' + builder)(builders)
def _add_script(self, xml_parent, script):
shell = XML.SubElement(xml_parent, 'hudson.tasks.Shell')
XML.SubElement(shell, 'command').text = script
def _copy_bundle(self, xml_parent):
copy = XML.SubElement(xml_parent, 'hudson.plugins.copyartifact.CopyArtifact')
XML.SubElement(copy, 'projectName').text = '$PROJECT-venv'
XML.SubElement(copy, 'filter')
XML.SubElement(copy, 'target')
XML.SubElement(copy, 'selector', {'class':'hudson.plugins.copyartifact.StatusBuildSelector'})
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/copy-bundle.sh')
def _coverage(self, xml_parent):
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/run-cover.sh')
def _docs(self, xml_parent):
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/run-docs.sh')
def _gerrit_git_prep(self, xml_parent):
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/gerrit-git-prep.sh {site}'.format(site=self.data['main']['site']))
def _pep8(self, xml_parent):
self._add_script(xml_parent, 'tox -v -epep8 | tee pep8.txt')
def _python26(self, xml_parent):
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/run-tox.sh 26')
def _python27(self, xml_parent):
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/run-tox.sh 27')
def _tarball(self, xml_parent):
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/create-tarball.sh')
def _venv(self, xml_parent):
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/build-bundle.sh')
def _ppa(self, xml_parent):
self._add_script(xml_parent, 'rm -rf build dist.zip\n\
mkdir build')
copy = XML.SubElement(xml_parent, 'hudson.plugins.copyartifact.CopyArtifact')
XML.SubElement(copy, 'projectName').text = '$PROJECT-tarball'
XML.SubElement(copy, 'filter').text = 'dist/*.tar.gz'
XML.SubElement(copy, 'target').text = 'build'
selector = XML.SubElement(copy, 'selector', {'class':'hudson.plugins.copyartifact.StatusBuildSelector'})
XML.SubElement(selector, 'parameterName').text = 'BUILD_SELECTOR'
self._add_script(xml_parent, '#!/bin/bash\n\
\n\
#export DO_UPLOAD="no"\n\
export PROJECT=&quot;<%= project %>&quot;\n\
export GERRIT_REFNAME=$BRANCH\n\
/usr/local/jenkins/slave_scripts/create-ppa-package.sh')

41
modules/logrotate.py Normal file
View File

@ -0,0 +1,41 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for logrotate
# To use add the folowing into your YAML:
# logrotate:
# daysToKeep: 3
# numToKeep: 20
# artifactDaysToKeep: -1
# artifactNumToKeep: -1
import xml.etree.ElementTree as XML
class logrotate(object):
def __init__(self, data):
self.data = data
def gen_xml(self, xml_parent):
if self.data.has_key('logrotate'):
lr_xml = XML.SubElement(xml_parent, 'logRotator')
logrotate = self.data['logrotate']
lr_days = XML.SubElement(lr_xml, 'daysToKeep')
lr_days.text = str(logrotate['daysToKeep'])
lr_num = XML.SubElement(lr_xml, 'numToKeep')
lr_num.text = str(logrotate['numToKeep'])
lr_adays = XML.SubElement(lr_xml, 'artifactDaysToKeep')
lr_adays.text = str(logrotate['artifactDaysToKeep'])
lr_anum = XML.SubElement(lr_xml, 'artifactNumToKeep')
lr_anum.text = str(logrotate['artifactNumToKeep'])

49
modules/properties.py Normal file
View File

@ -0,0 +1,49 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for job properties
# No additional YAML needed
import xml.etree.ElementTree as XML
class properties(object):
def __init__(self, data):
self.data = data
def gen_xml(self, xml_parent):
main = self.data['main']
properties = XML.SubElement(xml_parent, 'properties')
github = XML.SubElement(properties, 'com.coravy.hudson.plugins.github.GithubProjectProperty')
github_url = XML.SubElement(github, 'projectUrl')
github_url.text = "https://github.com/{site}/{project}".format(site=main['site'], project=main['project'])
throttle = XML.SubElement(properties, 'hudson.plugins.throttleconcurrents.ThrottleJobProperty')
XML.SubElement(throttle, 'maxConcurrentPerNode').text = '0'
XML.SubElement(throttle, 'maxConcurrentTotal').text = '0'
#XML.SubElement(throttle, 'categories')
XML.SubElement(throttle, 'throttleEnabled').text = 'false'
XML.SubElement(throttle, 'throttleOption').text = 'project'
XML.SubElement(throttle, 'configVersion').text = '1'
env = XML.SubElement(properties, 'EnvInjectJobProperty')
einfo = XML.SubElement(env, 'info')
eiproperties = XML.SubElement(einfo, 'propertiesContent')
eiproperties.text = 'PROJECT={project}'.format(project=main['project'])
XML.SubElement(einfo, 'loadFilesFromMaster').text = 'false'
XML.SubElement(env, 'on').text = 'true'
XML.SubElement(env, 'keepJenkinsSystemVariables').text = 'true'
XML.SubElement(env, 'keepBuildVariables').text = 'true'
XML.SubElement(env, 'contributors')
if main.has_key('authenticatedBuild') and main['authenticatedBuild'] == 'true':
security = XML.SubElement(properties, 'hudson.security.AuthorizationMatrixProperty')
XML.SubElement(security, 'permission').text = 'hudson.model.Item.Build:authenticated'

View File

@ -0,0 +1,63 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for coverage publishers
# No additional YAML needed
import xml.etree.ElementTree as XML
class publisher_coverage(object):
def __init__(self, data):
self.data = data
def gen_xml(self, xml_parent):
publishers = XML.SubElement(xml_parent, 'publishers')
cobertura = XML.SubElement(publishers, 'hudson.plugins.cobertura.CoberturaPublisher')
XML.SubElement(cobertura, 'coberturaReportFile').text = '**/coverage.xml'
XML.SubElement(cobertura, 'onlyStable').text = 'false'
healthy = XML.SubElement(cobertura, 'healthyTarget')
targets = XML.SubElement(healthy, 'targets', {'class':'enum-map','enum-type':'hudson.plugins.cobertura.targets.CoverageMetric'})
entry = XML.SubElement(targets, 'entry')
XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric').text = 'CONDITIONAL'
XML.SubElement(entry, 'int').text = '70'
entry = XML.SubElement(targets, 'entry')
XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric').text = 'LINE'
XML.SubElement(entry, 'int').text = '80'
entry = XML.SubElement(targets, 'entry')
XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric').text = 'METHOD'
XML.SubElement(entry, 'int').text = '80'
unhealthy = XML.SubElement(cobertura, 'unhealthyTarget')
targets = XML.SubElement(unhealthy, 'targets', {'class':'enum-map','enum-type':'hudson.plugins.cobertura.targets.CoverageMetric'})
entry = XML.SubElement(targets, 'entry')
XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric').text = 'CONDITIONAL'
XML.SubElement(entry, 'int').text = '0'
entry = XML.SubElement(targets, 'entry')
XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric').text = 'LINE'
XML.SubElement(entry, 'int').text = '0'
entry = XML.SubElement(targets, 'entry')
XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric').text = 'METHOD'
XML.SubElement(entry, 'int').text = '0'
failing = XML.SubElement(cobertura, 'failingTarget')
targets = XML.SubElement(failing, 'targets', {'class':'enum-map','enum-type':'hudson.plugins.cobertura.targets.CoverageMetric'})
entry = XML.SubElement(targets, 'entry')
XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric').text = 'CONDITIONAL'
XML.SubElement(entry, 'int').text = '0'
entry = XML.SubElement(targets, 'entry')
XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric').text = 'LINE'
XML.SubElement(entry, 'int').text = '0'
entry = XML.SubElement(targets, 'entry')
XML.SubElement(entry, 'hudson.plugins.cobertura.targets.CoverageMetric').text = 'METHOD'
XML.SubElement(entry, 'int').text = '0'
XML.SubElement(cobertura, 'sourceEncoding').text = 'ASCII'

43
modules/publisher_docs.py Normal file
View File

@ -0,0 +1,43 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for docs publishers
# No additional YAML needed
import xml.etree.ElementTree as XML
class publisher_docs(object):
def __init__(self, data):
self.data = data
def gen_xml(self, xml_parent):
main = self.data['main']
publishers = XML.SubElement(xml_parent, 'publishers')
scp = XML.SubElement(publishers, 'be.certipost.hudson.plugin.SCPRepositoryPublisher')
XML.SubElement(scp, 'siteName').text = '{proj}.{site}.org'.format(proj=main['project'], site=main['site'])
entries = XML.SubElement(scp, 'entries')
entry = XML.SubElement(entries, 'be.certipost.hudson.plugin.Entry')
XML.SubElement(entry, 'filePath').text = 'docs/{proj}'.format(proj=main['project'])
XML.SubElement(entry, 'sourceFile').text = 'doc/build/html/**'
XML.SubElement(entry, 'keepHierarchy').text = 'false'
entry = XML.SubElement(entries, 'be.certipost.hudson.plugin.Entry')
XML.SubElement(entry, 'filePath').text = 'docs/{proj}/_static'.format(proj=main['project'])
XML.SubElement(entry, 'sourceFile').text = 'doc/build/html/_static/**'
XML.SubElement(entry, 'keepHierarchy').text = 'false'
entry = XML.SubElement(entries, 'be.certipost.hudson.plugin.Entry')
XML.SubElement(entry, 'filePath').text = 'docs/{proj}/_sources'.format(proj=main['project'])
XML.SubElement(entry, 'sourceFile').text = 'doc/build/html/_sources/**'
XML.SubElement(entry, 'keepHierarchy').text = 'false'

26
modules/publisher_none.py Normal file
View File

@ -0,0 +1,26 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for no publishers
# No additional YAML needed
import xml.etree.ElementTree as XML
class publisher_none(object):
def __init__(self, data):
self.data = data
def gen_xml(self, xml_parent):
XML.SubElement(xml_parent, 'publishers')

74
modules/publisher_pep8.py Normal file
View File

@ -0,0 +1,74 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for pep8 publishers
# No additional YAML needed
import xml.etree.ElementTree as XML
class publisher_pep8(object):
def __init__(self, data):
self.data = data
def _add_entry(self, xml_parent, name):
entry = XML.SubElement(xml_parent, 'entry')
XML.SubElement(entry, 'string').text = name
tconfig = XML.SubElement(entry, 'hudson.plugins.violations.TypeConfig')
XML.SubElement(tconfig, 'type').text = name
XML.SubElement(tconfig, 'min').text = '10'
XML.SubElement(tconfig, 'max').text = '999'
XML.SubElement(tconfig, 'unstable').text = '999'
XML.SubElement(tconfig, 'usePattern').text = 'false'
XML.SubElement(tconfig, 'pattern')
def gen_xml(self, xml_parent):
publishers = XML.SubElement(xml_parent, 'publishers')
violations = XML.SubElement(publishers, 'hudson.plugins.violations.ViolationsPublisher')
config = XML.SubElement(violations, 'config')
suppressions = XML.SubElement(config, 'suppressions', {'class':'tree-set'})
XML.SubElement(suppressions, 'no-comparator')
configs = XML.SubElement(config, 'typeConfigs')
XML.SubElement(configs, 'no-comparator')
self._add_entry(configs, 'checkstyle')
self._add_entry(configs, 'codenarc')
self._add_entry(configs, 'cpd')
self._add_entry(configs, 'cpplint')
self._add_entry(configs, 'csslint')
self._add_entry(configs, 'findbugs')
self._add_entry(configs, 'fxcop')
self._add_entry(configs, 'gendarme')
self._add_entry(configs, 'jcreport')
self._add_entry(configs, 'jslint')
entry = XML.SubElement(configs, 'entry')
XML.SubElement(entry, 'string').text = 'pep8'
tconfig = XML.SubElement(entry, 'hudson.plugins.violations.TypeConfig')
XML.SubElement(tconfig, 'type').text = 'pep8'
XML.SubElement(tconfig, 'min').text = '0'
XML.SubElement(tconfig, 'max').text = '1'
XML.SubElement(tconfig, 'unstable').text = '1'
XML.SubElement(tconfig, 'usePattern').text = 'false'
XML.SubElement(tconfig, 'pattern').text = '**/pep8.txt'
self._add_entry(configs, 'pmd')
self._add_entry(configs, 'pylint')
self._add_entry(configs, 'simian')
self._add_entry(configs, 'stylecop')
XML.SubElement(config, 'limit').text = '100'
XML.SubElement(config, 'sourcePathPattern')
XML.SubElement(config, 'fauxProjectPath')
XML.SubElement(config, 'encoding').text = 'default'

29
modules/publisher_ppa.py Normal file
View File

@ -0,0 +1,29 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for PPA publishers
# No additional YAML needed
import xml.etree.ElementTree as XML
class publisher_ppa(object):
def __init__(self, data):
self.data = data
def gen_xml(self, xml_parent):
publishers = XML.SubElement(xml_parent, 'publishers')
archiver = XML.SubElement(publishers, 'hudson.tasks.ArtifactArchiver')
XML.SubElement(archiver, 'artifacts').text = 'build/*.dsc,build/*.tar.gz,build/*.changes'
XML.SubElement(archiver, 'latestOnly').text = 'false'

View File

@ -0,0 +1,54 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for tarball publishers
# To use you can optionally add the following into your YAML:
# publisher:
# uploadProject: 'glance'
#
# If you do not add this it will default to the project specified in the YAML
import xml.etree.ElementTree as XML
class publisher_tarball(object):
def __init__(self, data):
self.data = data
def gen_xml(self, xml_parent):
if self.data.has_key('publisher') and self.data['publisher'].has_key('upload_project'):
project = self.data['publisher']['upload_project']
else:
project = self.data['main']['project']
publishers = XML.SubElement(xml_parent, 'publishers')
archiver = XML.SubElement(publishers, 'hudson.tasks.ArtifactArchiver')
XML.SubElement(archiver, 'artifacts').text = 'dist/*.tar.gz'
XML.SubElement(archiver, 'latestOnly').text = 'false'
scp = XML.SubElement(publishers, 'be.certipost.hudson.plugin.SCPRepositoryPublisher')
XML.SubElement(scp, 'siteName').text = '{proj}.{site}.org'.format(proj=project, site=self.data['main']['site'])
entries = XML.SubElement(scp, 'entries')
entry = XML.SubElement(entries, 'be.certipost.hudson.plugin.Entry')
XML.SubElement(entry, 'filePath').text = 'tarballs/{proj}/'.format(proj=project)
XML.SubElement(entry, 'sourceFile').text = 'dist/*.tar.gz'
XML.SubElement(entry, 'keepHierarchy').text = 'false'
btrigger = XML.SubElement(publishers, 'hudson.plugins.parameterizedtrigger.BuildTrigger')
btrigger_configs = XML.SubElement(btrigger, 'configs')
btrigger_config = XML.SubElement(btrigger_configs, 'hudson.plugins.parameterizedtrigger.BuildTriggerConfig')
configs = XML.SubElement(btrigger_config, 'configs')
parameters = XML.SubElement(configs, 'hudson.plugins.parameterizedtrigger.PredefinedBuildParameters')
properties = XML.SubElement(parameters, 'properties').text = 'BUILD_SELECTOR=&lt;SpecificBuildSelector&gt;&lt;buildNumber&gt;$BUILD_NUMBER&lt;/buildNumber&gt;&lt;/SpecificBuildSelector&gt;\n\
BRANCH=$GERRIT_REFNAME'
XML.SubElement(btrigger_config, 'projects').text = '{proj}-ppa'.format(proj=self.data['main']['project'])
XML.SubElement(btrigger_config, 'condition').text = 'SUCCESS'
XML.SubElement(btrigger_config, 'triggerWithNoParameters').text = 'false'

29
modules/publisher_venv.py Normal file
View File

@ -0,0 +1,29 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for venv publishers
# No additional YAML needed
import xml.etree.ElementTree as XML
class publisher_venv(object):
def __init__(self, data):
self.data = data
def gen_xml(self, xml_parent):
publishers = XML.SubElement(xml_parent, 'publishers')
archiver = XML.SubElement(publishers, 'hudson.tasks.ArtifactArchiver')
XML.SubElement(archiver, 'artifacts').text = 'jenkins_venvs/**/.cache.bundle'
XML.SubElement(archiver, 'latestOnly').text = 'false'

64
modules/scm.py Normal file
View File

@ -0,0 +1,64 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for scm
# To use add the folowing into your YAML:
# scm:
# scm: 'true'
# or
# scm: 'false'
import xml.etree.ElementTree as XML
class scm(object):
def __init__(self, data):
self.data = data
def gen_xml(self, xml_parent):
main = self.data['main']
scm_enabled = self.data['scm']['scm']
if scm_enabled == 'true':
scm = XML.SubElement(xml_parent, 'scm', {'class':'hudson.plugins.git.GitSCM'})
XML.SubElement(scm, 'configVersion').text = '2'
user = XML.SubElement(scm, 'userRemoteConfigs')
huser = XML.SubElement(user, 'hudson.plugins.git.UserRemoteConfig')
XML.SubElement(huser, 'name').text = 'origin'
XML.SubElement(huser, 'refspec').text = '+refs/heads/*:refs/remotes/origin/*'
XML.SubElement(huser, 'url').text = 'git://github.com/{site}/{project}.git'.format(site=main['site'], project=main['project'])
branches = XML.SubElement(scm, 'branches')
bspec = XML.SubElement(branches, 'hudson.plugins.git.BranchSpec')
XML.SubElement(bspec, 'name').text = '**'
XML.SubElement(scm, 'disableSubmodules').text = 'false'
XML.SubElement(scm, 'recursiveSubmodules').text = 'false'
XML.SubElement(scm, 'doGenerateSubmoduleConfigurations').text = 'false'
XML.SubElement(scm, 'authorOrCommitter').text = 'false'
XML.SubElement(scm, 'clean').text = 'false'
XML.SubElement(scm, 'wipeOutWorkspace').text = 'true'
XML.SubElement(scm, 'pruneBranches').text = 'false'
XML.SubElement(scm, 'remotePoll').text = 'false'
XML.SubElement(scm, 'buildChooser', {'class':'hudson.plugins.git.util.DefaultBuildChooser'})
XML.SubElement(scm, 'gitTool').text = 'Default'
XML.SubElement(scm, 'submoduleCfg', {'class':'list'})
XML.SubElement(scm, 'relativeTargetDir')
XML.SubElement(scm, 'reference')
XML.SubElement(scm, 'excludedRegions')
XML.SubElement(scm, 'excludedUsers')
XML.SubElement(scm, 'gitConfigName')
XML.SubElement(scm, 'gitConfigEmail')
XML.SubElement(scm, 'skipTag').text = 'false'
XML.SubElement(scm, 'scmName')
else:
XML.SubElement(xml_parent, 'scm', {'class':'hudson.scm.NullSCM'})

70
modules/trigger_gerrit.py Normal file
View File

@ -0,0 +1,70 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for gerrit triggers
# To use add the following into your YAML:
# trigger:
# triggerOnPatchsetUploadedEvent: 'false'
# triggerOnChangeMergedEvent: 'false'
# triggerOnCommentAddedEvent: 'true'
# triggerOnRefUpdatedEvent: 'false'
# triggerApprovalCategory: 'APRV'
# triggerApprovalValue: 1
# failureMessage: 'This change was unable to be automatically merged with the current state of the repository. Please rebase your change and upload a new patchset.'
# projects:
# - projectCompareType: 'PLAIN'
# projectPattern: 'openstack/nova'
# branchCompareType: 'ANT'
# branchPattern: '**'
# - projectCompareType: 'PLAIN'
# projectPattern: 'openstack/glance'
# branchCompareType: 'ANT'
# branchPattern: '**'
# ...
import xml.etree.ElementTree as XML
class trigger_gerrit(object):
def __init__(self, data):
self.data = data
def gen_xml(self, xml_parent):
trigger_data = self.data['trigger']
projects = trigger_data['projects']
trigger = XML.SubElement(xml_parent, 'triggers', {'class':'vector'})
gtrig = XML.SubElement(trigger, 'com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.GerritTrigger')
XML.SubElement(gtrig, 'spec')
gprojects = XML.SubElement(gtrig, 'gerritProjects')
for project in projects:
gproj = XML.SubElement(gprojects, 'com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.data.GerritProject')
XML.SubElement(gproj, 'compareType').text = project['projectCompareType']
XML.SubElement(gproj, 'pattern').text = project['projectPattern']
branches = XML.SubElement(gproj, 'branches')
gbranch = XML.SubElement(branches, 'com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.data.Branch')
XML.SubElement(gbranch, 'compareType').text = project['branchCompareType']
XML.SubElement(gbranch, 'pattern').text = project['branchPattern']
XML.SubElement(gtrig, 'silentMode').text = 'false'
XML.SubElement(gtrig, 'escapeQuotes').text = 'true'
XML.SubElement(gtrig, 'triggerOnPatchsetUploadedEvent').text = trigger_data['triggerOnPatchsetUploadedEvent']
XML.SubElement(gtrig, 'triggerOnChangeMergedEvent').text = trigger_data['triggerOnChangeMergedEvent']
XML.SubElement(gtrig, 'triggerOnCommentAddedEvent').text = trigger_data['triggerOnCommentAddedEvent']
XML.SubElement(gtrig, 'triggerOnRefUpdatedEvent').text = trigger_data['triggerOnRefUpdatedEvent']
XML.SubElement(gtrig, 'commentAddedTriggerApprovalCategory').text = trigger_data['triggerApprovalCategory']
XML.SubElement(gtrig, 'commentAddedTriggerApprovalValue').text = str(trigger_data['triggerApprovalValue'])
XML.SubElement(gtrig, 'buildStartMessage')
XML.SubElement(gtrig, 'buildFailureMessage').text = trigger_data['failureMessage']
XML.SubElement(gtrig, 'buildSuccessfulMessage')
XML.SubElement(gtrig, 'buildUnstableMessage')
XML.SubElement(gtrig, 'customUrl')

26
modules/trigger_none.py Normal file
View File

@ -0,0 +1,26 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for no triggers
# No additional YAML needed
import xml.etree.ElementTree as XML
class trigger_none(object):
def __init__(self, data):
self.data = data
def gen_xml(self, xml_parent):
XML.SubElement(xml_parent, 'triggers', {'class':'vector'})

33
modules/trigger_timed.py Normal file
View File

@ -0,0 +1,33 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Jenkins Job module for timed triggers
# To use add the following into your YAML:
# trigger:
# timed: '@midnight'
# or
# timed: '*/15 * * * *'
import xml.etree.ElementTree as XML
class trigger_timed(object):
def __init__(self, data):
self.data = data
def gen_xml(self, xml_parent):
time = self.data['trigger']['timed']
trigger = XML.SubElement(xml_parent, 'triggers', {'class':'vector'})
scmtrig = XML.SubElement(trigger, 'hudson.triggers.SCMTrigger')
XML.SubElement(scmtrig, 'spec').text = time

View File

@ -0,0 +1,38 @@
---
modules:
- properties
- scm
- assignednode
- trigger_gerrit
- builders
- publisher_none
main:
name: 'gate-ceilometer-merge'
site: 'stackforge'
project: 'ceilometer'
authenticatedBuild: 'true'
disabled: 'false'
trigger:
triggerOnPatchsetUploadedEvent: 'false'
triggerOnChangeMergedEvent: 'false'
triggerOnCommentAddedEvent: 'true'
triggerOnRefUpdatedEvent: 'false'
triggerApprovalCategory: 'APRV'
triggerApprovalValue: 1
failureMessage: 'This change was unable to be automatically merged with the current state of the repository. Please rebase your change and upload a new patchset.'
projects:
- projectCompareType: 'PLAIN'
projectPattern: 'stackforge/ceilometer'
branchCompareType: 'ANT'
branchPattern: '**'
builders:
- gerrit_git_prep
scm:
scm: 'false'
assignednode:
node: 'oneiric'

View File

@ -0,0 +1,329 @@
---
# coverage
modules:
- logrotate
- properties
- scm
- assignednode
- trigger_timed
- builders
- publisher_coverage
main:
name: 'reddwarf-coverage'
site: 'stackforge'
project: 'reddwarf'
authenticatedBuild: 'false'
disabled: 'true'
trigger:
timed: '*/15 * * * *'
builders:
- copy_bundle
- coverage
scm:
scm: 'true'
assignednode:
node: 'oneiric'
logrotate:
daysToKeep: 3
numToKeep: 20
artifactDaysToKeep: -1
artifactNumToKeep: -1
---
# pep8-gate
modules:
- properties
- scm
- assignednode
- trigger_gerrit
- builders
- publisher_pep8
main:
name: 'gate-reddwarf-pep8'
site: 'stackforge'
project: 'reddwarf'
authenticatedBuild: 'true'
disabled: 'true'
trigger:
triggerOnPatchsetUploadedEvent: 'false'
triggerOnChangeMergedEvent: 'false'
triggerOnCommentAddedEvent: 'true'
triggerOnRefUpdatedEvent: 'false'
triggerApprovalCategory: 'APRV'
triggerApprovalValue: 1
failureMessage: ''
projects:
- projectCompareType: 'PLAIN'
projectPattern: 'stackforge/reddwarf'
branchCompareType: 'ANT'
branchPattern: '**'
builders:
- gerrit_git_prep
- copy_bundle
- pep8
scm:
scm: 'false'
assignednode:
node: 'oneiric'
---
# python26-gate
modules:
- properties
- scm
- assignednode
- trigger_gerrit
- builders
- publisher_none
main:
name: 'gate-reddwarf-python26'
site: 'stackforge'
project: 'reddwarf'
authenticatedBuild: 'true'
disabled: 'true'
trigger:
triggerOnPatchsetUploadedEvent: 'false'
triggerOnChangeMergedEvent: 'false'
triggerOnCommentAddedEvent: 'true'
triggerOnRefUpdatedEvent: 'false'
triggerApprovalCategory: 'APRV'
triggerApprovalValue: 1
failureMessage: ''
projects:
- projectCompareType: 'PLAIN'
projectPattern: 'stackforge/reddwarf'
branchCompareType: 'ANT'
branchPattern: '**'
builders:
- gerrit_git_prep
- copy_bundle
- python26
scm:
scm: 'true'
assignednode:
node: 'oneiric'
---
# python27-gate
modules:
- properties
- scm
- assignednode
- trigger_gerrit
- builders
- publisher_none
main:
name: 'gate-reddwarf-python27'
site: 'stackforge'
project: 'reddwarf'
authenticatedBuild: 'true'
disabled: 'true'
trigger:
triggerOnPatchsetUploadedEvent: 'false'
triggerOnChangeMergedEvent: 'false'
triggerOnCommentAddedEvent: 'true'
triggerOnRefUpdatedEvent: 'false'
triggerApprovalCategory: 'APRV'
triggerApprovalValue: 1
failureMessage: ''
projects:
- projectCompareType: 'PLAIN'
projectPattern: 'stackforge/reddwarf'
branchCompareType: 'ANT'
branchPattern: '**'
builders:
- gerrit_git_prep
- copy_bundle
- python27
scm:
scm: 'true'
assignednode:
node: 'oneiric'
---
# venv
modules:
- properties
- scm
- assignednode
- trigger_timed
- builders
- publisher_venv
main:
name: 'reddwarf-venv'
site: 'stackforge'
project: 'reddwarf'
authenticatedBuild: 'false'
disabled: 'true'
trigger:
timed: '@midnight'
builders:
- venv
scm:
scm: 'true'
assignednode:
node: 'oneiric'
---
# docs
modules:
- properties
- scm
- assignednode
- trigger_timed
- builders
- publisher_docs
main:
name: 'reddwarf-docs'
site: 'stackforge'
project: 'reddwarf'
authenticatedBuild: 'false'
disabled: 'true'
trigger:
timed: '*/15 * * * *'
builders:
- copy_bundle
- docs
scm:
scm: 'true'
assignednode:
node: 'oneiric'
---
# merge-gate
modules:
- properties
- scm
- assignednode
- trigger_gerrit
- builders
- publisher_none
main:
name: 'gate-reddwarf-merge'
site: 'stackforge'
project: 'reddwarf'
authenticatedBuild: 'true'
disabled: 'true'
trigger:
triggerOnPatchsetUploadedEvent: 'false'
triggerOnChangeMergedEvent: 'false'
triggerOnCommentAddedEvent: 'true'
triggerOnRefUpdatedEvent: 'false'
triggerApprovalCategory: 'APRV'
triggerApprovalValue: 1
failureMessage: 'This change was unable to be automatically merged with the current state of the repository. Please rebase your change and upload a new patchset.'
projects:
- projectCompareType: 'PLAIN'
projectPattern: 'stackforge/reddwarf'
branchCompareType: 'ANT'
branchPattern: '**'
builders:
- gerrit_git_prep
scm:
scm: 'false'
assignednode:
node: 'oneiric'
---
# ppa
modules:
- properties
- scm
- assignednode
- trigger_none
- builders
- publisher_ppa
main:
name: 'reddwarf-ppa'
site: 'stackforge'
project: 'reddwarf'
authenticatedBuild: 'false'
disabled: 'true'
builders:
- ppa
scm:
scm: 'false'
assignednode:
node: 'oneiric'
---
# tarball
modules:
- properties
- scm
- assignednode
- trigger_gerrit
- builders
- publisher_tarball
main:
name: 'reddwarf-tarball'
site: 'stackforge'
project: 'reddwarf'
authenticatedBulid: 'false'
disabled: 'true'
trigger:
triggerOnPatchsetUploadedEvent: 'false'
triggerOnChangeMergedEvent: 'false'
triggerOnCommentAddedEvent: 'false'
triggerOnRefUpdatedEvent: 'true'
triggerApprovalCategory: ''
triggerApprovalValue: ''
failureMessage: ''
projects:
- projectCompareType: 'PLAIN'
projectPattern: 'stackforge/reddwarf'
branchCompareType: 'REG_EXP'
branchPattern: '^(?!refs/).*$'
builders:
- gerrit_git_prep
- copy_bundle
- tarball
scm:
scm: 'true'
assignednode:
node: 'oneiric'