Getting the right Jenkins build number using Python

One of the jobs in our CI pipeline is responsible for compiling, building and packing the code. The artifacts of the job is a directory on our storage with the build number and all the artifacts that are related to this build number.

For example: //storage/build_1000, //storage/build_1001 and etc.

There are other jobs that are triggered by a scheduler that takes the artifacts of the latest job and runs some tests on it.

The build job can run on multiple git branches so we wanted to create a mechanism that will allow the “testing” job take the latest build of a specific branch and not the latest job that was successful.

Unfortunately, I didn’t find a way in Jenkins to do that so I used some python code. Jenkins has a nice RESTful API that is wrapped in an easy to use python library:

pip install python-jenkins

For convenience, each one of the build jobs has the branch name as part of the display name, e.g. “#1000 (origin/master)”

The script looks like this:

#! /usr/bin/python

import re
import sys
import jenkins
import logging
from optparse import OptionParser

logger = logging.getLogger()
hdlr = logging.StreamHandler(sys.stderr)
formatter = logging.Formatter('%(asctime)s %(levelname)s\t%(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.DEBUG)

def connect(server, port, username, password):
	logger.info("Connecting to Jenkins at %s:%d (user: %s)" % (server, port, username))
	try:
		return jenkins.Jenkins("http://%s:%d" % (server, port), username=username, password=password)

	except Exception as e:
		logger.error("Connection failed: %s" % e.message)
		return None

def check_build(conn, number):
	logger.info("Checking build number %d" % number)
	info = conn.get_build_info(name=options.job, number=number)

	if info["building"]:
		logger.info("Build %d is still in progress - skipping" % number)
		return False

	if info["result"] != "SUCCESS":
		logger.info("Build %d result is %s - skipping" % (number, info["result"]))
		return False

	branch = re.findall("\((.*)\)", info["displayName"])[0]
	if branch != options.branch:
		logger.info("Build %d is on branch %s - skipping" % (number, branch))
		return False

	logger.info("Found build %d" % number)
	print("%d" % number)
	return True

if __name__ == "__main__":
	parser = OptionParser()
	parser.add_option("", "--server", dest="server", default="10.0.0.3", help="Jenkins server ip address")
	parser.add_option("", "--port", dest="port", default=8080, type="int", help="Jenkins server port")
	parser.add_option("", "--username", dest="username", default="automation", help="Jenkins user name")
	parser.add_option("", "--password", dest="password", default="password", help="Jenkins password")
	parser.add_option("-j", "--job", dest="job", default="", type="string", help="Jenkins job name")
	parser.add_option("-b", "--branch", dest="branch", default="", type="string", help="Git branch for the job")
	parser.add_option("-n", "--number", dest="number", type="int", help="Build number as a hint")
	(options, args) = parser.parse_args()

	conn = connect(options.server, options.port, options.username, options.password)
	if conn == None:
		sys.exit(2)

	if options.number:
		logger.info("Checking given build number %d" % options.number)
		found = check_build(conn, options.number)
		if found:
			sys.exit(0)
		else:
			logger.warn("The given build is not in the right branch - not using it.")

	logger.info("Looking for last successful job %s on branch %s" % (options.job, options.branch))

	logger.info("Getting builds for job %s" % options.job)
	job = conn.get_job_info(options.job)
	builds = job["builds"]
	for b in builds:
		found = check_build(conn, b["number"])
		if found:
			sys.exit(0)

	logger.error("Did not found any suitable build.")
	sys.exit(1)

The execution is simple as well:

./get_latest_successful_build.py --job "Builder" --branch "origin/master"

We can give the latest successful build as an hint and the script will check it first but it’s an optimization and not mandatory.

Run example from our CI lab (censored a bit):

+ ./tools/get_latest_successful_build.py -j Builder -b origin/master -n 14151
2017-06-27 12:30:10,417 INFO	Connecting to Jenkins at 10.0.0.3:8080 (user: automation)
2017-06-27 12:30:10,417 INFO	Checking given build number 14151
2017-06-27 12:30:10,418 INFO	Checking build number 14151
2017-06-27 12:30:10,687 INFO	Build 14151 is on branch origin/feature1 - skipping
2017-06-27 12:30:10,688 WARNING	The given build is not in the right branch - not using it.
2017-06-27 12:30:10,688 INFO	Looking for last successful job Builder on branch origin/master
2017-06-27 12:30:10,688 INFO	Getting builds for job Builder
2017-06-27 12:30:10,822 INFO	Checking build number 14154
2017-06-27 12:30:10,978 INFO	Build 14154 is still in progress - skipping
2017-06-27 12:30:10,978 INFO	Checking build number 14153
2017-06-27 12:30:11,132 INFO	Build 14153 is still in progress - skipping
2017-06-27 12:30:11,132 INFO	Checking build number 14152
2017-06-27 12:30:11,335 INFO	Found build 14152

Feel free using and modifying this script.
It can be downloaded from my github repository.

Alexander.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s