为什么每次使用这个环境变量都要评估它?

why is this environment variable evaluated every time it is used?

我注意到了这种(对我来说)奇怪的行为。我有这个 Jenkins 声明式管道:

#!groovy

pipeline {

    agent {
        node {
            label 'mine-agent-pod'
        }
    }

    environment {
        MARKER = """run-${sh(
            returnStdout: true,
            script: "date -Ins | sed 's/[^a-zA-Z0-9-]/_/g'"
        ).trim()}"""
        STATUS_DATA = "status-data-${MARKER}.json"
    }

    stages {

        stage('Setup') {
            steps {
                sh("""echo MARKER=${MARKER}""")
                sh("""echo STATUS_DATA=${STATUS_DATA}""")
            }
        }

    }
}

我希望 MARKER 成为一种 ID,我可以用它来标记我在构建中创建的所有临时内容(我喜欢它是一个日期)。但看起来 MARKER 在每次使用时都会被评估,正如构建的输出所示(注意字符串的纳秒部分有何不同):

[Pipeline] sh
+ echo MARKER=run-2020-07-07T12_04_23_369785902_00_00
MARKER=run-2020-07-07T12_04_23_369785902_00_00
[Pipeline] sh
+ echo STATUS_DATA=status-data-run-2020-07-07T12_04_23_727188019_00_00.json
STATUS_DATA=status-data-run-2020-07-07T12_04_23_727188019_00_00.json

这是为什么?如何实现拥有“静态”变量?

这是因为 Groovy 闭包比单纯的表达式有一个有趣的优势:惰性求值。更多detail

environment {
        MARKER = 'run-' + sh(
            returnStdout: true,
            script: "date -Ins | sed 's/[^a-zA-Z0-9-]/_/g'").trim()
        STATUS_DATA = "status-data-${MARKER}.json"
    }

在同事的建议下,在管道外定义变量很有帮助:

#!groovy

def MARKER = """run-${ new Date().format("yyyy-MM-dd'T'HH:mm:ss.SZ") }"""

pipeline {

    agent {
        node {
            label 'sat-cpt'
        }
    }

    environment {
        STATUS_DATA = "status-data-${MARKER}.json"
    }

    stages {

        stage('Setup') {
            steps {
                sh("""echo MARKER=${MARKER}""")
                sh("""echo STATUS_DATA=${STATUS_DATA}""")
            }
        }

    }
}

这会打印:

[Pipeline] sh
+ echo MARKER=run-2020-07-08T19:41:56.130+0000
MARKER=run-2020-07-08T19:41:56.130+0000
[Pipeline] sh
+ echo STATUS_DATA=status-data-run-2020-07-08T19:41:56.130+0000.json
STATUS_DATA=status-data-run-2020-07-08T19:41:56.130+0000.json