Jenkins status checks sans webhooks

Posted by Levi on Thursday, December 3, 2020

Jenkins the once dominant CI/CD work horse has taken a back seat as of late. Not because it’s less powerful then its competition but because they have lowered the barrier to entry. One place where tools like Travis-CI, Circle-CI and Gitlab-CI excel at are ease of use.

CI/CD Magic Qudrant
I guess Gitlab is not in the cool kids club =/

A very basic need for most teams is the ability to block a feature branch from being merged that doesn’t meet certain requirements. These checks could be things like code linting or no failing tests.

CI/CD Magic Qudrant
This is what a status check looks like in GitHub

If you google ${toolName} status checks the first result for every tool except Jenkins will be official documentation with one or two simple steps to enable GitHub status checks. Doing the same thing with Jenkins results in a maze of third party blogs posts with tons of confusing and miss leading information on how to enable status checks. Some suggest you need the Blue Ocean plugin, the GitHub plugin but the worst advice I often see repeated is that you need to be able to send a web hook to Jenkins in order to get status checks.

So what does a web hook from GitHub do? It lets you know of events in your GitHub repo. That’s it. Someone pushed, opened a PR, etc. You could use these to kick off a build if you had a public facing Jenkins or if you put your Jenkins behind some kinda reverse proxy but most of us don’t have that kinda setup. So can we do status checks without a web hook? Yes!

Status checks are just a simple API call. The details are here. The first thing we will need is a way to talk to the GitHub API. Authenticating can be done several ways but for now I will just use a personal access token for the demonstration. The only permission your token will need is repo:status.

Once you have a token we will need to create your status check. You could do this several ways but for this example I’m going to use curl.

node{
    
    def json = """\
    {"state": "pending","target_url": "${BUILD_URL}console","description": "${BUILD_NUMBER}#","context": "Jenkins CI - Testing"}
    """
    
    sh("""#!/bin/bash
    curl -s -XPOST -H \"Authorization: token <YOUR_TOKEN>\" \\
    https://api.github.com/repos/<USERNAME>/<REPO_NAME>/statuses/<COMMIT_HASH> \\
    -d '${json}'
    """)
}

Don’t forget to change <> with your information! After running that you should get something like this in your repo.

CI/CD Magic Qudrant

Awesome! So lets make an example pipeline to demonstrate what might be possible. First we will move our curl command to its own function.

node{
    statusCheck('pending', 'Testing')
}

def statusCheck(state, action){
    def json = """\
    {"state": "${state}","target_url": "${BUILD_URL}console","description": "${BUILD_NUMBER}#","context": "Jenkins CI - ${action}"}
    """
    
    sh("""#!/bin/bash
    curl -s -XPOST -H \"Authorization: token <YOUR_TOKEN>\" \\
    https://api.github.com/repos/<USERNAME>/<REPO_NAME>/statuses/<COMMIT_HASH> \\
    -d '${json}'
    """)
}

Now for the job…

node{
    statusCheck('pending','Testing')
    stage('Package code'){
        statusCheck('pending','NPM Audit')
        // npm ci
        try{
            //npm audit
        }catch(Exception ex){
            statusCheck('failure','NPM Audit')
        }
        statusCheck('success','NPM Audit')
    }
    
    stage('Unit Tests'){
        statusCheck('pending','Unit Tests')
        try{
            //npm test
        }catch(Exception ex){
            statusCheck('failure','Unit Tests')
        }
        statusCheck('success','Unit Tests')
    }
    statusCheck('success','Testing')
}

def statusCheck(state, action){
    def json = """\
    {"state": "${state}","target_url": "${BUILD_URL}console","description": "${BUILD_NUMBER}#","context": "Jenkins CI - ${action}"}
    """
    
    sh("""#!/bin/bash
    curl -s -XPOST -H \"Authorization: token <YOUR_TOKEN>\" \\
    https://api.github.com/repos/<USERNAME>/<REPO_NAME>/statuses/<COMMIT_HASH> \\
    -d '${json}'
    """)
}
CI/CD Magic Qudrant

There you have it. Jenkins status checks in GitHub without a web hook.