I ended up using lock and a closure that decides when to lock (no concurrency) and when not to lock ( concurrency allowed ) based on the branch as input
public withLock(String branch, Closure closure){
boolean noConcurrency = branch in ['master'] // branches for which no concurrent builds are allowed
if (noConcurrency) {
lock( // only one build enters the lock
resource: "${env.JOB_NAME}", // use the job name as lock resource to make the mutual exclusion only for builds from the same branch/tag
inversePrecedence: true // latest build in the queue will get the lock/ start first
) {
milestone() // aborts build older than this build that have been waiting for the lock
closure()
}
}
else {
closure()
}
}
// pipeline code
withLock (env.BRANCH_NAME){
// lock is before any node call
node(){
// here the logic of the pipeline
}
}
NOTE this will mess up the build duration statistics though - so the builds that are waiting can have up to the double of the normal build duration