Skip to content
This repository is currently being migrated. It's locked while the migration is in progress.

A Jenkins meta-plugin for defining a build in python sourced from version control

License

Notifications You must be signed in to change notification settings

tanium/pyjenkins

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

68 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Jenkins Python API Plugin

A Jenkins meta-plugin for defining a build in python sourced from version control

Overview

Basic motivation: A build definition sourced from version control

This plugin allows you to define your Jenkins build using a python script sourced from your build job's workspace, rather than configured using Jenkins' web UI. The plugin injects the Jenkins build context into the python script to allow the python script to communicate with Jenkins and perform build actions.

It's hard to describe how cool this is.

  • Backed up and versioned

    Your build definition is as well backed up as is your source code--and versioned to boot!

  • Empowering to Jenkins consumers and admins

    You can lock down job configuration while still empowering developers to modify, for example, which files get archived, where test reports are gathered from, and that all from code changes without ever having to login to Jenkins!

  • Lightweight templating

    This does not get you all the way to a truly "templated" build, but it does get much of the way there, since it can get you down to one build step that can be the same everywhere: "run the jenkins-build.py thing".

    You no longer have to have slightly different job configurations per branch. If there is a new place for test reports or a new artifact to archive, just update the python build script on that branch!

Enough talk! Let's seen an example!

from pyjenkins import *

class TheBuild(PyJenkinsBuild):
    def run(self):
        # read a remote file
        version = read_remote_file('the-version.txt')
        # set the display name for this build
        build.setDisplayName(version)

        # execute the build
        built = False
        if is_windows():
            built = execute(['cmd', '/c', 'build.bat'])
        else:
            built = execute(['bash', 'build.sh'])

        # report results always
        report_tests( '**/test-reports/*-TEST.xml' )

        if not built:
            logger.println('Whoops. It broked.')
            return Result.FAILURE

        # archive artifacts on build success
        if is_windows():
            # fails if artifact is not present
            archive_artifacts( 'build/foo.exe' )
            # fails if not one of the patterns is not present
            archive_artifacts( 'build/setup.msi,foo/setup.exe' )

        else:
            # fails if artifact is not present
            archive_artifacts( 'build/foo' )
            # fails if not one of the patterns is not present
            archive_artifacts( 'build/setup.rpm,build/setup.deb' )

        # return success
        return Result.SUCCESS

register_pybuild( TheBuild() )

All you need to tell jenkins is:

PyJenkins Build Step

Warning: The python script runs on the master

One of the downsides of using a scripting language (i.e. python) to describe the build as opposed to something more declarative (like XML) is that it makes it appear that the python script is actually running on the remote node.

It is not. The plugin runs on the master.

It took a bit of a mindshift for me to get at first. Nevertheless, it is still quite powerful to have build logic expressed in python. This world already has far too much XML in it anyway, and I didn't want to add to the mess. You'll just have to remember to use the Jenkins API to run commands, read remote files, etc., rather than trying to do it directly.

PyJenkins API

The full PyJenkins API is available in pyjenkins.py.

Basics

Step 1: Create a class that implements the PyJenkinsBuild interface

Step 2: Make sure the run method returns an instance of Result. (This class is made available in python as pyjenkins.Result.)

Step 3: Call pyjenkins.register_pybuild() with an instance of this class.

Functions made available in pyjenkins.py:

register_pybuild: Registers your python class with the Java plugin so it knows what to run

report_tests: Takes a file pattern as an argument (e.g. report_tests('test-reports/\*\*/\*-TEST.xml')) and runs all these files through the JUnit XML report parser.

archive_artifacts: Takes a file pattern of build artifacts that should be archived by Jenkins. This will fail the build if the artifact is not present. If a comma-separated list is provided, as in archive_artifacts('\*.rpm,\*.deb'), it will only fail if none of the patterns match.

execute: Takes an array of strings -- the command to execute along with its arguments. Example: execute(['bash', 'echo', 'hello there!'])

is_windows: True if the Jenkins node the job is running on is Windows.

remote_file: Takes a relative path to the job's workspace and returns a FilePath object that represents the target file on the Jenkins remote node.

read_remote_file: A shortcut for remote_file(file).readToString()

Development

Development environment should be really simple to setup; all you need is a JDK and maven.

  1. Run tests:

     mvn test
    
  2. Package:

     mvn package -DskipTests
    
  3. Upload target/jenkins-python-api-plugin.hpi to your Jenkins instance!

About

A Jenkins meta-plugin for defining a build in python sourced from version control

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published