Remove Old Job Builds on Jenkins with Groovy Script

From Bonus Bits
Jump to: navigation, search

Purpose

This article describes a way to use a Groovy script to remove old builds from all jobs up to a set number. In other words, you set how many of the latest builds to keep and run it on the Jenkins Master to cleanup all previous builds for all jobs. This script includes Cloudbees Folders, Basic Projects, Github Organizations plugin and Workflow Multibranch plugin support.

Take the following script and either run it from the CLI or from the Jenkins Management Script Console GUI.


Prerequisites

  • Jenkins 2.x+

Steps Using GUI

  1. Set the 2nd argument to how many builds you wish to keep. In this example all but 10 builds will be deleted for each job. listJobObjects(item, 10, 0)
  2. Select Manage Jenkins on the Master you wish to perform the task on
  3. Select Script Console
  4. Paste the Groovy Script in the script window
  5. Select Run

Steps for CLI

  1. First you have to have a SSH Key pair setup for a user that has adiquite permissions
    1. Generate SSH Key pair
      ssh-keygen -t rsa -b 4096
      
    2. Add public key to local admin Jenkins user
      1. Browse People | <username> | Configure
      2. Paste Public key in SSH Public Keys window and Save
  2. Copy the groovy script content to a file such as /tmp/remove-old-builds.groovy
  3. Copy private RSA key if not already on the system i.e. ~/.ssh/id_rsa
  4. Set permissions on the private key if needed
    chmod 0400 ~/.ssh/id_rsa
    
  5. Locate the jenkins-cli.jar
    find . -type f -name 'jenkins-cli.jar'
    
    OR
    updatedb && locate 'jenkins-cli.jar'
    
  6. Run java jenkins cli from Master or you could setup another box with the jar etc.
    java -jar /var/cache/jenkins/war/WEB-INF/jenkins-cli.jar -i ~/.ssh/id_rsa -s http://localhost:8080 groovy /tmp/remove-old-builds.groovy
    


Groovy Script

import jenkins.model.*
import hudson.model.*
import com.cloudbees.hudson.plugins.folder.*
import jenkins.branch.*
import org.jenkinsci.plugins.workflow.job.*
import org.jenkinsci.plugins.workflow.multibranch.*

/**
 * Utility function used to delete old builds.
 *
 * @param item the current Jenkins item to process, this can be a Folder or a Project
 * @param numberOfBuildsToKeep the total number of builds to keep. Please note that one more build could be
 *        kept if the first "numberOfBuildsToKeep" builds are all in a failed state.
 */

def deleteOldBuilds(item, Integer numberOfBuildsToKeep, Integer numberOfSuccessfulBuildsKept) {
    def count = 1

    println('Checking for Old Builds...')

    for (build in item.getBuilds()) {
        if(count++ >= numberOfBuildsToKeep) {
            if(item.getBuildStatusIconClassName() == 'icon-blue' && numberOfSuccessfulBuildsKept == 0) {
                println('Keep ' + build)
            } else {
                println('Deleting ' + build)
                build.delete()
            }
        } else if(item.getBuildStatusIconClassName() == 'icon-blue') {
            numberOfSuccessfulBuildsKept++
        }
    }
    println('PRIOR BUILD COUNT: (' + count + ')')
    println ''
}

def listJobObjects(item, Integer numberOfBuildsToKeep, Integer numberOfSuccessfulBuildsKept) {
    if(item instanceof Project) {
        println('PROJECT: (' + item.getName() + ')')
        deleteOldBuilds(item, numberOfBuildsToKeep, numberOfSuccessfulBuildsKept)
    } else if(item instanceof Folder) {
        println ''
        println('FOLDER: (' + item.getName() + ')')
        println('*************************************')
        for (subItem in item.items) {
            listJobObjects(subItem, numberOfBuildsToKeep, numberOfSuccessfulBuildsKept)
        }
    } else if(item instanceof WorkflowMultiBranchProject) {
        println('MULTIBRANCH-PROJECT: (' + item.getName() + ')')
        for (subItem in item.items) {
            listJobObjects(subItem, numberOfBuildsToKeep, numberOfSuccessfulBuildsKept)
        }
    }  else if(item instanceof WorkflowJob) {
        println('MULTIBRANCH-JOB: (' + item.getName() + ')')
        deleteOldBuilds(item, numberOfBuildsToKeep, numberOfSuccessfulBuildsKept)
    } else if(item instanceof OrganizationFolder) {
        println('ORG-FOLDER: (' + item.getName() + ')')
        for (subItem in item.items) {
            listJobObjects(subItem, numberOfBuildsToKeep, numberOfSuccessfulBuildsKept)
        }
    } else {
        println('UNKNOWN: (' + item.getName() + ')')
        println('CLASS: (' + item.getClass() + ')')
        println('INSPECT: (' + item.inspect() + ')')
    }
}

for (item in Jenkins.instance.items) {
    println ''
    listJobObjects(item, 10, 0)
    println('*************************************')
}


Icon-Tip-Square-Green.png I recommend commenting out the build.delete() and running a trial first. The output is the same as if it deleted the builds, but it won't actually.


Sources