'Wait until a Jenkins build is complete

I am using Python 2.7 and Jenkins.

I am writing some code in Python that will perform a checkin and wait/poll for Jenkins job to be complete. I would like some thoughts on around how I achieve it.

  1. Python function to create a check-in in Perforce-> This can be easily done as P4 has CLI
  2. Python code to detect when a build got triggered -> I have the changelist and the job number. How do I poll the Jenkins API for the build log to check if it has the appropriate changelists? The output of this step is a build url which is carrying out the job
  3. How do I wait till the Jenkins job is complete?

Can I use snippets from the Jenkins Rest API or from Python Jenkins module?



Solution 1:[1]

If you need to know if the job is finished, the buildNumber and buildTimestamp are not enough.

This is the gist of how I find out if a job is complete, I have it in ruby but not python so perhaps someone could update this into real code.

lastBuild = get jenkins/job/myJob/lastBuild/buildNumber
get jenkins/job/myJob/lastBuild/build?token=gogogo

currentBuild = get jenkins/job/myJob/lastBuild/buildNumber
while currentBuild  == lastBuild 
  sleep 1 

thisBuild = get jenkins/job/myJob/lastBuild/buildNumber
buildInfo = get jenkins/job/myJob/[thisBuild]/api/xml?depth=0

while buildInfo["freeStyleBuild/building"] == true
  buildInfo = get jenkins/job/myJob/[thisBuild]/api/xml?depth=0
  sleep 1

ie. I found I needed to A) wait until the build starts (new build number) and B) wait until the building finishes (building is false).

Solution 2:[2]

Simple solution using invoke and block_until_complete methods (tested with Python 3.7)

import jenkinsapi
from jenkinsapi.jenkins import Jenkins

...

server = Jenkins(jenkinsUrl, username=jenkinsUser,
                 password=jenkinsToken, ssl_verify=sslVerifyFlag)

job = server.create_job(jobName, None)
queue = job.invoke()
queue.block_until_complete()

Inpsired by a test method in pycontribs

Solution 3:[3]

This snippet starts build job and wait until job is done.

It is easy to start the job but we need some kind of logic to know when job is done. First we need to wait for job ID to be applied and than we can query job for details:

from jenkinsapi import jenkins

server = jenkins.Jenkins(jenkinsurl, username=username, password='******')
job = server.get_job(j_name)
prev_id = job.get_last_buildnumber()
server.build_job(j_name)
while True:
    print('Waiting for build to start...')
    if prev_id != job.get_last_buildnumber():
        break
    time.sleep(3)
print('Running...')
last_build = job.get_last_build()
while last_build.is_running():
    time.sleep(1)
print(str(last_build.get_status()))

Solution 4:[4]

Don't know if this was available at the time of the question, but jenkinsapi module's Job.invoke() and/or Jenkins.build_job() return a QueueItem object, which can block_until_building(), or block_until_complete()

jobq = server.build_job(job_name, job_params)
jobq.block_until_building()
print("Job %s (%s) is building." % (jobq.get_job_name(), jobq.get_build_number()))
jobq.block_until_complete(5) # check every 5s instead of the default 15
print("Job complete, %s" % jobq.get_build().get_status())

Solution 5:[5]

Was going through the same problem and this worked for me, using python3 and python-jenkins.

while "".join([d['color'] for d in j.get_jobs() if d['name'] == "job_name"]) == 'blue_anime':
        print('Job is Running')
        time.sleep(1)

print('Job Over!!')

Working Github Script: Link

Solution 6:[6]

This is working for me

#!/usr/bin/env python

import jenkins
import time

server = jenkins.Jenkins('https://jenkinsurl/', username='xxxxx', password='xxxxxx')

j_name = 'test'
server.build_job(j_name, {'testparam1': 'test', 'testparam2': 'test'})
while True:
    print('Running....')
    if server.get_job_info(j_name)['lastCompletedBuild']['number'] == server.get_job_info(j_name)['lastBuild']['number']:
        print "Last ID %s, Current ID %s"  % (server.get_job_info(j_name)['lastCompletedBuild']['number'], server.get_job_info(j_name)['lastBuild']['number'])
        break
time.sleep(3)
print('Stop....')

console_output = server.get_build_console_output(j_name, server.get_job_info(j_name)['lastBuild']['number'])
print console_output

Solution 7:[7]

the issue main issue that the build_job doesn't return the number of the job, returns the number of a queue item (that only last 5 min). so the trick is

  1. build_job
  2. get the queue number,
  3. with the queue number get the job_number
  4. now we know the name of the job and the job number
  5. get_job_info and loop the jobs till we find one with our job number
  6. check the status

so i made a function for it with time_out

import time
from datetime import datetime, timedelta
import jenkins

def launch_job(jenkins_connection, job_name, parameters={}, wait=False, interval=30,  time_out=7200):
    """
    Create a jenkins job and waits for the job to finish
    :param jenkins_connection: jenkins server jenkins object
    :param job_name: the name of job we want to create and see if finish string
    :param parameters: the parameters of the job to build directory
    :param wait: if we want to wait for the job to finish or not bool
    :param interval: how often we want to monitor seconds int
    :param time_out: break the loop after certain X seconds int
    :return: build job number int
    """
    # we lunch the job and returns a queue_id
    job_id = jenkins_connection.build_job(job_name, parameters)
    # from the queue_id we get the job number that was created
    queue_job = jenkins_connection.get_queue_item(job_id, depth=0)
    build_number = queue_job["executable"]["number"]
    print(f"job_name: {job_name} build_number: {build_number}")
    if wait is True:
        now = datetime.now()
        later = now + timedelta(seconds=time_out)

        while True:
            # we check current time vs the timeout(later)
            if datetime.now() > later:
                raise ValueError(f"Job: {job_name}:{build_number} is running for more than {time_out} we"
                                 f"stop monitoring the job,  you can check it in Jenkins")
            b = jenkins_connection.get_job_info(job_name, depth=1, fetch_all_builds=False)
            for i in b["builds"]:
                loop_id = i["id"]
                if int(loop_id) == build_number:
                    result = (i["result"])
                    print(f"result: {result}")  # in the json looks like null
                    if result is not None:
                        return i
                        # break
            time.sleep(interval)
        # return result

    return build_number

after we ask jenkins to build the job>get queue#>get job#> loop the info and get the status till change from None to something else. if works will return the directory with the information of that job. (hope the jenkins library could implement something like this.)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 esko.tajakka
Solution 3
Solution 4
Solution 5
Solution 6 Jesus Mogollon
Solution 7 pelos