• Home
  • Testimonials
  • Blog
  • Contact Us

At a Glance of a Key

Crafting Dreams into Ventures, Code into Excellence: Your Journey to Success

  • Home
  • Testimonials
  • Blog
  • Contact Us

Getting the right Jenkins build number using Python

2017-06-27 Tips & Tricks No Comments 3 minute read

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:

1
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#! /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:

1
./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):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+ ./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

Oh hi there 👋
It’s nice to meet you.

Sign up to receive a notification when new posts are published!

We don’t spam!

Check your inbox or spam folder to confirm your subscription.

DevOpsJenkinsPython

Proper use of static keyword

Serverless On-call duty notifier - Part 1

Leave a Reply Cancel reply

About Me

Principal Software Engineer and an industry leader with startup and FAANG experience. I specialize in distributed systems, storage, data protection services and payment processors.

Beyond technical expertise, I am passionate about supporting fellow engineers in their careers. Through approachable blogs and hands-on guidance, I help navigate the ever-evolving landscape of technology, empowering individuals to thrive in their professional journeys.

Open LinkedIn

Recent Posts

  • Building a Delayed Message System with Redis and FastAPI
  • Go Concurrency, Practical Example
  • Using GORM – Part 3: Models and Idempotency
  • Using GORM – Part 2: Transactions and Save Points
  • Using GORM – Part 1: Introduction

Archives

  • January 2025
  • December 2024
  • March 2023
  • February 2023
  • September 2022
  • July 2022
  • July 2021
  • June 2021
  • February 2021
  • April 2018
  • March 2018
  • January 2018
  • July 2017
  • June 2017
  • May 2017

Categories

  • AWS
  • Career Growth
  • Cyber Security
  • Debugging
  • Development
  • Storage
  • Tips & Tricks

Tags

API AWS Azure Bash Brainfuck C++ Challenge Cloud Cloud Bursting Concurrency Database DevOps Disassembly DLL Documentation DynamoDB Go Golang Guice Java Jenkins Mossad NoSQL OOP Performance Programming Python Redis Security Serverless Singleton Streams Testing Unit Tests WebService

All Rights Reserved 2025 © Sirotin Enterprises Inc.
Proudly powered by WordPress | Theme: Doo by ThemeVS.