2
"""Create/Update jenkins jobs for a given stack
4
- Reads stack configuration from YAML configuration file
5
- Create/updates the jenkins jobs on the server configured in the credentials
9
# Copyright (C) 2012, Canonical Ltd (http://www.canonical.com/)
11
# Author: Jean-Baptiste Lallement <jean-baptiste.lallement@canonical.com>
13
# This software is free software: you can redistribute it
14
# and/or modify it under the terms of the GNU General Public License
15
# as published by the Free Software Foundation, either version 3 of
16
# the License, or (at your option) any later version.
18
# This software is distributed in the hope that it will
19
# be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
20
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
# GNU General Public License for more details.
23
# You should have received a copy of the GNU General Public License
24
# along with this software. If not, see <http://www.gnu.org/licenses/>.
35
sys.path.append('..') # add local cupstream2distro
36
from cupstream2distro import launchpadmanager
38
BINDIR = os.path.dirname(__file__)
41
'master': 'master-config.xml.tmpl',
42
'waitonstacks': 'waitonstacks-stack-config.xml.tmpl',
43
'prepare-master': 'prepare-config.xml.tmpl',
44
'prepare-project': 'prepare-project-config.xml.tmpl',
45
'build': 'build-stack-config.xml.tmpl',
46
'check': 'check-stack-config.xml.tmpl',
47
'publish': 'publish-stack-config.xml.tmpl'
49
DEFAULT_CREDENTIALS = os.path.expanduser('~/.cu2d.cred')
52
def load_jenkins_credentials(path):
53
""" Load Credentials from credentials configuration file """
54
if not os.path.exists(path):
57
logging.debug('Loading credentials from %s', path)
58
cred = yaml.load(file(path, 'r'))
59
return False if not 'jenkins' in cred else cred['jenkins']
62
def load_stack_cfg(path):
63
""" Load stack configuration from file
65
TODO: Verify that mandatory settings are defined
68
if not os.path.exists(path):
71
logging.debug('Loading stack configuration from %s', path)
72
cfg = yaml.load(file(path, 'r'))
73
return False if not 'stack' in cfg else cfg['stack']
76
def setup_branch(stack):
77
"""Configure the branch so that lp-propose target the wanted one
79
:param stack: dictionary with configuration of the stack
83
lp = launchpadmanager.get_launchpad(use_cred_file=None)
84
for prj in stack['projects']:
86
srcname = prj.keys()[0]
89
lpb = "lp:{}".format(prj)
91
current_branch = lp.branches.getByUrl(url=lpb)
92
if not current_branch:
93
logging.error("No branch exist on launchpad for {}".format(lpb))
96
# set the bzr config so that targeted branch is the right one when using the short name
97
logging.info("Set branch {} as target for {}".format(current_branch.unique_name, lpb))
98
cmd = ["bzr", "config", "-d", "lp:{}".format(current_branch.unique_name), "public_branch={}".format(lpb)]
99
if subprocess.call(cmd) != 0:
100
logging.error("Can't set the target branch {} for {}".format(current_branch.unique_name, lpb))
104
def setup_job(jkh, jjenv, jobname, tmplname, ctx, update=False):
105
""" Generate template and create or update jenkins job
107
:param jkh: jenkins handle
108
:param jjenv: handle to jinja environment
109
:param jobname: jenkins' job name
110
:param tmplname: template name
111
:param ctx: jinja context (dict) to merge with the template
112
:param update: update existing job if True
114
logging.debug('Generating job: %s', jobname)
115
tmpl = jjenv.get_template(tmplname)
116
jkcfg = tmpl.render(ctx)
117
if not jkh.job_exists(jobname):
118
logging.info("Creating Jenkins Job %s ", jobname)
119
jkh.create_job(jobname, jkcfg)
122
logging.info("Reconfiguring Jenkins Job %s ", jobname)
123
jkh.reconfig_job(jobname, jkcfg)
125
logging.debug('update set to %s. Skipping reconfiguration of '
126
'%s', update, jobname)
130
def update_jenkins(jkcfg, stack, update=False):
131
""" Add/update jenkins jobs
133
:param jkcfg: dictionary with the credentials
134
:param stack: dictionary with configuration of the stack
135
:param update: Update existing jobs if true
137
:return: True on success
139
if not 'tmpldir' in stack:
140
tmpldir = os.path.join(BINDIR, 'templates')
142
tmpldir = stack['tmpldir']
144
tmpldir = os.path.abspath(tmpldir)
145
logging.debug('Templates directory: %s', tmpldir)
147
if not os.path.exists(tmpldir):
148
logging.error('Template directory doesn\'t exist')
152
logging.error("Please provide a URL to the jenkins instance.")
155
jjenv = jinja2.Environment(loader=jinja2.FileSystemLoader(tmpldir))
157
if 'username' in jkcfg:
158
jkh = jenkins.Jenkins(jkcfg['url'],
159
username=jkcfg['username'],
160
password=jkcfg['password'])
162
jkh = jenkins.Jenkins(jkcfg['url'])
164
basename = [PREFIX, stack['name'], stack['release']]
166
'stack': stack['name'],
167
'release': stack['release']
171
# wait that other stack are published if there are some stack dependencies
172
if 'dependencies' in stack:
173
jobname = "-".join(basename + ['0waitonstacks'])
174
subjobs['jobwaitonstacks'] = jobname
177
setup_job(jkh, jjenv, jobname, TEMPLATES['waitonstacks'],
183
if stack['projects']:
184
for prj in stack['projects']:
185
if type(prj) == dict:
186
srcname = prj.keys()[0]
191
jobname = "-".join(basename + ['1.1prepare', srcname])
192
projects.append(jobname)
194
lpb = "lp:%s" % srcname
196
ctx['opts'] = '-s %s -p %s -b %s -n %s' % (
197
stack['series'], stack['ppa'], lpb, srcname)
198
ctx['projectname'] = srcname
199
setup_job(jkh, jjenv, jobname, TEMPLATES['prepare-project'],
203
jobname = "-".join(basename + ['1.0prepare'])
204
subjobs['jobprepare'] = jobname
206
ctx['projects'] = ','.join(projects)
207
setup_job(jkh, jjenv, jobname, TEMPLATES['prepare-master'],
211
jobname = "-".join(basename + ['2.1build'])
212
subjobs['jobbuild'] = jobname
214
ctx['opts'] = '-s %s -p %s' % (stack['series'], stack['ppa'])
215
setup_job(jkh, jjenv, jobname, TEMPLATES['build'],
219
if 'extracheck' in stack and stack['extracheck']:
220
jobname = "-".join(basename + ['2.2check'])
221
subjobs['jobbuild'] += ',' + jobname
223
ctx['opts'] = '-a -s %s -p %s' % (stack['series'], stack['ppa'])
224
ctx['extracheck'] = stack['extracheck']
225
setup_job(jkh, jjenv, jobname, TEMPLATES['check'],
229
jobname = "-".join(basename + ['3.0publish'])
230
subjobs['jobpublish'] = jobname
232
ctx['opts'] = '-s %s -p %s -j cu2d-%s-%s' % (stack['series'], stack['ppa'],
233
stack['name'], stack['release'])
234
setup_job(jkh, jjenv, jobname, TEMPLATES['publish'],
238
# Process at the end because it must know information about subjobs
239
jobname = "-".join(basename)
242
if 'schedule' in stack:
243
ctx['schedule'] = stack['schedule']
244
if 'dependencies' in stack:
245
ctx['dependencies'] = True
246
setup_job(jkh, jjenv, jobname, TEMPLATES['master'],
252
def set_logging(debugmode=False):
253
"""Initialize logging"""
255
level=logging.DEBUG if debugmode else logging.INFO,
256
format="%(asctime)s %(levelname)s %(message)s"
258
logging.debug('Debug mode enabled')
263
parser = argparse.ArgumentParser(
264
description='Create/Update the configuration of the Jenkins jobs '
267
To update the indicator stack run the following command:
268
$ ./cu2d-update-stack -dU ./etc/indicators-head.cfg
270
formatter_class=argparse.RawTextHelpFormatter)
271
parser.add_argument('-C', '--credentials', metavar='CREDENTIALFILE',
272
default=DEFAULT_CREDENTIALS,
273
help='use Jenkins and load credentials from '
274
'CREDENTIAL FILE\n(default: %s)' % DEFAULT_CREDENTIALS)
275
parser.add_argument('-U', '--update-jobs', action='store_true',
277
help='by default only new jobs are added. This '
278
'option enables \nupdate of existing jobs from '
279
'configuration template.')
280
parser.add_argument('-S', '--no-setupbranch', action='store_true',
282
help='Skip branch setup (useful if you do not have '
283
'privileges to configure branchs but need to update '
284
'job configuration. In this case, only update of '
285
'existing jobs is allowed')
286
parser.add_argument('-d', '--debug', action='store_true', default=False,
287
help='enable debug mode')
288
parser.add_argument('stackcfg', help='Path to a configuration file for '
291
args = parser.parse_args()
292
set_logging(args.debug)
294
stackcfg = load_stack_cfg(args.stackcfg)
296
logging.error('Stack configuration failed to load. Aborting!')
298
if not args.no_setupbranch:
299
setup_branch(stackcfg)
303
credentialsfile = args.credentials
304
credentials = load_jenkins_credentials(
305
os.path.expanduser(credentialsfile))
307
logging.error('Credentials not found. Aborting!')
309
if not update_jenkins(credentials, stackcfg, args.update_jobs):
310
logging.error('Failed to configure jenkins jobs. Aborting!')
313
if __name__ == "__main__":