Parallel Conditional Stage Never Evaluated When Before Agent Condition Is True

I have a single git repository with several top level root folders such as…

/repo/
   /api/
   /client/
   /db/
   Jenkinsfile

I want to create a pipeline that builds the projects in the /api/ or /client/ folders in parallel, but only if the contents from the respective folder changes. In addition, each project requires a different agent to complete its build.

This accomplished with the following Jenkinsfile.

pipeline {
    agent none
    environment {
        CI = "true"
    }
    stages {
        stage("Parallel") {
            failFast true
            parallel {
                stage("client") {
                    agent {
                        dockerfile {
                            dir "client"
                            filename "Dockerfile"
                            args "-v \"\${WORKSPACE}\\client:C:\\dev\""
                        }
                    }
                    when {
                        beforeAgent true
                        changeset "client/**"
                    }
                    stages {
                        stage("Build") {
                            steps {
                                pwsh "cd C:\\dev && yarn install && yarn build"
                            }
                        }
                    }
                }
                stage("api") {
                    agent {
                        dockerfile {
                            dir "api"
                            filename "Dockerfile"
                            args "-v \"\${WORKSPACE}\\api:C:\\dev\""
                        }
                    }
                    when {
                        beforeAgent true
                        changeset "api/**"
                    }
                    stages {
                        stage("Build") {
                            steps {
                                pwsh "(cd C:\\dev) && dotnet build"
                            }
                        }
                    }
                }
            }
        }
    }
}

This works fine when beforeAgent is false. If and only if the contents of /client/ or /api/ change are the respective pipeline stages executed. However, this means the evaluation happens after construction of their agents which is time wasted.

To save precious build minutes I can set beforeAgent to true but when I do the the conditional never evaluates to true and the respective stages are never run.

  1. What am I misunderstanding about how beforeAgent operates?
  2. How can I resolve this issue?

Environment:

  • Jenkins 2.263.2
  • Host: Windows Server 2019
  • Agents: Docker Containers (Windows)

I have found a workaround. If I add a stage to the start of the pipeline the evaluation happens correctly before agent creation. If files in /api/ and/or /client/ are modified only the respective agent is created.

This is the new Jenkinsfile

pipeline {
    agent none
    environment {
        CI = "true"
    }
    stages {
        // adding this stage allows correct behavior when evaluating when conditions
        // in the following "parallel" stage
        stage("start") {
            agent {
                docker { image 'mcr.microsoft.com/powershell:lts-windowsservercore-1809' }
            }
            steps {
                pwsh "Write-Host 'Start'"
            }
        }
        stage("parallel") {
            failFast true
            parallel {
                stage("client") {
                    agent {
                        dockerfile {
                            dir "client"
                            filename "Dockerfile"
                            args "-v \"\${WORKSPACE}\\client:C:\\dev\""
                        }
                    }
                    when {
                        beforeAgent true
                        changeset "client/**"
                    }
                    stages {
                        stage("client/build") {
                            steps {
                                pwsh "cd C:\\dev && yarn install && yarn build"
                            }
                        }
                    }
                }
                stage("api") {
                    agent {
                        dockerfile {
                            dir "api"
                            filename "Dockerfile"
                            args "-v \"\${WORKSPACE}\\api:C:\\dev\""
                        }
                    }
                    when {
                        beforeAgent true
                        changeset "api/**"
                    }
                    stages {
                        stage("api/build") {
                            steps {
                                pwsh "(cd C:\\dev) && dotnet build"
                            }
                        }
                    }
                }
            }
        }
    }
}

It’s not clear to me why this is required, but it is a suitable workaround until there is an official answer from the Jenkins community.