Jenkins script to abort jobs on PR update

Test runs and sonarqube analysis for hybris projects are time-consuming and almost in each project it is required to abort CI jenkins job on branch update to reduce load on jenkins servers. Unfortunately there is no commonly used generic solution to solve this task.

Script below iterates via all queued jobs and all running jobs and will abort all jobs, which were triggered by BPPR plugin for same branch.

To use it just add at the top of Jenkinsfile before pipeline block:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@NonCPS
boolean isTriggeredFromPR(runnable, branchName) {
    def runTaskCauses = runnable.getCauses()
    for (cause in runTaskCauses) {
        if (cause instanceof io.jenkins.plugins.bitbucketpushandpullrequest.cause.BitBucketPPRTriggerCause) {
            def prAction = cause.getAction()
            if (prAction.getSourceBranch() == branchName) {
                return true
            }
        }
    }
    return false
}

@NonCPS
def abortSimilarJobs() {
    println("Abort queued jobs for same branch")

    def BRANCH_FOR_JOB_ABORTING
    if (env.BITBUCKET_SOURCE_BRANCH) {
        BRANCH_FOR_JOB_ABORTING = env.BITBUCKET_SOURCE_BRANCH
    } else {
        BRANCH_FOR_JOB_ABORTING = env.JENKINS_JOB_CUSTOM_BRANCH
    }

    if (BRANCH_FOR_JOB_ABORTING) {
        println("Identify jobs for aborting by branch '${BRANCH_FOR_JOB_ABORTING}'")

        println("Clean up queue")
        def taskQueue = Jenkins.instance.queue
        taskQueue.items.each { queueItem ->
            def placeholderTask = queueItem.task
            // To filter by task type put here if placeholderTask.getOwnerTask().getName() == Auto_CI etc
            try {
                def run = placeholderTask.run()
                if (run && isTriggeredFromPR(run, BRANCH_FOR_JOB_ABORTING)) {
                    println("Cancelling " + placeholderTask.getName())
                    taskQueue.cancel(placeholderTask)
                }
            } catch (Exception e) {
                println("Not executable job found")
            }
        }

        println("Clean up running build")
        Jenkins.instance.computers.each { computer ->
            computer.getExecutors().each { executor ->
                if (executor.isBusy()) {
                    def executableWrapper = executor.getCurrentExecutable()
                    def placeholderTask = executableWrapper.getParent()
                    try {
                        def run = placeholderTask.run()
                        if (run && isTriggeredFromPR(run, BRANCH_FOR_JOB_ABORTING)) {
                            println("Cancelling running " + placeholderTask.getName())
                            executor.interrupt(Result.ABORTED, new CauseOfInterruption.UserInterruption("Interrupted by PR update"))
                        }
                    } catch (Exception e) {
                        println("Executing job already finished.")
                    }
                }
            }
        }
    }

    println("Finish job aborting")
}

// Execute before scheduling on execution
abortSimilarJobs()

P.S. Suppose this script could be simplified with Jenkins OOTB milestones after milestone groups would be merged and released.

comments powered by Disqus