Jenkins - when a build starts, abort all previous builds of the same branch

Viewed 794

I use Jenkins to execute tests whenever a PR is created or updated.

The tests take about 20 minutes, and quite often a PR is created, but 4-5 minutes later is updated by another commit. In that case, I want to cancel the previous test run as it is no longer valuable.

I tried implementing it using a groovy script

import hudson.model.Result
import jenkins.model.CauseOfInterruption
import hudson.model.ParametersAction
import hudson.model.Job
import jenkins.model.Jenkins

//iterate through current project runs
build.getProject()._getRuns().iterator().each{ run ->
  def exec = run.getExecutor()
  //if the run is not a current build and it has executor (running) then stop it
  if(run != build && exec != null) {
    def other_run_params = run.actions.find{ it instanceof ParametersAction }?.parameters
    def current_build_params = build.actions.find{ it instanceof ParametersAction }?.parameters

    def should_cancel = true
    
    other_run_params.each { other_run_param ->
        current_build_params.each { current_build_param ->
            if (other_run_param.name == current_build_param.name && other_run_param.dump() != current_build_param.dump()) {
                should_cancel = false
            }
        }
    }
    
    // Cancelling all subprojects. If I skip this loop and just cancel 'exec', the subprojects continue to run
    if (should_cancel) {
        println("Attempting to cancel previous build!")
        // prepare the cause of interruption
        def cause = { "interrupted by build #${build.getId()}" as String } as CauseOfInterruption         

        // Cancel all child objects
        def buildingJobs = Jenkins.instance.getAllItems(Job.class).findAll { it.isBuilding() }
        buildingJobs.each { job ->
          allRuns = job._getRuns().iterator().each { child_run ->
            upstream = child_run.getCause(hudson.model.Cause.UpstreamCause.class)?.upstreamRun

            while (upstream != null) {
              if (upstream == run) {                
                def child_exec = child_run.getExecutor()
                if (child_exec != null) {
                  println("Cancelling child job!")
                  child_exec.interrupt(Result.ABORTED, cause)
                }
              }

              upstream = upstream.getCause(hudson.model.Cause.UpstreamCause.class)?.upstreamRun
            }
          }
        }

        // Cancel the root
        exec.interrupt(Result.ABORTED, cause)
    }
  }
}

But currently, I have in Jenkins about 30 jobs, each one with a history of about 5k, so this execution takes about 10 minutes to run. Can I do it more efficiently?

EDIT: If I just skip the second loop and just abort the second run, it does not stop all subprojects.

0 Answers
Related