Refactor modules and templating.

Switch to using entry points for loading modules as well as
individual buliders, triggers, publishers, etc.

Remove most openstack-specific python code.

Change templating so it's less repetitive -- a single project
definition will suffice for multiple jobs or job-groups.

This outputs XML that is identical to the current production XML,
warts and all.  There are significant improvements that can be made
to the YAML in a separate change, as they will cause minor changes
to existing jobs (adding timestamps, logrotate, etc.).  These are
mostly marked with TODO in this change.

Change-Id: Idcfddb3b43b6cfef4b20919a84540706d7a0a0b1
This commit is contained in:
James E. Blair 2012-08-07 14:11:29 -07:00
parent ae673c9ac2
commit df87c42d5e
65 changed files with 3398 additions and 1312 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.pyc
*.egg-info

80
example/ceilometer.yml Normal file
View File

@ -0,0 +1,80 @@
- project:
name: ceilometer
github-org: stackforge
node: oneiric
jobs:
- python-jobs
- job:
name: 'gate-ceilometer-python26-essex'
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: https://github.com/stackforge/ceilometer
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
# TODO: logrotate this job
#logrotate:
# daysToKeep: 28
# numToKeep: -1
# artifactDaysToKeep: -1
# artifactNumToKeep: -1
triggers:
- zuul
builders:
- gerrit_git_prep
- python26_essex
# >= precise does not have python2.6
node: oneiric
- job:
name: 'gate-ceilometer-python27-essex'
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: https://github.com/stackforge/ceilometer
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
# TODO: logrotate this job
#logrotate:
# daysToKeep: 28
# numToKeep: -1
# artifactDaysToKeep: -1
# artifactNumToKeep: -1
triggers:
- zuul
builders:
- gerrit_git_prep
- python27_essex
node: oneiric

10
example/cinder.yml Normal file
View File

@ -0,0 +1,10 @@
- project:
name: cinder
github-org: openstack
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- openstack-publish-jobs

273
example/devstack-gate.yml Normal file
View File

@ -0,0 +1,273 @@
- job:
name: gate-devstack-gate-merge
project-type: freestyle
concurrent: true
node: precise
properties:
- github:
url: https://github.com/openstack-ci/devstack-gate
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul
builders:
- gerrit_git_prep
- job:
name: gate-integration-tests-devstack-vm
project-type: freestyle
concurrent: true
node: devstack-precise
properties:
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
parameters:
- label:
name: NODE_LABEL
description: Label of node to use for this build
default: devstack-precise
wrappers:
- timeout:
timeout: 40
fail: true
- timestamps
triggers:
- zuul
builders:
- trigger-builds:
- project: devstack-update-inprogress
predefined_parameters:
DEVSTACK_NODE_NAME=${NODE_NAME}
- shell: |
#!/bin/bash -xe
#
# This job also gates devstack-gate, but in case a previous run fails,
# we need to always make sure that we're starting with the latest copy
# from master, before we start applying changes to it. If a previous run
# leaves a bad copy of the gate script, we may get stuck.
#
if [[ ! -e devstack-gate ]]; then
git clone https://review.openstack.org/p/openstack-ci/devstack-gate
else
cd devstack-gate
git remote update
git reset --hard
git clean -x -f
git checkout master
git reset --hard remotes/origin/master
git clean -x -f
cd ..
fi
- shell: |
#!/bin/bash -xe
export PYTHONUNBUFFERED=true
cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
./safe-devstack-vm-gate-wrap.sh
publishers:
- archive:
artifacts: logs/*
- trigger-parameterized-builds:
- project: devstack-update-complete
when: complete
predefined_parameters:
DEVSTACK_NODE_NAME=${NODE_NAME}
- job:
name: gate-tempest-devstack-vm
project-type: freestyle
concurrent: true
node: devstack-precise
properties:
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
parameters:
- label:
name: NODE_LABEL
description: Label of node to use for this build
default: devstack-precise
wrappers:
- timeout:
timeout: 90
fail: true
- timestamps
triggers:
- zuul
builders:
- trigger-builds:
- project: devstack-update-inprogress
predefined_parameters:
DEVSTACK_NODE_NAME=${NODE_NAME}
- shell: |
#!/bin/bash -xe
#
# This job also gates devstack-gate, but in case a previous run fails,
# we need to always make sure that we're starting with the latest copy
# from master, before we start applying changes to it. If a previous run
# leaves a bad copy of the gate script, we may get stuck.
#
if [[ ! -e devstack-gate ]]; then
git clone https://review.openstack.org/p/openstack-ci/devstack-gate
else
cd devstack-gate
git remote update
git reset --hard
git clean -x -f
git checkout master
git reset --hard remotes/origin/master
git clean -x -f
cd ..
fi
- shell: |
#!/bin/bash -xe
export PYTHONUNBUFFERED=true
export DEVSTACK_GATE_TEMPEST=1
cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh
./safe-devstack-vm-gate-wrap.sh
publishers:
- archive:
artifacts: logs/*
- trigger-parameterized-builds:
- project: devstack-update-complete
when: complete
predefined_parameters:
DEVSTACK_NODE_NAME=${NODE_NAME}
# Called by devstack jobs to alert that they have started so that the
# jenkins slave they are running on can be disabled.
- job:
name: devstack-update-inprogress
project-type: freestyle
concurrent: false
node: master
properties:
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: 100
artifactDaysToKeep: -1
artifactNumToKeep: -1
wrappers:
- timeout:
timeout: 10
fail: true
- timestamps
builders:
- shell: |
#!/bin/bash -xe
if [[ ! -e devstack-gate ]]; then
git clone https://review.openstack.org/p/openstack-ci/devstack-gate
else
cd devstack-gate
git remote update
git pull --ff-only origin
cd ..
fi
- shell: |
#!/bin/bash -xe
export PYTHONUNBUFFERED=true
$WORKSPACE/devstack-gate/devstack-vm-inprogress.py $DEVSTACK_NODE_NAME
# Called by devstack jobs to alert that they have completed so that the
# jenkins slave may be deleted.
- job:
name: devstack-update-complete
project-type: freestyle
concurrent: false
node: master
properties:
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: 100
artifactDaysToKeep: -1
artifactNumToKeep: -1
wrappers:
- timeout:
timeout: 10
fail: true
- timestamps
builders:
- shell: |
#!/bin/bash -xe
if [[ ! -e devstack-gate ]]; then
git clone https://review.openstack.org/p/openstack-ci/devstack-gate
else
cd devstack-gate
git remote update
git pull --ff-only origin
cd ..
fi
- shell: |
#!/bin/bash -xe
export PYTHONUNBUFFERED=true
$WORKSPACE/devstack-gate/devstack-vm-delete.py $DEVSTACK_NODE_NAME

33
example/devstack.yml Normal file
View File

@ -0,0 +1,33 @@
- job:
name: gate-devstack-merge
project-type: freestyle
concurrent: true
node: precise
properties:
- github:
url: https://github.com/openstack-dev/devstack
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
# TODO: logrotate this job
#logrotate:
# daysToKeep: 28
# numToKeep: -1
# artifactDaysToKeep: -1
# artifactNumToKeep: -1
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul
builders:
- gerrit_git_prep

View File

@ -0,0 +1,35 @@
- project:
name: gerrit-verification-status-plugin
github-org: openstack-ci
node: precise
# TODO: standardize
#jobs:
#- gate-{name}-merge
- job:
name: gate-gerrit-verification-status-plugin-merge
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: https://github.com/openstack-ci/gerrit-verification-status-plugin
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
triggers:
- zuul
builders:
- gerrit_git_prep
node: precise

159
example/gerrit.yml Normal file
View File

@ -0,0 +1,159 @@
- project:
name: gerrit
github-org: openstack-ci
node: precise
# TODO: standardize
#jobs:
# - gate-{name}-merge
- job:
name: gate-gerrit-merge
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: https://github.com/openstack-ci/gerrit
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
triggers:
- zuul
builders:
- gerrit_git_prep
node: precise
- job:
name: check-gerrit-unittests
project-type: maven
concurrent: true
node: precise
wrappers:
- timeout:
timeout: 40
fail: true
properties:
- github:
url: https://github.com/openstack-ci/gerrit
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
triggers:
- zuul
prebuilders:
- gerrit_git_prep
- gerrit_preclean
maven:
root_module:
group_id: com.google.gerrit
artifact_id: gerrit-parent
goals: 'clean package -Dgerrit.include-documentation=1 -X'
postbuilders:
- gerrit_postrun
publishers:
- war:
site: 'nova.openstack.org'
warfile: 'gerrit-war/target/gerrit*.war'
target: 'tarballs/ci/test/'
- job:
name: gate-gerrit-unittests
project-type: maven
concurrent: true
node: precise
wrappers:
- timeout:
timeout: 40
fail: true
properties:
- github:
url: https://github.com/openstack-ci/gerrit
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
triggers:
- zuul
prebuilders:
- gerrit_git_prep
- gerrit_preclean
maven:
root_module:
group_id: com.google.gerrit
artifact_id: gerrit-parent
goals: 'clean package -Dgerrit.include-documentation=1 -X'
postbuilders:
- gerrit_postrun
- job:
name: gerrit-package
project-type: maven
concurrent: true
node: precise
wrappers:
- timeout:
timeout: 40
fail: true
properties:
- github:
url: https://github.com/openstack-ci/gerrit
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
triggers:
- zuul_post
prebuilders:
- gerrit_git_prep
- gerrit_preclean
maven:
root_module:
group_id: com.google.gerrit
artifact_id: gerrit-parent
goals: 'clean package -Dgerrit.include-documentation=1 -X'
postbuilders:
- gerrit_postrun
publishers:
- war:
site: 'nova.openstack.org'
warfile: 'gerrit-war/target/gerrit*.war'
target: 'tarballs/ci/'

90
example/gerritbot.yml Normal file
View File

@ -0,0 +1,90 @@
- project:
name: gerritbot
github-org: openstack-ci
node: precise
doc-publisher-site: ci.openstack.org
tarball-publisher-site: ci.openstack.org
jobs:
- gate-{name}-merge
- gate-{name}-pep8
- gate-{name}-pyflakes
# TODO: standardize
#- '{name}-sdist-tarball'
#- '{name}-pypi'
- job:
name: 'gerritbot-sdist-tarball'
project-type: freestyle
concurrent: true
node: precise
properties:
- github:
url: 'https://github.com/openstack-ci/gerritbot'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
triggers:
- zuul_post
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
builders:
- gerrit_git_prep
- shell: |
#!/bin/bash -xe
BRANCH=$GERRIT_REFNAME
BRANCH_PATH=`echo $BRANCH | tr / -`
tox -v -evenv python setup.py sdist
cp dist/* dist/gerritbot-$BRANCH_PATH.tar.gz
publishers:
- tarball:
project: 'gerritbot'
site: 'ci.openstack.org'
- job:
name: 'gerritbot-pypi'
concurrent: true
node: pypi
properties:
- github:
url: 'https://github.com/openstack-ci/gerritbot'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
triggers:
- zuul_post
builders:
- gerrit_git_prep
- shell: tox -v -evenv python setup.py sdist upload
publishers:
- tarball:
project: 'gerritbot'
site: 'ci.openstack.org'

45
example/gerritlib.yml Normal file
View File

@ -0,0 +1,45 @@
- project:
name: gerritlib
github-org: openstack-ci
node: precise
doc-publisher-site: ci.openstack.org
tarball-publisher-site: ci.openstack.org
jobs:
- gate-{name}-merge
- gate-{name}-pep8
- gate-{name}-pyflakes
# TODO: standardize
#- '{name}-pypi'
- job:
name: 'gerritlib-pypi'
concurrent: true
node: pypi
properties:
- github:
url: 'https://github.com/openstack-ci/gerritlib'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul_post
builders:
- gerrit_git_prep
- shell: tox -v -evenv python setup.py sdist upload
publishers:
- tarball:
project: 'ci'
site: 'nova.openstack.org'

10
example/glance.yml Normal file
View File

@ -0,0 +1,10 @@
- project:
name: glance
github-org: openstack
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- openstack-publish-jobs

7
example/heat.yml Normal file
View File

@ -0,0 +1,7 @@
- project:
name: heat
github-org: heat-api
node: oneiric
jobs:
- python-jobs

43
example/horizon.yml Normal file
View File

@ -0,0 +1,43 @@
- project:
name: horizon
github-org: openstack
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- openstack-publish-jobs
- job:
name: gate-horizon-selenium
concurrent: true
node: precise
properties:
- github:
url: https://github.com/openstack/horizon
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul
builders:
- gerrit_git_prep
- selenium

View File

@ -0,0 +1,34 @@
- project:
name: jenkins-job-builder
github-org: openstack
node: precise
# TODO: standardize
#jobs:
# - gate-{name}-merge
- job:
name: gate-jenkins-job-builder-merge
concurrent: true
node: precise
properties:
- github:
url: https://github.com/openstack-ci/jenkins-job-builder
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul
builders:
- gerrit_git_prep

10
example/keystone.yml Normal file
View File

@ -0,0 +1,10 @@
- project:
name: keystone
github-org: openstack
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- openstack-publish-jobs

115
example/macros.yml Normal file
View File

@ -0,0 +1,115 @@
- builder:
name: gerrit_git_prep
builders:
- shell: "/usr/local/jenkins/slave_scripts/gerrit-git-prep.sh review.openstack.org"
- builder:
name: coverage
builders:
- shell: "/usr/local/jenkins/slave_scripts/run-cover.sh"
- builder:
name: docs
builders:
- shell: "/usr/local/jenkins/slave_scripts/run-docs.sh"
- builder:
name: maven_test
builders:
- shell: "mvn test"
- builder:
name: maven_package
builders:
- shell: "mvn package"
- builder:
name: gerrit_package
builders:
- shell: "/usr/local/jenkins/slave_scripts/package-gerrit.sh"
- builder:
name: gerrit_preclean
#TODO: multiline:
builders:
- shell: "#!/bin/bash -xe\nrm -fr ~/.m2\nrm -fr ~/.java\n./tools/version.sh --release"
- builder:
name: gerrit_postrun
builders:
- shell: "./tools/version.sh --reset"
- builder:
name: pep8
builders:
- shell: "set -o pipefail ; tox -v -epep8 | tee pep8.txt ; set +o pipefail"
- builder:
name: pyflakes
builders:
- shell: "tox -v -epyflakes"
- builder:
name: puppet_syntax
builders:
# TODO: remove blank line
- shell: |
find . -iname *.pp | xargs puppet parser validate --modulepath=`pwd`/modules
for f in `find . -iname *.erb` ; do
erb -x -T '-' $f | ruby -c
done
- builder:
name: selenium
builders:
- shell: "/usr/local/jenkins/slave_scripts/run-selenium.sh"
- builder:
name: python26
builders:
- shell: "/usr/local/jenkins/slave_scripts/run-tox.sh 26"
- something:
arg: value
- builder:
name: python27
builders:
- shell: "/usr/local/jenkins/slave_scripts/run-tox.sh 27"
- builder:
name: python26_essex
builders:
- shell: "/usr/local/jenkins/slave_scripts/run-tox.sh 26-essex"
- builder:
name: python27_essex
builders:
- shell: "/usr/local/jenkins/slave_scripts/run-tox.sh 27-essex"
- builder:
name: tarball
builders:
- shell: "/usr/local/jenkins/slave_scripts/create-tarball.sh {project}"
# ======================================================================
- publisher:
name: tarball
publishers:
- archive:
artifacts: 'dist/*.tar.gz'
- scp:
site: '{site}'
source: 'dist/*.tar.gz'
target: 'tarballs/{project}/'
- publisher:
name: war
publishers:
- archive:
artifacts: '{warfile}'
- scp:
site: '{site}'
source: '{warfile}'
target: '{target}'

82
example/mraas.yml Normal file
View File

@ -0,0 +1,82 @@
- project:
name: MRaaS
github-org: stackforge
node: precise
- job:
name: gate-MRaaS-merge
concurrent: false
node: oneiric
properties:
- github:
url: https://github.com/stackforge/MRaaS
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- gerrit:
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/MRaaS'
branchCompareType: 'ANT'
branchPattern: '**'
builders:
- gerrit_git_prep
- job:
name: check-MRaaS-merge
concurrent: false
node: oneiric
properties:
- github:
url: https://github.com/stackforge/MRaaS
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- gerrit:
triggerOnPatchsetUploadedEvent: 'true'
triggerOnChangeMergedEvent: 'false'
triggerOnCommentAddedEvent: 'false'
triggerOnRefUpdatedEvent: 'false'
overrideVotes: 'true'
gerritBuildSuccessfulVerifiedValue: 1
gerritBuildFailedVerifiedValue: -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/MRaaS'
branchCompareType: 'ANT'
branchPattern: '**'
builders:
- gerrit_git_prep

12
example/nova.yml Normal file
View File

@ -0,0 +1,12 @@
- project:
name: nova
github-org: openstack
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- python-diablo-bitrot-jobs
- python-essex-bitrot-jobs
- openstack-publish-jobs

View File

@ -0,0 +1,63 @@
- project:
name: ci-puppet
github-org: openstack
node: precise
# TODO: standardize
#jobs:
# - gate-{name}-merge
- job:
name: gate-ci-puppet-merge
concurrent: true
node: precise
properties:
- github:
url: https://github.com/openstack/openstack-ci-puppet
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul
builders:
- gerrit_git_prep
- job:
name: gate-ci-puppet-syntax
concurrent: true
node: precise
properties:
- github:
url: https://github.com/openstack/openstack-ci-puppet
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul
builders:
- gerrit_git_prep
- puppet_syntax
- pyflakes

View File

@ -0,0 +1,10 @@
- project:
name: openstack-common
github-org: openstack
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- openstack-publish-jobs

View File

@ -0,0 +1,87 @@
- job-template:
name: '{name}-docs'
project-type: freestyle
concurrent: true
node: precise
properties:
- github:
url: 'https://github.com/{github-org}/{name}'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul_post
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
builders:
- gerrit_git_prep
- docs
publishers:
- ftp:
site: '{doc-publisher-site}'
source: 'doc/build/html/**'
target: 'developer/{name}'
remove-prefix: 'doc/build/html'
excludes: ''
- job-template:
name: '{name}-tarball'
project-type: freestyle
concurrent: true
node: precise
properties:
- github:
url: 'https://github.com/{github-org}/{name}'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul_post
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
builders:
- gerrit_git_prep
- tarball:
project: '{name}'
publishers:
- tarball:
project: '{name}'
site: '{tarball-publisher-site}'
- job-group:
name: openstack-publish-jobs
jobs:
- '{name}-docs'
- '{name}-tarball'

10
example/pbr.yml Normal file
View File

@ -0,0 +1,10 @@
- project:
name: pbr
github-org: openstack-dev
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- pypi-jobs

85
example/pypi-jobs.yml Normal file
View File

@ -0,0 +1,85 @@
- job-template:
name: '{name}-sdist-tarball'
project-type: freestyle
concurrent: true
node: precise
properties:
- github:
url: 'https://github.com/{github-org}/{name}'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul_post
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
builders:
- gerrit_git_prep
- shell: |
#!/bin/bash -xe
BRANCH=$GERRIT_REFNAME
BRANCH_PATH=`echo $BRANCH | tr / -`
tox -v -evenv python setup.py sdist
cp dist/* dist/{name}-$BRANCH_PATH.tar.gz
publishers:
- tarball:
project: '{name}'
site: '{tarball-publisher-site}'
- job-template:
name: '{name}-pypi'
project-type: freestyle
concurrent: true
node: pypi
properties:
- github:
url: 'https://github.com/{github-org}/{name}'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul_post
builders:
- gerrit_git_prep
- shell: tox -v -evenv python setup.py sdist upload
publishers:
- tarball:
project: '{name}'
site: '{tarball-publisher-site}'
- job-group:
name: pypi-jobs
jobs:
- '{name}-docs'
- '{name}-sdist-tarball'
- '{name}-pypi'

64
example/pypi-mirror.yml Normal file
View File

@ -0,0 +1,64 @@
- project:
name: pypi-mirror
github-org: openstack-ci
node: precise
# TODO: standardize
#jobs:
# - gate-{name}-merge
# - gate-{name}-pyflakes
- job:
name: gate-pypi-mirror-merge
project-type: freestyle
concurrent: true
node: precise
properties:
- github:
url: https://github.com/openstack-ci/pypi-mirror
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul
builders:
- gerrit_git_prep
- job:
name: gate-pypi-mirror-pyflakes
project-type: freestyle
concurrent: true
node: precise
properties:
- github:
url: https://github.com/openstack-ci/pypi-mirror
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul
builders:
- gerrit_git_prep
- pyflakes

View File

@ -0,0 +1,140 @@
- job-template:
name: 'periodic-{name}-python26-{branch-name}'
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: 'https://github.com/{github-org}/{name}'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
triggers:
- timed: '@daily'
builders:
- python26
scm:
- git:
url: 'git://github.com/{github-org}/{name}.git'
branches:
- 'origin/{branch}'
# >= precise does not have python2.6
node: oneiric
- job-template:
name: 'periodic-{name}-python27-{branch-name}'
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: 'https://github.com/{github-org}/{name}'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
triggers:
- timed: '@daily'
builders:
- python27
scm:
- git:
url: 'git://github.com/{github-org}/{name}.git'
branches:
- 'origin/{branch}'
node: '{node}'
- job-template:
name: 'periodic-{name}-docs-{branch-name}'
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: 'https://github.com/{github-org}/{name}'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
triggers:
- timed: '@daily'
builders:
- docs
scm:
- git:
url: 'git://github.com/{github-org}/{name}.git'
branches:
- 'origin/{branch}'
node: '{node}'
- job-group:
name: python-diablo-bitrot-jobs
branch: 'stable/diablo'
branch-name: 'stable-diablo'
node: oneiric
jobs:
- 'periodic-{name}-python26-{branch-name}'
- 'periodic-{name}-python27-{branch-name}'
- 'periodic-{name}-docs-{branch-name}'
- job-group:
name: python-essex-bitrot-jobs
branch: 'stable/essex'
branch-name: 'stable-essex'
node: precise
jobs:
- 'periodic-{name}-python26-{branch-name}'
- 'periodic-{name}-python27-{branch-name}'
- 'periodic-{name}-docs-{branch-name}'

View File

@ -0,0 +1,10 @@
- project:
name: python-cinderclient
github-org: openstack
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- pypi-jobs

View File

@ -0,0 +1,10 @@
- project:
name: python-glanceclient
github-org: openstack
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- pypi-jobs

267
example/python-jobs.yml Normal file
View File

@ -0,0 +1,267 @@
- job-template:
name: '{name}-coverage'
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: 'https://github.com/{github-org}/{name}'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
triggers:
- zuul_post
builders:
- gerrit_git_prep
- coverage
publishers:
- coverage
node: '{node}'
- job-template:
name: 'gate-{name}-pep8'
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: 'https://github.com/{github-org}/{name}'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
triggers:
- zuul
builders:
- gerrit_git_prep
- pep8
publishers:
- pep8
node: '{node}'
- job-template:
name: 'gate-{name}-python26'
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: 'https://github.com/{github-org}/{name}'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
triggers:
- zuul
builders:
- gerrit_git_prep
- python26
# >= precise does not have python2.6
node: oneiric
- job-template:
name: 'gate-{name}-python27'
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: 'https://github.com/{github-org}/{name}'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
parameters:
- label:
name: NODE_LABEL
description: Label of node to use for this build
default: '{node}'
triggers:
- zuul
builders:
- gerrit_git_prep
- python27
node: '{node}'
- job-template:
name: 'gate-{name}-merge'
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: 'https://github.com/{github-org}/{name}'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
triggers:
- zuul
builders:
- gerrit_git_prep
node: '{node}'
- job-template:
name: 'gate-{name}-docs'
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: 'https://github.com/{github-org}/{name}'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
triggers:
- zuul
builders:
- gerrit_git_prep
- docs
node: '{node}'
- job-template:
name: 'gate-{name}-pyflakes'
project-type: freestyle
concurrent: true
node: precise
properties:
- github:
url: 'https://github.com/{github-org}/{name}'
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul
builders:
- gerrit_git_prep
- pyflakes
- job-group:
name: python-jobs
jobs:
- '{name}-coverage'
- 'gate-{name}-merge'
- 'gate-{name}-pep8'
- 'gate-{name}-python26'
- 'gate-{name}-python27'
- 'gate-{name}-docs'
# pyflakes isn't standard

View File

@ -0,0 +1,10 @@
- project:
name: python-keystoneclient
github-org: openstack
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- pypi-jobs

View File

@ -0,0 +1,10 @@
- project:
name: python-novaclient
github-org: openstack
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- pypi-jobs

View File

@ -0,0 +1,10 @@
- project:
name: python-openstackclient
github-org: openstack
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- pypi-jobs

View File

@ -0,0 +1,10 @@
- project:
name: python-quantumclient
github-org: openstack
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- pypi-jobs

View File

@ -0,0 +1,10 @@
- project:
name: python-swiftclient
github-org: openstack
node: precise
tarball-publisher-site: swift.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- pypi-jobs

10
example/quantum.yml Normal file
View File

@ -0,0 +1,10 @@
- project:
name: quantum
github-org: openstack
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- openstack-publish-jobs

7
example/reddwarf.yml Normal file
View File

@ -0,0 +1,7 @@
- project:
name: reddwarf
github-org: stackforge
node: oneiric
jobs:
- python-jobs

34
example/requirements.yml Normal file
View File

@ -0,0 +1,34 @@
- project:
name: requirements
github-org: openstack
node: oneiric
# TODO: standardize
#jobs:
# - gate-{name}-merge
- job:
name: gate-requirements-merge
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
properties:
- github:
url: https://github.com/openstack/requirements
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
triggers:
- zuul
builders:
- gerrit_git_prep
node: precise

10
example/swift.yml Normal file
View File

@ -0,0 +1,10 @@
- project:
name: swift
github-org: openstack
node: precise
tarball-publisher-site: nova.openstack.org
doc-publisher-site: docs.openstack.org
jobs:
- python-jobs
- openstack-publish-jobs

67
example/tempest.yml Normal file
View File

@ -0,0 +1,67 @@
- project:
name: tempest
github-org: openstack
node: precise
# TODO: standardize
#jobs:
# - gate-{name}-merge
# - gate-{name}-pep8
- job:
name: gate-tempest-merge
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: https://github.com/openstack-ci/tempest
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
triggers:
- zuul
builders:
- gerrit_git_prep
node: precise
- job:
name: gate-tempest-pep8
concurrent: true
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
properties:
- github:
url: https://github.com/openstack-ci/tempest
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
triggers:
- zuul
builders:
- gerrit_git_prep
- pep8
publishers:
- pep8
node: precise

79
example/zuul.yml Normal file
View File

@ -0,0 +1,79 @@
- project:
name: zuul
github-org: openstack-ci
node: precise
jobs:
- python-jobs
# TODO: standardize these
#- gate-{name}-pyflakes
#- '{name}-docs
- job:
name: gate-zuul-pyflakes
project-type: freestyle
concurrent: true
node: precise
properties:
- github:
url: https://github.com/openstack-ci/zuul
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul
builders:
- gerrit_git_prep
- pyflakes
- job:
name: zuul-docs
project-type: freestyle
concurrent: true
node: precise
properties:
- github:
url: https://github.com/openstack-ci/zuul
- throttle:
max-per-node: 0
max-total: 0
option: project
enabled: false
wrappers:
- timeout:
timeout: 30
fail: true
- timestamps
triggers:
- zuul_post
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
builders:
- gerrit_git_prep
- docs
publishers:
- scp:
site: '173.203.107.207'
source: 'doc/build/html/**/*'
target: 'ci/zuul'
keep-hierarchy: true

47
jenkins-jobs Executable file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env python
import jenkins_jobs.builder
import argparse
def main():
parser = argparse.ArgumentParser()
subparser = parser.add_subparsers(help='update, test or delete job',
dest='command')
parser_update = subparser.add_parser('update')
parser_update.add_argument('path', help='Path to YAML file or directory')
parser_update.add_argument('name', help='name of job')
parser_test = subparser.add_parser('test')
parser_test.add_argument('path', help='Path to YAML file or directory')
parser_test.add_argument('-o', dest='output_dir', help='Path to output XML')
parser_test.add_argument('name', help='name of job', nargs='?')
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'
if not options.command == 'test':
conffp = open(conf, 'r')
config = ConfigParser.ConfigParser()
config.readfp(conffp)
else:
config = {}
builder = jenkins_jobs.builder.Builder(config.get('jenkins','url'),
config.get('jenkins','user'),
config.get('jenkins','password'))
if options.command == 'delete':
builder.delete_job()
elif options.command == 'update':
builder.update_job()
elif options.command == 'test':
builder.update_job(options.path, options.name,
output_dir=options.output_dir)
if __name__ == '__main__':
main()

View File

@ -1,279 +0,0 @@
#! /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 xml.etree.ElementTree as XML
from xml.dom import minidom
import jenkins
import ConfigParser
from StringIO import StringIO
import re
import pkgutil
import modules
class JenkinsJobsException(Exception): pass
parser = argparse.ArgumentParser()
subparser = parser.add_subparsers(help='update, test or delete job', dest='command')
parser_update = subparser.add_parser('update')
parser_update.add_argument('file', help='YAML file for update')
parser_update = subparser.add_parser('test')
parser_update.add_argument('file', help='YAML file for test')
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'
if not options.command == 'test':
conffp = open(conf, 'r')
config = ConfigParser.ConfigParser()
config.readfp(conffp)
class YamlParser(object):
def __init__(self, yfile):
self.registry = ModuleRegistry()
self.data = yaml.load_all(yfile)
self.it = self.data.__iter__()
self.job_name = None
self.template_data = None
self.current = None
self.current_template = None
self.template_it = None
self.reading_template = False
self.eof = False
self.seek_next_xml()
def process_template(self):
project_data = self.current['project']
template_file = file('templates/' + project_data['template'] + '.yml', 'r')
template = template_file.read()
template_file.close()
values = self.current['values'].iteritems()
for key, value in values:
key = '@' + key.upper() + '@'
template = template.replace(key, value)
template_steam = StringIO(template)
self.template_data = yaml.load_all(template_steam)
self.template_it = self.template_data.__iter__()
self.reading_template = True
def get_next_xml(self):
if not self.eof:
if self.reading_template:
data = XmlParser(self.current_template, self.registry)
self.job_name = self.current_template['main']['name']
else:
data = XmlParser(self.current, self.registry)
self.job_name = self.current['main']['name']
self.seek_next_xml()
return data
else:
raise JenkinsJobsException('End of file')
def seek_next_xml(self):
if self.reading_template:
try:
self.current_template = self.template_it.next()
return
except StopIteration:
self.reading_template = False
try:
self.current = self.it.next()
except StopIteration:
self.eof = True
if self.current.has_key('project'):
self.process_template()
self.current_template = self.template_it.next()
def get_name(self):
return self.job_name
class ModuleRegistry(object):
# TODO: make this extensible
def __init__(self):
self.modules = []
self.handlers = {}
for importer, modname, ispkg in pkgutil.iter_modules(modules.__path__):
module = __import__('modules.'+modname, fromlist=['register'])
register = getattr(module, 'register', None)
if register:
register(self)
def registerModule(self, mod):
self.modules.append(mod)
self.modules.sort(lambda a, b: cmp(a.sequence, b.sequence))
def registerHandler(self, category, name, method):
cat_dict = self.handlers.get(category, {})
if not cat_dict:
self.handlers[category] = cat_dict
cat_dict[name] = method
def getHandler(self, category, name):
return self.handlers[category][name]
class XmlParser(object):
def __init__(self, data, registry):
self.data = data
self.registry = registry
self._build()
def _build(self):
for module in self.registry.modules:
if hasattr(module, 'root_xml'):
element = module.root_xml(self.data)
if element is not None:
self.xml = element
for module in self.registry.modules:
if hasattr(module, 'handle_data'):
module.handle_data(self.data)
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'
if self.data['main'].get('disabled'):
XML.SubElement(self.xml, 'disabled').text = 'true'
else:
XML.SubElement(self.xml, 'disabled').text = 'false'
XML.SubElement(self.xml, 'blockBuildWhenDownstreamBuilding').text = 'false'
XML.SubElement(self.xml, 'blockBuildWhenUpstreamBuilding').text = 'false'
if self.data['main'].get('concurrent'):
XML.SubElement(self.xml, 'concurrentBuild').text = 'true'
else:
XML.SubElement(self.xml, 'concurrentBuild').text = 'false'
for module in self.registry.modules:
if hasattr(module, 'gen_xml'):
module.gen_xml(self.xml, self.data)
def md5(self):
return hashlib.md5(self.output()).hexdigest()
# Pretty printing ideas from http://stackoverflow.com/questions/749796/pretty-printing-xml-in-python
pretty_text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)
def output(self):
out = minidom.parseString(XML.tostring(self.xml)).toprettyxml(indent=' ')
return self.pretty_text_re.sub('>\g<1></', out)
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.Jenkins(url, user, password)
def update_job(self, job_name, xml):
if self.is_job(job_name):
self.jenkins.reconfig_job(job_name, xml)
else:
self.jenkins.create_job(job_name, xml)
def is_job(self, job_name):
return self.jenkins.job_exists(job_name)
def get_job_md5(self, job_name):
xml = self.jenkins.get_job_config(job_name)
return hashlib.md5(xml).hexdigest()
def delete_job(self, job_name):
if self.is_job(job_name):
self.jenkins.delete_job(job_name)
def delete_job():
remote_jenkins = Jenkins(config.get('jenkins','url'), config.get('jenkins','user'), config.get('jenkins','password'))
remote_jenkins.delete_job(options.name)
def update_job(test = False):
if os.path.isdir(options.file):
files_to_process = [os.path.join(options.file, f)
for f in os.listdir(options.file)]
else:
files_to_process = [options.file]
cache = CacheStorage()
if not test:
remote_jenkins = Jenkins(config.get('jenkins','url'), config.get('jenkins','user'), config.get('jenkins','password'))
for in_file in files_to_process:
yparse = YamlParser(open(in_file, 'r'))
while True:
try:
xml = yparse.get_next_xml()
job = yparse.get_name()
if test:
print xml.output()
continue
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 JenkinsJobsException:
break
if options.command == 'delete':
delete_job()
elif options.command == 'update':
update_job()
elif options.command == 'test':
update_job(True)

274
jenkins_jobs/builder.py Normal file
View File

@ -0,0 +1,274 @@
#!/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 hashlib
import yaml
import xml.etree.ElementTree as XML
from xml.dom import minidom
import jenkins
import ConfigParser
from StringIO import StringIO
import re
import pkgutil
import pkg_resources
import pprint
import sys
class JenkinsJobsException(Exception): pass
class YamlParser(object):
def __init__(self):
self.registry = ModuleRegistry()
self.data = {}
self.jobs = []
def parse(self, fn):
data = yaml.load(open(fn))
for item in data:
cls, dfn = item.items()[0]
group = self.data.get(cls, {})
name = dfn['name']
group[name] = dfn
self.data[cls] = group
def getJob(self, name):
return self.data.get('job', {}).get(name, None)
def getJobGroup(self, name):
return self.data.get('job-group', {}).get(name, None)
def getJobTemplate(self, name):
return self.data.get('job-template', {}).get(name, None)
def generateXML(self):
changed = True
while changed:
changed = False
for module in self.registry.modules:
if hasattr(module, 'handle_data'):
if module.handle_data(self):
changed = True
for job in self.data.get('job', {}).values():
self.getXMLForJob(job)
for project in self.data.get('project', {}).values():
for jobname in project.get('jobs', []):
job = self.getJob(jobname)
if job:
# Just naming an existing defined job
continue
# see if it's a job group
group = self.getJobGroup(jobname)
if group:
for group_jobname in group['jobs']:
job = self.getJob(group_jobname)
if job:
continue
template = self.getJobTemplate(group_jobname)
# Allow a group to override parameters set by a project
d = {}
d.update(project)
d.update(group)
# Except name, since the group's name is not useful
d['name'] = project['name']
if template:
self.getXMLForTemplateJob(d, template)
continue
# see if it's a template
template = self.getJobTemplate(jobname)
if template:
self.getXMLForTemplateJob(project, template)
def getXMLForTemplateJob(self, project, template):
s = yaml.dump(template, default_flow_style=False)
s = s.format(**project)
data = yaml.load(s)
self.getXMLForJob(data)
def getXMLForJob(self, data):
kind = data.get('project-type', 'freestyle')
for ep in pkg_resources.iter_entry_points(
group='jenkins_jobs.projects', name=kind):
Mod = ep.load()
mod = Mod(self.registry)
xml = mod.root_xml(data)
self.gen_xml(xml, data)
job = XmlJob(xml, data['name'])
self.jobs.append(job)
break
def gen_xml(self, xml, data):
XML.SubElement(xml, 'actions')
description = XML.SubElement(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(xml, 'keepDependencies').text = 'false'
if data.get('disabled'):
XML.SubElement(xml, 'disabled').text = 'true'
else:
XML.SubElement(xml, 'disabled').text = 'false'
XML.SubElement(xml, 'blockBuildWhenDownstreamBuilding').text = 'false'
XML.SubElement(xml, 'blockBuildWhenUpstreamBuilding').text = 'false'
if data.get('concurrent'):
XML.SubElement(xml, 'concurrentBuild').text = 'true'
else:
XML.SubElement(xml, 'concurrentBuild').text = 'false'
for module in self.registry.modules:
if hasattr(module, 'gen_xml'):
module.gen_xml(self, xml, data)
class ModuleRegistry(object):
# TODO: make this extensible
def __init__(self):
self.modules = []
self.handlers = {}
for entrypoint in pkg_resources.iter_entry_points(
group='jenkins_jobs.modules'):
Mod = entrypoint.load()
mod = Mod(self)
self.modules.append(mod)
self.modules.sort(lambda a, b: cmp(a.sequence, b.sequence))
def registerHandler(self, category, name, method):
cat_dict = self.handlers.get(category, {})
if not cat_dict:
self.handlers[category] = cat_dict
cat_dict[name] = method
def getHandler(self, category, name):
return self.handlers[category][name]
class XmlJob(object):
def __init__(self, xml, name):
self.xml = xml
self.name = name
def md5(self):
return hashlib.md5(self.output()).hexdigest()
# Pretty printing ideas from http://stackoverflow.com/questions/749796/pretty-printing-xml-in-python
pretty_text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)
def output(self):
out = minidom.parseString(XML.tostring(self.xml)).toprettyxml(indent=' ')
return self.pretty_text_re.sub('>\g<1></', out)
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.Jenkins(url, user, password)
def update_job(self, job_name, xml):
if self.is_job(job_name):
self.jenkins.reconfig_job(job_name, xml)
else:
self.jenkins.create_job(job_name, xml)
def is_job(self, job_name):
return self.jenkins.job_exists(job_name)
def get_job_md5(self, job_name):
xml = self.jenkins.get_job_config(job_name)
return hashlib.md5(xml).hexdigest()
def delete_job(self, job_name):
if self.is_job(job_name):
self.jenkins.delete_job(job_name)
class Builder(object):
def __init__(self, jenkins_url, jenkins_user, jenkins_password):
self.jenkins = Jenkins(jenkins_url, jenkins_user, jenkins_password)
self.cache = CacheStorage()
def delete_job(self):
self.jenkins.delete_job(options.name)
def update_job(self, fn, name=None, output_dir=None):
if os.path.isdir(fn):
files_to_process = [os.path.join(fn, f)
for f in os.listdir(fn)
if (f.endswith('.yml') or f.endswith('.yaml'))]
else:
files_to_process = [fn]
parser = YamlParser()
for in_file in files_to_process:
parser.parse(in_file)
parser.generateXML()
parser.jobs.sort(lambda a,b: cmp(a.name, b.name))
for job in parser.jobs:
if name and job.name != name:
continue
if output_dir:
#print '='*70
#print job.name
#print '-'*70
if name:
print job.output()
continue
fn = os.path.join(output_dir, job.name)
f = open(fn, 'w')
f.write(job.output())
f.close()
continue
md5 = job.md5()
if (remote_jenkins.is_job(job.nam)
and not self.cache.is_cached(job.name)):
old_md5 = remote_jenkins.get_job_md5(job.name)
self.cache.set(job.name, old_md5)
if self.cache.has_changed(job.name, md5):
remote_jenkins.update_job(job.name, xml.output())
self.cache.set(job.name, md5)

View File

View File

@ -1,5 +1,4 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -19,18 +18,14 @@
# - node: 'oneiric'
import xml.etree.ElementTree as XML
import jenkins_jobs.modules.base
def register(registry):
mod = AssignedNode()
registry.registerModule(mod)
class AssignedNode(object):
class AssignedNode(jenkins_jobs.modules.base.Base):
sequence = 40
def gen_xml(self, xml_parent, data):
node = data['assignednode']['node']
XML.SubElement(xml_parent, 'assignedNode').text = node
XML.SubElement(xml_parent, 'canRoam').text = 'false'
def gen_xml(self, parser, xml_parent, data):
node = data.get('node', None)
if node:
XML.SubElement(xml_parent, 'assignedNode').text = node
XML.SubElement(xml_parent, 'canRoam').text = 'false'

View File

@ -0,0 +1,58 @@
# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# 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.
# Base class for a jenkins_jobs module
import pkg_resources
import yaml
class Base(object):
sequence = 10
def __init__(self, registry):
self.registry = registry
def _dispatch(self, component_type, component_list_type,
parser, xml_parent,
component, template_data={}):
if isinstance(component, dict):
# The component is a sigleton dictionary of name: dict(args)
name, component_data = component.items()[0]
if template_data:
# Template data contains values that should be interpolated
# into the component definition
s = yaml.dump(component_data, default_flow_style=False)
s = s.format(**template_data)
component_data = yaml.load(s)
else:
# The component is a simple string name, eg "run-tests"
name = component
component_data = {}
# Look for a component function defined in an entry point
for ep in pkg_resources.iter_entry_points(
group='jenkins_jobs.{0}'.format(component_list_type), name=name):
func = ep.load()
func(parser, xml_parent, component_data)
else:
# Otherwise, see if it's defined as a macro
component = parser.data.get(component_type, {}).get(name)
if component:
for b in component[component_list_type]:
# Pass component_data in as template data to this function
# so that if the macro is invoked with arguments,
# the arguments are interpolated into the real defn.
self._dispatch(component_type, component_list_type,
parser, xml_parent, b, component_data)

View File

@ -0,0 +1,63 @@
# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# 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
import jenkins_jobs.modules.base
import pkg_resources
import yaml
def shell(parser, xml_parent, data):
shell = XML.SubElement(xml_parent, 'hudson.tasks.Shell')
XML.SubElement(shell, 'command').text = data
def trigger_builds(parser, xml_parent, data):
tbuilder = XML.SubElement(xml_parent,
'hudson.plugins.parameterizedtrigger.TriggerBuilder')
configs = XML.SubElement(tbuilder, 'configs')
for project_def in data:
tconfig = XML.SubElement(configs,
'hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig')
tconfigs = XML.SubElement(tconfig, 'configs')
if project_def.has_key('predefined_parameters'):
params = XML.SubElement(tconfigs,
'hudson.plugins.parameterizedtrigger.PredefinedBuildParameters')
properties = XML.SubElement(params, 'properties')
properties.text = project_def['predefined_parameters']
else:
tconfigs.set('class', 'java.util.Collections$EmptyList')
projects = XML.SubElement(tconfig, 'projects')
projects.text = project_def['project']
condition = XML.SubElement(tconfig, 'condition')
condition.text = 'ALWAYS'
trigger_with_no_params = XML.SubElement(tconfig, 'triggerWithNoParameters')
trigger_with_no_params.text = 'false'
build_all_nodes_with_label = XML.SubElement(tconfig, 'buildAllNodesWithLabel')
build_all_nodes_with_label.text = 'false'
class Builders(jenkins_jobs.modules.base.Base):
sequence = 60
def gen_xml(self, parser, xml_parent, data):
for alias in ['prebuilders', 'builders', 'postbuilders']:
if alias in data:
builders = XML.SubElement(xml_parent, alias)
for builder in data[alias]:
self._dispatch('builder', 'builders',
parser, builders, builder)

View File

@ -1,5 +1,4 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -22,23 +21,16 @@
# artifactNumToKeep: -1
import xml.etree.ElementTree as XML
import jenkins_jobs.modules.base
def register(registry):
mod = LogRotate()
registry.registerModule(mod)
class LogRotate(object):
class LogRotate(jenkins_jobs.modules.base.Base):
sequence = 10
def handle_data(self, data):
self.data = data
def gen_xml(self, xml_parent, data):
if self.data.has_key('logrotate'):
def gen_xml(self, parser, xml_parent, data):
if data.has_key('logrotate'):
lr_xml = XML.SubElement(xml_parent, 'logRotator')
logrotate = self.data['logrotate']
logrotate = data['logrotate']
lr_days = XML.SubElement(lr_xml, 'daysToKeep')
lr_days.text = str(logrotate['daysToKeep'])
lr_num = XML.SubElement(lr_xml, 'numToKeep')

View File

@ -1,5 +1,4 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -22,18 +21,12 @@
# goals: 'test'
import xml.etree.ElementTree as XML
import jenkins_jobs.modules.base
def register(registry):
mod = Freestyle()
registry.registerModule(mod)
class Freestyle(object):
class Freestyle(jenkins_jobs.modules.base.Base):
sequence = 0
def root_xml(self, data):
if 'maven' in data:
return None
xml_parent = XML.Element('project')
return xml_parent

View File

@ -1,5 +1,4 @@
#! /usr/bin/env python
# Copyright (C) 2012 OpenStack, LLC.
# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -22,14 +21,9 @@
# goals: 'test'
import xml.etree.ElementTree as XML
import jenkins_jobs.modules.base
def register(registry):
mod = Maven()
registry.registerModule(mod)
class Maven(object):
class Maven(jenkins_jobs.modules.base.Base):
sequence = 0
def root_xml(self, data):

View File

@ -0,0 +1,117 @@
# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# 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
import jenkins_jobs.modules.base
def github(parser, xml_parent, data):
github = XML.SubElement(xml_parent,
'com.coravy.hudson.plugins.github.GithubProjectProperty')
github_url = XML.SubElement(github, 'projectUrl')
github_url.text = data['url']
def throttle(parser, xml_parent, data):
throttle = XML.SubElement(xml_parent,
'hudson.plugins.throttleconcurrents.ThrottleJobProperty')
XML.SubElement(throttle, 'maxConcurrentPerNode').text = str(
data.get('max-per-node'))
XML.SubElement(throttle, 'maxConcurrentTotal').text = str(
data.get('max-total'))
# TODO: What's "categories"?
#XML.SubElement(throttle, 'categories')
if data.get('enabled', True):
XML.SubElement(throttle, 'throttleEnabled').text = 'true'
else:
XML.SubElement(throttle, 'throttleEnabled').text = 'false'
XML.SubElement(throttle, 'throttleOption').text = data.get('option')
XML.SubElement(throttle, 'configVersion').text = '1'
def authenticated_build(parser, xml_parent, data):
# TODO: generalize this
if data:
security = XML.SubElement(xml_parent,
'hudson.security.AuthorizationMatrixProperty')
XML.SubElement(security, 'permission').text = \
'hudson.model.Item.Build:authenticated'
def base_param(parser, xml_parent, data, do_default, ptype):
pdef = XML.SubElement(xml_parent, ptype)
XML.SubElement(pdef, 'name').text = data['name']
XML.SubElement(pdef, 'description').text = data['description']
if do_default:
default = data.get('default', None)
if default:
XML.SubElement(pdef, 'defaultValue').text = default
else:
XML.SubElement(pdef, 'defaultValue')
def string_param(parser, xml_parent, data):
base_param(parser, xml_parent, data, True,
'hudson.model.StringParameterDefinition')
def bool_param(parser, xml_parent, data):
base_param(parser, xml_parent, data, True,
'hudson.model.BooleanParameterDefinition')
def file_param(parser, xml_parent, data):
base_param(parser, xml_parent, data, False,
'hudson.model.FileParameterDefinition')
def text_param(parser, xml_parent, data):
base_param(parser, xml_parent, data, True,
'hudson.model.TextParameterDefinition')
def label_param(parser, xml_parent, data):
base_param(parser, xml_parent, data, True,
'org.jvnet.jenkins.plugins.nodelabelparameter.LabelParameterDefinition')
def http_endpoint(parser, xml_parent, data):
endpoint_element = XML.SubElement(xml_parent,
'com.tikal.hudson.plugins.notification.Endpoint')
XML.SubElement(endpoint_element, 'protocol').text = 'HTTP'
XML.SubElement(endpoint_element, 'url').text = data['url']
class Properties(jenkins_jobs.modules.base.Base):
sequence = 20
def gen_xml(self, parser, xml_parent, data):
properties = XML.SubElement(xml_parent, 'properties')
for prop in data.get('properties', []):
self._dispatch('property', 'properties',
parser, properties, prop)
parameters = data.get('parameters', [])
if parameters:
pdefp = XML.SubElement(properties,
'hudson.model.ParametersDefinitionProperty')
pdefs = XML.SubElement(pdefp, 'parameterDefinitions')
for param in parameters:
self._dispatch('parameter', 'parameters',
parser, pdefs, param)
notifications = data.get('notifications', [])
if notifications:
notify_element = XML.SubElement(properties,
'com.tikal.hudson.plugins.notification.HudsonNotificationProperty')
endpoints_element = XML.SubElement(notify_element, 'endpoints')
for endpoint in notifications:
self._dispatch('notification', 'notifications',
parser, endpoints_element, endpoint)

View File

@ -0,0 +1,290 @@
# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# 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
import jenkins_jobs.modules.base
def archive(parser, xml_parent, data):
archiver = XML.SubElement(xml_parent, 'hudson.tasks.ArtifactArchiver')
artifacts = XML.SubElement(archiver, 'artifacts')
artifacts.text = data['artifacts']
if 'excludes' in data:
excludes = XML.SubElement(archiver, 'excludes')
excludes.text = data['excludes']
latest = XML.SubElement(archiver, 'latestOnly')
latest_only = data.get('latest_only', False)
if latest_only:
latest.text = 'true'
else:
latest.text = 'false'
def trigger_parameterized_builds(parser, xml_parent, data):
tbuilder = XML.SubElement(xml_parent,
'hudson.plugins.parameterizedtrigger.BuildTrigger')
configs = XML.SubElement(tbuilder, 'configs')
for project_def in data:
tconfig = XML.SubElement(configs,
'hudson.plugins.parameterizedtrigger.BuildTriggerConfig')
tconfigs = XML.SubElement(tconfig, 'configs')
if project_def.has_key('predefined_parameters'):
params = XML.SubElement(tconfigs,
'hudson.plugins.parameterizedtrigger.PredefinedBuildParameters')
properties = XML.SubElement(params, 'properties')
properties.text = project_def['predefined_parameters']
else:
tconfigs.set('class', 'java.util.Collections$EmptyList')
projects = XML.SubElement(tconfig, 'projects')
projects.text = project_def['project']
condition = XML.SubElement(tconfig, 'condition')
condition.text = project_def.get('condition', 'ALWAYS')
trigger_with_no_params = XML.SubElement(tconfig,
'triggerWithNoParameters')
trigger_with_no_params.text = 'false'
def coverage(parser, xml_parent, data):
cobertura = XML.SubElement(xml_parent,
'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'
# Jenkins Job module for publishing via ftp
# publish:
# site: 'docs.openstack.org'
# remote_dir: 'dest/dir'
# source_files: 'base/source/dir/**'
# remove_prefix: 'base/source/dir'
# excludes: '**/*.exludedfiletype'
#
# This will upload everything under $workspace/base/source/dir to
# docs.openstack.org $ftpdir/dest/dir exluding the excluded file type.
def ftp(parser, xml_parent, data):
"""
Example XML:
<publishers>
<jenkins.plugins.publish__over__ftp.BapFtpPublisherPlugin>
<consolePrefix>FTP: </consolePrefix>
<delegate>
<publishers>
<jenkins.plugins.publish__over__ftp.BapFtpPublisher>
<configName>docs.openstack.org</configName>
<verbose>true</verbose>
<transfers>
<jenkins.plugins.publish__over__ftp.BapFtpTransfer>
<remoteDirectory></remoteDirectory>
<sourceFiles>openstack-identity-api/target/docbkx/webhelp/api/openstack-identity-service/2.0/**</sourceFiles>
<excludes>**/*.xml,**/null*</excludes>
<removePrefix>openstack-identity-api/target/docbkx/webhelp</removePrefix>
<remoteDirectorySDF>false</remoteDirectorySDF>
<flatten>false</flatten>
<cleanRemote>false</cleanRemote>
<asciiMode>false</asciiMode>
</jenkins.plugins.publish__over__ftp.BapFtpTransfer>
</transfers>
<useWorkspaceInPromotion>false</useWorkspaceInPromotion>
<usePromotionTimestamp>false</usePromotionTimestamp>
</jenkins.plugins.publish__over__ftp.BapFtpPublisher>
</publishers>
<continueOnError>false</continueOnError>
<failOnError>false</failOnError>
<alwaysPublishFromMaster>false</alwaysPublishFromMaster>
<hostConfigurationAccess class="jenkins.plugins.publish_over_ftp.BapFtpPublisherPlugin" reference="../.."/>
</delegate>
</jenkins.plugins.publish__over__ftp.BapFtpPublisherPlugin>
</publishers>
"""
outer_ftp = XML.SubElement(xml_parent,
'jenkins.plugins.publish__over__ftp.BapFtpPublisherPlugin')
XML.SubElement(outer_ftp, 'consolePrefix').text = 'FTP: '
delegate = XML.SubElement(outer_ftp, 'delegate')
publishers = XML.SubElement(delegate, 'publishers')
ftp = XML.SubElement(publishers,
'jenkins.plugins.publish__over__ftp.BapFtpPublisher')
XML.SubElement(ftp, 'configName').text = data['site']
XML.SubElement(ftp, 'verbose').text = 'true'
transfers = XML.SubElement(ftp, 'transfers')
ftp_transfers = XML.SubElement(transfers, 'jenkins.plugins.publish__over__ftp.BapFtpTransfer')
XML.SubElement(ftp_transfers, 'remoteDirectory').text = data['target']
XML.SubElement(ftp_transfers, 'sourceFiles').text = data['source']
XML.SubElement(ftp_transfers, 'excludes').text = data['excludes']
XML.SubElement(ftp_transfers, 'removePrefix').text = data['remove-prefix']
XML.SubElement(ftp_transfers, 'remoteDirectorySDF').text = 'false'
XML.SubElement(ftp_transfers, 'flatten').text = 'false'
XML.SubElement(ftp_transfers, 'cleanRemote').text = 'false'
XML.SubElement(ftp_transfers, 'asciiMode').text = 'false'
XML.SubElement(ftp, 'useWorkspaceInPromotion').text = 'false'
XML.SubElement(ftp, 'usePromotionTimestamp').text = 'false'
XML.SubElement(delegate, 'continueOnError').text = 'false'
XML.SubElement(delegate, 'failOnError').text = 'false'
XML.SubElement(delegate, 'alwaysPublishFromMaster').text = 'false'
XML.SubElement(delegate, 'hostConfigurationAccess',
{'class': 'jenkins.plugins.publish_over_ftp.BapFtpPublisherPlugin',
'reference': '../..'})
# Jenkins Job module for coverage publishers
# To use you add the following into your YAML:
# publisher:
# results: 'nosetests.xml'
def junit(parser, xml_parent, data):
junitresult = XML.SubElement(xml_parent,
'hudson.tasks.junit.JUnitResultArchiver')
XML.SubElement(junitresult, 'testResults').text = data['results']
XML.SubElement(junitresult, 'keepLongStdio').text = "true"
XML.SubElement(junitresult, 'testDataPublishers')
def _pep8_add_entry(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')
# Jenkins Job module for pep8 publishers
# No additional YAML needed
def pep8(parser, xml_parent, data):
violations = XML.SubElement(xml_parent,
'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')
_pep8_add_entry(configs, 'checkstyle')
_pep8_add_entry(configs, 'codenarc')
_pep8_add_entry(configs, 'cpd')
_pep8_add_entry(configs, 'cpplint')
_pep8_add_entry(configs, 'csslint')
_pep8_add_entry(configs, 'findbugs')
_pep8_add_entry(configs, 'fxcop')
_pep8_add_entry(configs, 'gendarme')
_pep8_add_entry(configs, 'jcreport')
_pep8_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'
_pep8_add_entry(configs, 'pmd')
_pep8_add_entry(configs, 'pylint')
_pep8_add_entry(configs, 'simian')
_pep8_add_entry(configs, 'stylecop')
XML.SubElement(config, 'limit').text = '100'
XML.SubElement(config, 'sourcePathPattern')
XML.SubElement(config, 'fauxProjectPath')
XML.SubElement(config, 'encoding').text = 'default'
# Jenkins Job module for generic scp publishing
# To use you add the following into your YAML:
# publish:
# site: 'openstack-ci.openstack.org'
# source: 'doc/build/html/**/*'
# target_path: 'ci/zuul'
# keep_heirarchy: 'true'
def scp(parser, xml_parent, data):
site = data['site']
scp = XML.SubElement(xml_parent,
'be.certipost.hudson.plugin.SCPRepositoryPublisher')
XML.SubElement(scp, 'siteName').text = site
entries = XML.SubElement(scp, 'entries')
entry = XML.SubElement(entries, 'be.certipost.hudson.plugin.Entry')
XML.SubElement(entry, 'filePath').text = data['target']
XML.SubElement(entry, 'sourceFile').text = data['source']
if data.get('keep-hierarchy', False):
XML.SubElement(entry, 'keepHierarchy').text = 'true'
else:
XML.SubElement(entry, 'keepHierarchy').text = 'false'
class Publishers(jenkins_jobs.modules.base.Base):
sequence = 70
def gen_xml(self, parser, xml_parent, data):
publishers = XML.SubElement(xml_parent, 'publishers')
for action in data.get('publishers', []):
self._dispatch('publisher', 'publishers',
parser, publishers, action)

View File

@ -0,0 +1,72 @@
# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# 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
import jenkins_jobs.modules.base
def git(self, xml_parent, data):
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 = data['url']
xml_branches = XML.SubElement(scm, 'branches')
branches = data.get('branches', ['**'])
for branch in branches:
bspec = XML.SubElement(xml_branches, 'hudson.plugins.git.BranchSpec')
XML.SubElement(bspec, 'name').text = branch
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')
class SCM(jenkins_jobs.modules.base.Base):
sequence = 30
def gen_xml(self, parser, xml_parent, data):
scms = data.get('scm', [])
if scms:
for scm in data.get('scm', []):
self._dispatch('scm', 'scm',
parser, xml_parent, scm)
else:
XML.SubElement(xml_parent, 'scm', {'class':'hudson.scm.NullSCM'})

View File

@ -0,0 +1,124 @@
# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# 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
# overrideVotes: 'true'
# gerritBuildSuccessfulVerifiedValue: 1
# gerritBuildFailedVerifiedValue: -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: '**'
# ...
#
# triggerApprovalCategory and triggerApprovalValue only required if triggerOnCommentAddedEvent: 'true'
import xml.etree.ElementTree as XML
import jenkins_jobs.modules.base
def gerrit(parser, xml_parent, data):
projects = data['projects']
gtrig = XML.SubElement(xml_parent,
'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 = \
data['triggerOnPatchsetUploadedEvent']
XML.SubElement(gtrig, 'triggerOnChangeMergedEvent').text = \
data['triggerOnChangeMergedEvent']
XML.SubElement(gtrig, 'triggerOnCommentAddedEvent').text = \
data['triggerOnCommentAddedEvent']
XML.SubElement(gtrig, 'triggerOnRefUpdatedEvent').text = \
data['triggerOnRefUpdatedEvent']
if data.has_key('overrideVotes') and data['overrideVotes'] == 'true':
XML.SubElement(gtrig, 'gerritBuildSuccessfulVerifiedValue').text = \
str(data['gerritBuildSuccessfulVerifiedValue'])
XML.SubElement(gtrig, 'gerritBuildFailedVerifiedValue').text = \
str(data['gerritBuildFailedVerifiedValue'])
if data['triggerOnCommentAddedEvent'] == 'true':
XML.SubElement(gtrig, 'commentAddedTriggerApprovalCategory').text = \
data['triggerApprovalCategory']
XML.SubElement(gtrig, 'commentAddedTriggerApprovalValue').text = \
str(data['triggerApprovalValue'])
XML.SubElement(gtrig, 'buildStartMessage')
XML.SubElement(gtrig, 'buildFailureMessage').text = data['failureMessage']
XML.SubElement(gtrig, 'buildSuccessfulMessage')
XML.SubElement(gtrig, 'buildUnstableMessage')
XML.SubElement(gtrig, 'customUrl')
# Jenkins Job module for scm polling triggers
# To use add the following into your YAML:
# trigger:
# pollscm: '@midnight'
# or
# pollscm: '*/15 * * * *'
def pollscm(parser, xml_parent, data):
scmtrig = XML.SubElement(xml_parent, 'hudson.triggers.SCMTrigger')
XML.SubElement(scmtrig, 'spec').text = data
# Jenkins Job module for timed triggers
# To use add the following into your YAML:
# trigger:
# timed: '@midnight'
# or
# timed: '*/15 * * * *'
def timed(parser, xml_parent, data):
scmtrig = XML.SubElement(xml_parent, 'hudson.triggers.TimerTrigger')
XML.SubElement(scmtrig, 'spec').text = data
class Triggers(jenkins_jobs.modules.base.Base):
sequence = 50
def gen_xml(self, parser, xml_parent, data):
triggers = data.get('triggers', [])
if not triggers:
return
trig_e = XML.SubElement(xml_parent, 'triggers', {'class':'vector'})
for trigger in triggers:
self._dispatch('trigger', 'triggers',
parser, trig_e, trigger)

View File

@ -0,0 +1,51 @@
# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# 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 wrappers
import xml.etree.ElementTree as XML
import jenkins_jobs.modules.base
def timeout(parser, xml_parent, data):
twrapper = XML.SubElement(xml_parent,
'hudson.plugins.build__timeout.BuildTimeoutWrapper')
tminutes = XML.SubElement(twrapper, 'timeoutMinutes')
tminutes.text = str(data['timeout'])
failbuild = XML.SubElement(twrapper, 'failBuild')
fail = data.get('fail', False)
if fail:
failbuild.text = 'true'
else:
failbuild.text = 'false'
def timestamps(parser, xml_parent, data):
XML.SubElement(xml_parent,
'hudson.plugins.timestamper.TimestamperBuildWrapper')
def ansicolor(parser, xml_parent, data):
XML.SubElement(xml_parent,
'hudson.plugins.ansicolor.AnsiColorBuildWrapper')
class Wrappers(jenkins_jobs.modules.base.Base):
sequence = 80
def gen_xml(self, parser, xml_parent, data):
wrappers = XML.SubElement(xml_parent, 'buildWrappers')
for wrap in data.get('wrappers', []):
self._dispatch('wrapper', 'wrappers',
parser, wrappers, wrap)

View File

@ -0,0 +1,85 @@
# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# 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 Zuul
import jenkins_jobs.modules.base
ZUUL_PARAMETERS = [
{'string':
{'description': 'Zuul provided key to link builds with Gerrit events',
'name': 'UUID'}},
{'string':
{'description': 'Zuul provided project name',
'name': 'GERRIT_PROJECT'}},
{'string':
{'description': 'Zuul provided branch name',
'name': 'GERRIT_BRANCH'}},
{'string':
{'description': 'Zuul provided list of dependent changes to merge',
'name': 'GERRIT_CHANGES'}},
]
ZUUL_POST_PARAMETERS = [
{'string':
{'description': 'Zuul provided key to link builds with Gerrit events',
'name': 'UUID'}},
{'string':
{'description': 'Zuul provided project name',
'name': 'GERRIT_PROJECT'}},
{'string':
{'description': 'Zuul provided ref name',
'name': 'GERRIT_REFNAME'}},
{'string':
{'description': 'Zuul provided old reference for ref-updated',
'name': 'GERRIT_OLDREV'}},
{'string':
{'description': 'Zuul provided new reference for ref-updated',
'name': 'GERRIT_NEWREV'}},
]
ZUUL_NOTIFICATIONS = [
{'http':
{'url': 'http://127.0.0.1:8001/jenkins_endpoint'}}
]
class Zuul(jenkins_jobs.modules.base.Base):
sequence = 0
def handle_data(self, parser):
changed = False
jobs = (parser.data.get('job', {}).values() +
parser.data.get('job-template', {}).values())
for job in jobs:
triggers = job.get('triggers')
if not triggers:
continue
if ('zuul' not in job.get('triggers', []) and
'zuul_post' not in job.get('triggers', [])):
continue
if 'parameters' not in job:
job['parameters'] = []
if 'notifications' not in job:
job['notifications'] = []
job['notifications'].extend(ZUUL_NOTIFICATIONS)
if 'zuul' in job.get('triggers', []):
job['parameters'].extend(ZUUL_PARAMETERS)
job['triggers'].remove('zuul')
if 'zuul_post' in job.get('triggers', []):
job['parameters'].extend(ZUUL_POST_PARAMETERS)
job['triggers'].remove('zuul_post')
changed = True
return changed

View File

@ -1,161 +0,0 @@
#! /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
def register(registry):
mod = Builders(registry)
registry.registerModule(mod)
class Builders(object):
sequence = 60
def __init__(self, registry):
self.registry = registry
for f in dir(self):
if not f.startswith('_builder_'):
continue
self.registry.registerHandler('builder', f[len('_builder_'):],
getattr(self, f))
def handle_data(self, data):
self.data = data
def gen_xml(self, xml_parent, data):
for alias in ['prebuilders', 'builders', 'postbuilders']:
if alias in data:
builders = XML.SubElement(xml_parent, alias)
for builder in data[alias]:
if isinstance(builder, dict):
for key, value in builder.items():
func = self.registry.getHandler('builder', key)
func(builders, value)
else:
func = self.registry.getHandler('builder', builder)
func(builders)
def _add_script(self, xml_parent, script):
shell = XML.SubElement(xml_parent, 'hudson.tasks.Shell')
XML.SubElement(shell, 'command').text = script
def _builder_coverage(self, xml_parent):
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/run-cover.sh')
def _builder_docs(self, xml_parent):
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/run-docs.sh')
def _builder_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']['review_site']))
def _builder_maven_test(self, xml_parent):
self._add_script(xml_parent, 'mvn test')
def _builder_maven_package(self, xml_parent):
self._add_script(xml_parent, 'mvn package')
def _builder_gerrit_package(self, xml_parent):
self._add_script(xml_parent,
'/usr/local/jenkins/slave_scripts/package-gerrit.sh')
def _builder_gerrit_preclean(self, xml_parent):
self._add_script(xml_parent, "#!/bin/bash -xe\n\
rm -fr ~/.m2\n\
rm -fr ~/.java\n\
./tools/version.sh --release")
def _builder_gerrit_postrun(self, xml_parent):
self._add_script(xml_parent, "./tools/version.sh --reset")
def _builder_pep8(self, xml_parent):
self._add_script(xml_parent, 'set -o pipefail ; tox -v -epep8 | tee pep8.txt ; set +o pipefail')
def _builder_pyflakes(self, xml_parent):
self._add_script(xml_parent, 'tox -v -epyflakes')
def _builder_puppet_syntax(self, xml_parent):
self._add_script(xml_parent, """
find . -iname *.pp | xargs puppet parser validate --modulepath=`pwd`/modules
for f in `find . -iname *.erb` ; do
erb -x -T '-' $f | ruby -c
done
""")
def _builder_selenium(self, xml_parent):
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/run-selenium.sh')
def _builder_shell(self, xml_parent, data):
self._add_script(xml_parent, data)
def _builder_trigger_builds(self, xml_parent, data):
tbuilder = XML.SubElement(xml_parent, 'hudson.plugins.parameterizedtrigger.TriggerBuilder')
configs = XML.SubElement(tbuilder, 'configs')
for project_def in data:
tconfig = XML.SubElement(configs, 'hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig')
tconfigs = XML.SubElement(tconfig, 'configs')
if project_def.has_key('predefined_parameters'):
params = XML.SubElement(tconfigs,
'hudson.plugins.parameterizedtrigger.PredefinedBuildParameters')
properties = XML.SubElement(params, 'properties')
properties.text = project_def['predefined_parameters']
else:
tconfigs.set('class', 'java.util.Collections$EmptyList')
projects = XML.SubElement(tconfig, 'projects')
projects.text = project_def['project']
condition = XML.SubElement(tconfig, 'condition')
condition.text = 'ALWAYS'
trigger_with_no_params = XML.SubElement(tconfig, 'triggerWithNoParameters')
trigger_with_no_params.text = 'false'
build_all_nodes_with_label = XML.SubElement(tconfig, 'buildAllNodesWithLabel')
build_all_nodes_with_label.text = 'false'
def _builder_python26(self, xml_parent):
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/run-tox.sh 26')
def _builder_python27(self, xml_parent):
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/run-tox.sh 27')
def _builder_python26_essex(self, xml_parent):
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/run-tox.sh 26-essex')
def _builder_python27_essex(self, xml_parent):
self._add_script(xml_parent, '/usr/local/jenkins/slave_scripts/run-tox.sh 27-essex')
def _builder_tarball(self, xml_parent):
self._add_script(xml_parent,
'/usr/local/jenkins/slave_scripts/create-tarball.sh %s' % self.data['main']['project'])
def _builder_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 = '%s-tarball' % self.data['main']['project']
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=&quot;no&quot;\n\
export PROJECT=&quot;<%= project %>&quot;\n\
export GERRIT_REFNAME=$BRANCH\n\
/usr/local/jenkins/slave_scripts/create-ppa-package.sh')

View File

@ -1,90 +0,0 @@
#! /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
def register(registry):
mod = Properties()
registry.registerModule(mod)
class Properties(object):
sequence = 20
def handle_data(self, data):
self.data = data
def gen_xml(self, xml_parent, data):
main = self.data['main']
properties = XML.SubElement(xml_parent, 'properties')
if main.get('project'):
github = XML.SubElement(properties, 'com.coravy.hudson.plugins.github.GithubProjectProperty')
github_url = XML.SubElement(github, 'projectUrl')
github_url.text = "https://github.com/{org}/{project}".format(
org=main['github_org'], 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'
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'
self.do_parameters(properties)
self.do_notifications(properties)
parameter_types = {
'string': 'hudson.model.StringParameterDefinition',
'bool': 'hudson.model.BooleanParameterDefinition',
'file': 'hudson.model.FileParameterDefinition',
'text': 'hudson.model.TextParameterDefinition',
'label': 'org.jvnet.jenkins.plugins.nodelabelparameter.LabelParameterDefinition',
# Others require more work
}
def do_parameters(self, xml_parent):
params = self.data.get('parameters', None)
if not params:
return
pdefp = XML.SubElement(xml_parent, 'hudson.model.ParametersDefinitionProperty')
pdefs = XML.SubElement(pdefp, 'parameterDefinitions')
for param in params:
ptype = self.parameter_types.get(param['type'])
pdef = XML.SubElement(pdefs, ptype)
XML.SubElement(pdef, 'name').text = param['name']
XML.SubElement(pdef, 'description').text = param['description']
if param['type'] != 'file':
default = param.get('default', None)
if default:
XML.SubElement(pdef, 'defaultValue').text = default
else:
XML.SubElement(pdef, 'defaultValue')
def do_notifications(self, xml_parent):
endpoints = self.data.get('notification_endpoints', None)
if not endpoints:
return
notify_element = XML.SubElement(xml_parent, 'com.tikal.hudson.plugins.notification.HudsonNotificationProperty')
endpoints_element = XML.SubElement(notify_element, 'endpoints')
for ep in endpoints:
endpoint_element = XML.SubElement(endpoints_element, 'com.tikal.hudson.plugins.notification.Endpoint')
XML.SubElement(endpoint_element, 'protocol').text = ep['protocol']
XML.SubElement(endpoint_element, 'url').text = ep['URL']

View File

@ -1,329 +0,0 @@
#! /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
def register(registry):
mod = Publishers(registry)
registry.registerModule(mod)
class Publishers(object):
sequence = 70
def __init__(self, registry):
self.registry = registry
for f in dir(self):
if not f.startswith('_publisher_'):
continue
self.registry.registerHandler('publisher', f[len('_publisher_'):],
getattr(self, f))
def handle_data(self, data):
self.data = data
def gen_xml(self, xml_parent, data):
publishers = XML.SubElement(xml_parent, 'publishers')
actions = self.data.get('post_build_actions', [])
for action in actions:
if isinstance(action, dict):
for key, value in action.items():
func = self.registry.getHandler('publisher', key)
func(publishers, value)
else:
func = self.registry.getHandler('publisher', action)
func(publishers)
def _publisher_archive(self, xml_parent, data):
archiver = XML.SubElement(xml_parent, 'hudson.tasks.ArtifactArchiver')
artifacts = XML.SubElement(archiver, 'artifacts')
artifacts.text = data['artifacts']
if 'excludes' in data:
excludes = XML.SubElement(archiver, 'excludes')
excludes.text = data['excludes']
latest = XML.SubElement(archiver, 'latestOnly')
latest_only = data.get('latest_only', False)
if latest_only:
latest.text = 'true'
else:
latest.text = 'false'
def _publisher_trigger_parameterized_builds(self, xml_parent, data):
tbuilder = XML.SubElement(xml_parent, 'hudson.plugins.parameterizedtrigger.BuildTrigger')
configs = XML.SubElement(tbuilder, 'configs')
for project_def in data:
tconfig = XML.SubElement(configs, 'hudson.plugins.parameterizedtrigger.BuildTriggerConfig')
tconfigs = XML.SubElement(tconfig, 'configs')
if project_def.has_key('predefined_parameters'):
params = XML.SubElement(tconfigs,
'hudson.plugins.parameterizedtrigger.PredefinedBuildParameters')
properties = XML.SubElement(params, 'properties')
properties.text = project_def['predefined_parameters']
else:
tconfigs.set('class', 'java.util.Collections$EmptyList')
projects = XML.SubElement(tconfig, 'projects')
projects.text = project_def['project']
condition = XML.SubElement(tconfig, 'condition')
condition.text = project_def.get('condition', 'ALWAYS')
trigger_with_no_params = XML.SubElement(tconfig, 'triggerWithNoParameters')
trigger_with_no_params.text = 'false'
def _publisher_coverage(self, xml_parent):
cobertura = XML.SubElement(xml_parent, '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'
# Jenkins Job module for publishing via ftp
# publish:
# site: 'docs.openstack.org'
# remote_dir: 'dest/dir'
# source_files: 'base/source/dir/**'
# remove_prefix: 'base/source/dir'
# excludes: '**/*.exludedfiletype'
#
# This will upload everything under $workspace/base/source/dir to
# docs.openstack.org $ftpdir/dest/dir exluding the excluded file type.
def _publisher_ftp(self, xml_parent, data):
"""
Example XML:
<publishers>
<jenkins.plugins.publish__over__ftp.BapFtpPublisherPlugin>
<consolePrefix>FTP: </consolePrefix>
<delegate>
<publishers>
<jenkins.plugins.publish__over__ftp.BapFtpPublisher>
<configName>docs.openstack.org</configName>
<verbose>true</verbose>
<transfers>
<jenkins.plugins.publish__over__ftp.BapFtpTransfer>
<remoteDirectory></remoteDirectory>
<sourceFiles>openstack-identity-api/target/docbkx/webhelp/api/openstack-identity-service/2.0/**</sourceFiles>
<excludes>**/*.xml,**/null*</excludes>
<removePrefix>openstack-identity-api/target/docbkx/webhelp</removePrefix>
<remoteDirectorySDF>false</remoteDirectorySDF>
<flatten>false</flatten>
<cleanRemote>false</cleanRemote>
<asciiMode>false</asciiMode>
</jenkins.plugins.publish__over__ftp.BapFtpTransfer>
</transfers>
<useWorkspaceInPromotion>false</useWorkspaceInPromotion>
<usePromotionTimestamp>false</usePromotionTimestamp>
</jenkins.plugins.publish__over__ftp.BapFtpPublisher>
</publishers>
<continueOnError>false</continueOnError>
<failOnError>false</failOnError>
<alwaysPublishFromMaster>false</alwaysPublishFromMaster>
<hostConfigurationAccess class="jenkins.plugins.publish_over_ftp.BapFtpPublisherPlugin" reference="../.."/>
</delegate>
</jenkins.plugins.publish__over__ftp.BapFtpPublisherPlugin>
</publishers>
"""
outer_ftp = XML.SubElement(xml_parent,
'jenkins.plugins.publish__over__ftp.BapFtpPublisherPlugin')
XML.SubElement(outer_ftp, 'consolePrefix').text = 'FTP: '
delegate = XML.SubElement(outer_ftp, 'delegate')
publishers = XML.SubElement(delegate, 'publishers')
ftp = XML.SubElement(publishers, 'jenkins.plugins.publish__over__ftp.BapFtpPublisher')
XML.SubElement(ftp, 'configName').text = data['site']
XML.SubElement(ftp, 'verbose').text = 'true'
transfers = XML.SubElement(ftp, 'transfers')
ftp_transfers = XML.SubElement(transfers, 'jenkins.plugins.publish__over__ftp.BapFtpTransfer')
XML.SubElement(ftp_transfers, 'remoteDirectory').text = data['remote_dir']
XML.SubElement(ftp_transfers, 'sourceFiles').text = data['source_files']
XML.SubElement(ftp_transfers, 'excludes').text = data['excludes']
XML.SubElement(ftp_transfers, 'removePrefix').text = data['remove_prefix']
XML.SubElement(ftp_transfers, 'remoteDirectorySDF').text = 'false'
XML.SubElement(ftp_transfers, 'flatten').text = 'false'
XML.SubElement(ftp_transfers, 'cleanRemote').text = 'false'
XML.SubElement(ftp_transfers, 'asciiMode').text = 'false'
XML.SubElement(ftp, 'useWorkspaceInPromotion').text = 'false'
XML.SubElement(ftp, 'usePromotionTimestamp').text = 'false'
XML.SubElement(delegate, 'continueOnError').text = 'false'
XML.SubElement(delegate, 'failOnError').text = 'false'
XML.SubElement(delegate, 'alwaysPublishFromMaster').text = 'false'
XML.SubElement(delegate, 'hostConfigurationAccess',
{'class': 'jenkins.plugins.publish_over_ftp.BapFtpPublisherPlugin',
'reference': '../..'})
# Jenkins Job module for coverage publishers
# To use you add the following into your YAML:
# publisher:
# results: 'nosetests.xml'
def _publisher_junit(self, xml_parent, data):
junitresult = XML.SubElement(xml_parent,
'hudson.tasks.junit.JUnitResultArchiver')
XML.SubElement(junitresult, 'testResults').text = data['results']
XML.SubElement(junitresult, 'keepLongStdio').text = "true"
XML.SubElement(junitresult, 'testDataPublishers')
# Jenkins Job module for pep8 publishers
# No additional YAML needed
def _pep8_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 _publisher_pep8(self, xml_parent):
violations = XML.SubElement(xml_parent, '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._pep8_add_entry(configs, 'checkstyle')
self._pep8_add_entry(configs, 'codenarc')
self._pep8_add_entry(configs, 'cpd')
self._pep8_add_entry(configs, 'cpplint')
self._pep8_add_entry(configs, 'csslint')
self._pep8_add_entry(configs, 'findbugs')
self._pep8_add_entry(configs, 'fxcop')
self._pep8_add_entry(configs, 'gendarme')
self._pep8_add_entry(configs, 'jcreport')
self._pep8_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._pep8_add_entry(configs, 'pmd')
self._pep8_add_entry(configs, 'pylint')
self._pep8_add_entry(configs, 'simian')
self._pep8_add_entry(configs, 'stylecop')
XML.SubElement(config, 'limit').text = '100'
XML.SubElement(config, 'sourcePathPattern')
XML.SubElement(config, 'fauxProjectPath')
XML.SubElement(config, 'encoding').text = 'default'
# Jenkins Job module for PPA publishers
# No additional YAML needed
def _publisher_ppa(self, xml_parent):
archiver = XML.SubElement(xml_parent, 'hudson.tasks.ArtifactArchiver')
XML.SubElement(archiver, 'artifacts').text = 'build/*.dsc,build/*.tar.gz,build/*.changes'
XML.SubElement(archiver, 'latestOnly').text = 'false'
# Jenkins Job module for tarball publishers
# To use you add the following into your YAML:
# publish:
# site: 'glance.openstack.org'
# dir: 'glance'
def _publisher_tarball(self, xml_parent, data):
site = data['site']
archiver = XML.SubElement(xml_parent, 'hudson.tasks.ArtifactArchiver')
XML.SubElement(archiver, 'artifacts').text = 'dist/*.tar.gz'
XML.SubElement(archiver, 'latestOnly').text = 'false'
scp = XML.SubElement(xml_parent, 'be.certipost.hudson.plugin.SCPRepositoryPublisher')
XML.SubElement(scp, 'siteName').text = site
entries = XML.SubElement(scp, 'entries')
entry = XML.SubElement(entries, 'be.certipost.hudson.plugin.Entry')
XML.SubElement(entry, 'filePath').text = 'tarballs/{proj}/'.format(proj=data['project'])
XML.SubElement(entry, 'sourceFile').text = 'dist/*.tar.gz'
XML.SubElement(entry, 'keepHierarchy').text = 'false'
# Jenkins Job module for war publishers
# To use you add the following into your YAML:
# publish:
# site: 'nova.openstack.org'
# warfile: 'gerrit-war/target/gerrit*.war'
# target_path: 'tarballs/ci/'
def _publisher_war(self, xml_parent, data):
site = data['site']
archiver = XML.SubElement(xml_parent, 'hudson.tasks.ArtifactArchiver')
XML.SubElement(archiver, 'artifacts').text = data['warfile']
XML.SubElement(archiver, 'latestOnly').text = 'false'
scp = XML.SubElement(xml_parent, 'be.certipost.hudson.plugin.SCPRepositoryPublisher')
XML.SubElement(scp, 'siteName').text = site
entries = XML.SubElement(scp, 'entries')
entry = XML.SubElement(entries, 'be.certipost.hudson.plugin.Entry')
XML.SubElement(entry, 'filePath').text = data['target_path']
XML.SubElement(entry, 'sourceFile').text = data['warfile']
XML.SubElement(entry, 'keepHierarchy').text = 'false'
# Jenkins Job module for generic scp publishing
# To use you add the following into your YAML:
# publish:
# site: 'openstack-ci.openstack.org'
# source: 'doc/build/html/**/*'
# target_path: 'ci/zuul'
# keep_heirarchy: 'true'
def _publisher_scp(self, xml_parent, data):
site = data['site']
scp = XML.SubElement(xml_parent, 'be.certipost.hudson.plugin.SCPRepositoryPublisher')
XML.SubElement(scp, 'siteName').text = site
entries = XML.SubElement(scp, 'entries')
entry = XML.SubElement(entries, 'be.certipost.hudson.plugin.Entry')
XML.SubElement(entry, 'filePath').text = data['target_path']
XML.SubElement(entry, 'sourceFile').text = data['source']
XML.SubElement(entry, 'keepHierarchy').text = data['keep_heirarchy']

View File

@ -1,74 +0,0 @@
#! /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
def register(registry):
mod = SCM()
registry.registerModule(mod)
class SCM(object):
sequence = 30
def handle_data(self, data):
self.data = data
def gen_xml(self, xml_parent, data):
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/{org}/{project}.git'.format(org=main['github_org'], project=main['project'])
xml_branches = XML.SubElement(scm, 'branches')
branches = self.data['scm'].get('branches', ['**'])
for branch in branches:
bspec = XML.SubElement(xml_branches, 'hudson.plugins.git.BranchSpec')
XML.SubElement(bspec, 'name').text = branch
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'})

View File

@ -1,129 +0,0 @@
#! /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
# overrideVotes: 'true'
# gerritBuildSuccessfulVerifiedValue: 1
# gerritBuildFailedVerifiedValue: -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: '**'
# ...
#
# triggerApprovalCategory and triggerApprovalValue only required if triggerOnCommentAddedEvent: 'true'
import xml.etree.ElementTree as XML
def register(registry):
mod = Triggers(registry)
registry.registerModule(mod)
class Triggers(object):
sequence = 50
def __init__(self, registry):
self.registry = registry
for f in dir(self):
if not f.startswith('_trigger_'):
continue
self.registry.registerHandler('trigger', f[len('_trigger_'):],
getattr(self, f))
def handle_data(self, data):
self.data = data
def gen_xml(self, xml_parent, data):
actions = self.data.get('triggers', [])
if not actions:
return
triggers = XML.SubElement(xml_parent, 'triggers', {'class':'vector'})
for action in actions:
if isinstance(action, dict):
for key, value in action.items():
func = self.registry.getHandler('trigger', key)
func(triggers, value)
else:
func = self.registry.getHandler('trigger', action)
func(triggers)
def _trigger_gerrit(self, xml_parent, data):
projects = data['projects']
gtrig = XML.SubElement(xml_parent, '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 = data['triggerOnPatchsetUploadedEvent']
XML.SubElement(gtrig, 'triggerOnChangeMergedEvent').text = data['triggerOnChangeMergedEvent']
XML.SubElement(gtrig, 'triggerOnCommentAddedEvent').text = data['triggerOnCommentAddedEvent']
XML.SubElement(gtrig, 'triggerOnRefUpdatedEvent').text = data['triggerOnRefUpdatedEvent']
if data.has_key('overrideVotes') and data['overrideVotes'] == 'true':
XML.SubElement(gtrig, 'gerritBuildSuccessfulVerifiedValue').text = str(data['gerritBuildSuccessfulVerifiedValue'])
XML.SubElement(gtrig, 'gerritBuildFailedVerifiedValue').text = str(data['gerritBuildFailedVerifiedValue'])
if data['triggerOnCommentAddedEvent'] == 'true':
XML.SubElement(gtrig, 'commentAddedTriggerApprovalCategory').text = data['triggerApprovalCategory']
XML.SubElement(gtrig, 'commentAddedTriggerApprovalValue').text = str(data['triggerApprovalValue'])
XML.SubElement(gtrig, 'buildStartMessage')
XML.SubElement(gtrig, 'buildFailureMessage').text = data['failureMessage']
XML.SubElement(gtrig, 'buildSuccessfulMessage')
XML.SubElement(gtrig, 'buildUnstableMessage')
XML.SubElement(gtrig, 'customUrl')
# Jenkins Job module for scm polling triggers
# To use add the following into your YAML:
# trigger:
# pollscm: '@midnight'
# or
# pollscm: '*/15 * * * *'
def _trigger_pollscm(self, xml_parent, data):
scmtrig = XML.SubElement(xml_parent, 'hudson.triggers.SCMTrigger')
XML.SubElement(scmtrig, 'spec').text = data
# Jenkins Job module for timed triggers
# To use add the following into your YAML:
# trigger:
# timed: '@midnight'
# or
# timed: '*/15 * * * *'
def _trigger_timed(self, xml_parent, data):
scmtrig = XML.SubElement(xml_parent, 'hudson.triggers.TimerTrigger')
XML.SubElement(scmtrig, 'spec').text = data

View File

@ -1,53 +0,0 @@
#! /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 wrappers
import xml.etree.ElementTree as XML
def register(registry):
mod = Wrappers()
registry.registerModule(mod)
class Wrappers(object):
sequence = 80
def gen_xml(self, xml_parent, data):
wrappers = XML.SubElement(xml_parent, 'buildWrappers')
if 'timeout' in data['main']:
self._timeout(wrappers, data)
if 'ansicolor' in data['main']:
self._ansicolor(wrappers, data)
if 'timestamps' in data['main']:
self._timestamps(wrappers, data)
def _timeout(self, xml_parent, data):
twrapper = XML.SubElement(xml_parent, 'hudson.plugins.build__timeout.BuildTimeoutWrapper')
tminutes = XML.SubElement(twrapper, 'timeoutMinutes')
tminutes.text = str(data['main']['timeout'])
failbuild = XML.SubElement(twrapper, 'failBuild')
fail = data['main'].get('timeout_fail', False)
if fail:
failbuild.text = 'true'
else:
failbuild.text = 'false'
def _timestamps(self, xml_parent, data):
XML.SubElement(xml_parent, 'hudson.plugins.timestamper.TimestamperBuildWrapper')
def _ansicolor(self, xml_parent, data):
XML.SubElement(xml_parent, 'hudson.plugins.ansicolor.AnsiColorBuildWrapper')

View File

@ -1,79 +0,0 @@
#! /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 Zuul
ZUUL_PARAMETERS = [
{'description': 'Zuul provided key to link builds with Gerrit events',
'name': 'UUID',
'type': 'string'},
{'description': 'Zuul provided project name',
'name': 'GERRIT_PROJECT',
'type': 'string'},
{'description': 'Zuul provided branch name',
'name': 'GERRIT_BRANCH',
'type': 'string'},
{'description': 'Zuul provided list of dependent changes to merge',
'name': 'GERRIT_CHANGES',
'type': 'string'}
]
ZUUL_POST_PARAMETERS = [
{'description': 'Zuul provided key to link builds with Gerrit events',
'name': 'UUID',
'type': 'string'},
{'description': 'Zuul provided project name',
'name': 'GERRIT_PROJECT',
'type': 'string'},
{'description': 'Zuul provided ref name',
'name': 'GERRIT_REFNAME',
'type': 'string'},
{'description': 'Zuul provided old reference for ref-updated',
'name': 'GERRIT_OLDREV',
'type': 'string'},
{'description': 'Zuul provided new reference for ref-updated',
'name': 'GERRIT_NEWREV',
'type': 'string'}
]
ZUUL_NOTIFICATIONS = [
{'URL': 'http://127.0.0.1:8001/jenkins_endpoint',
'protocol': 'HTTP'}
]
def register(registry):
mod = Zuul()
registry.registerModule(mod)
class Zuul(object):
sequence = 0
def handle_data(self, data):
if ('zuul' not in data.get('triggers', []) and
'zuul_post' not in data.get('triggers', [])):
return
if 'parameters' not in data:
data['parameters'] = []
if 'notification_endpoints' not in data:
data['notification_endpoints'] = []
data['notification_endpoints'].extend(ZUUL_NOTIFICATIONS)
if 'zuul' in data.get('triggers', []):
data['parameters'].extend(ZUUL_PARAMETERS)
data['triggers'].remove('zuul')
if 'zuul_post' in data.get('triggers', []):
data['parameters'].extend(ZUUL_POST_PARAMETERS)
data['triggers'].remove('zuul_post')

View File

@ -1,71 +1,62 @@
project:
template: 'python_jobs'
#project:
# template: 'python_jobs'
#
#values:
# name: 'zuul'
# disabled: 'false'
# github_org: 'openstack-ci'
# review_site: 'review.openstack.org'
# node: 'precise'
#
#---
values:
name: 'zuul'
disabled: 'false'
github_org: 'openstack-ci'
review_site: 'review.openstack.org'
node: 'precise'
- job:
name: gate-zuul-pyflakes
project-type: freestyle
concurrent: true
timeout: 20
timeout_fail: true
---
# pyflakes-gate
main:
name: 'gate-zuul-pyflakes'
review_site: 'review.openstack.org'
github_org: 'openstack-ci'
project: 'zuul'
concurrent: 'true'
timeout: 20
timeout_fail: true
triggers:
- zuul
triggers:
- zuul
builders:
- gerrit_git_prep
- pyflakes
builders:
- gerrit_git_prep
- pyflakes
assignednode:
node: 'precise'
scm:
scm: 'false'
- job:
name: zuul-docs
project-type: freestyle
#review_site: 'review.openstack.org'
#github_org: 'openstack-ci'
#project: 'zuul'
#authenticatedBuild: 'false'
concurrent: true
timeout: 20
timeout_fail: true
assignednode:
node: 'precise'
triggers:
- zuul_post
---
# zuul-docs
main:
name: 'zuul-docs'
review_site: 'review.openstack.org'
github_org: 'openstack-ci'
project: 'zuul'
authenticatedBuild: 'false'
concurrent: 'true'
timeout: 20
timeout_fail: true
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
triggers:
- zuul_post
builders:
- gerrit_git_prep
- docs
logrotate:
daysToKeep: 28
numToKeep: -1
artifactDaysToKeep: -1
artifactNumToKeep: -1
post_build_actions:
- scp:
site: '173.203.107.207'
source: 'doc/build/html/**/*'
target_path: 'ci/zuul'
keep_heirarchy: 'true'
builders:
- gerrit_git_prep
- docs
post_build_actions:
- scp:
site: '173.203.107.207'
source: 'doc/build/html/**/*'
target_path: 'ci/zuul'
keep_heirarchy: 'true'
scm:
scm: 'false'
assignednode:
node: 'precise'
assignednode:
node: 'precise'

90
setup.py Normal file
View File

@ -0,0 +1,90 @@
# Copyright 2012 Hewlett-Packard Development Company, L.P.
#
# 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.
from setuptools import find_packages
from setuptools import setup
setup(name='jenkins_job_builder',
version='0.1',
description="Manage Jenkins jobs with YAML",
license='Apache License (2.0)',
author='Hewlett-Packard Development Company, L.P.',
author_email='openstack@lists.launchpad.net',
scripts=['jenkins-jobs'],
include_package_data=True,
zip_safe=False,
packages=find_packages(),
entry_points = {
'jenkins_jobs.projects': [
'freestyle=jenkins_jobs.modules.project_freestyle:Freestyle',
'maven=jenkins_jobs.modules.project_maven:Maven',
],
'jenkins_jobs.builders': [
'shell=jenkins_jobs.modules.builders:shell',
'trigger-builds=jenkins_jobs.modules.builders:trigger_builds',
],
'jenkins_jobs.properties': [
'github=jenkins_jobs.modules.properties:github',
'throttle=jenkins_jobs.modules.properties:throttle',
'authenticated-build=jenkins_jobs.modules.properties:'
'authenticated_build',
],
'jenkins_jobs.parameters': [
'string=jenkins_jobs.modules.properties:string_param',
'bool=jenkins_jobs.modules.properties:bool_param',
'file=jenkins_jobs.modules.properties:file_param',
'text=jenkins_jobs.modules.properties:text_param',
'label=jenkins_jobs.modules.properties:label_param',
],
'jenkins_jobs.notifications': [
'http=jenkins_jobs.modules.properties:http_endpoint',
],
'jenkins_jobs.publishers': [
'archive=jenkins_jobs.modules.publishers:archive',
'trigger-parameterized-builds='
'jenkins_jobs.modules.publishers:trigger_parameterized_builds',
'coverage=jenkins_jobs.modules.publishers:coverage',
'ftp=jenkins_jobs.modules.publishers:ftp',
'junit=jenkins_jobs.modules.publishers:junit',
'pep8=jenkins_jobs.modules.publishers:pep8',
'scp=jenkins_jobs.modules.publishers:scp',
],
'jenkins_jobs.scm': [
'git=jenkins_jobs.modules.scm:git',
],
'jenkins_jobs.triggers': [
'gerrit=jenkins_jobs.modules.triggers:gerrit',
'pollscm=jenkins_jobs.modules.triggers:pollscm',
'timed=jenkins_jobs.modules.triggers:timed',
],
'jenkins_jobs.wrappers': [
'timeout=jenkins_jobs.modules.wrappers:timeout',
'timestamps=jenkins_jobs.modules.wrappers:timestamps',
'ansicolor=jenkins_jobs.modules.wrappers:ansicolor',
],
'jenkins_jobs.modules': [
'assignednode=jenkins_jobs.modules.assignednode:AssignedNode',
'builders=jenkins_jobs.modules.builders:Builders',
'logrotate=jenkins_jobs.modules.logrotate:LogRotate',
'properties=jenkins_jobs.modules.properties:Properties',
'publishers=jenkins_jobs.modules.publishers:Publishers',
'scm=jenkins_jobs.modules.scm:SCM',
'triggers=jenkins_jobs.modules.triggers:Triggers',
'wrappers=jenkins_jobs.modules.wrappers:Wrappers',
'zuul=jenkins_jobs.modules.zuul:Zuul',
]
}
)

23
test.sh
View File

@ -9,18 +9,19 @@ mkdir -p /tmp/jenkins_jobs_test/test
if [ "$1" == "save" ]
then
for x in `find projects/ -name *.yml`
do
echo $x
BASENAME=`basename $x`
python jenkins_jobs.py test $x > /tmp/jenkins_jobs_test/saved/$BASENAME.xml
done
rm -f /tmp/jenkins_jobs_test/saved/*
jenkins-jobs test -o /tmp/jenkins_jobs_test/saved/ example
else
for x in `find projects/ -name *.yml`
rm -f /tmp/jenkins_jobs_test/test/*
jenkins-jobs test -o /tmp/jenkins_jobs_test/test/ example
for x in `(cd /tmp/jenkins_jobs_test/saved && find -type f)`
do
echo $x
BASENAME=`basename $x`
python jenkins_jobs.py test $x > /tmp/jenkins_jobs_test/test/$BASENAME.xml
if ! diff -u /tmp/jenkins_jobs_test/saved/$x /tmp/jenkins_jobs_test/test/$x >/dev/null 2>&1
then
echo "============================================================"
echo $x
echo "------------------------------------------------------------"
fi
diff -u /tmp/jenkins_jobs_test/saved/$x /tmp/jenkins_jobs_test/test/$x
done
diff -r /tmp/jenkins_jobs_test/saved /tmp/jenkins_jobs_test/test
fi