防止在 Jenkins 作业参数中扩展环境变量
Preventing expansion of environment variables in Jenkins job parameters
问题
我正在做一项 Jenkins 工作,它接受来自用户的一些参数。我遇到了一个不希望的行为:在我的脚本有机会读取它们之前,Jenkins 似乎正在扩展参数环境变量中的环境变量引用。
如果用户输入foo-$BUILD_NUMBER
作为参数,我的脚本实际看到的是foo-123
;环境变量被扩展。如果输入的值包含 $$
,我的脚本只会看到一个 $
。但是,如果它包含环境中不存在的 $variable
,则该值保持不变(不会引发任何类型的错误)。
这很不方便,因为它甚至发生在密码字段中,而且我通常使用可以包含 $
个字符的密码生成器。我不希望我的密码可能被无声地破坏。
例子
我的初始测试用例如下,使用 Jenkins Job Builder Groovy DSL。
new BaseJobBuilder(
jobName: 'example',
jobBuildName: 'example-${BUILD_NUMBER}',
).build(this).with {
parameters {
nonStoredPasswordParam('SERVICE_PASSWORD')
}
steps {
shell('echo "$SERVICE_PASSWORD";')
}
}
然而,为了更简化的测试用例,我从 jenkins/jenkins:lts
Docker 图像创建了一个新的 Jenkins 安装,在没有任何插件(甚至是默认插件)的情况下配置它,并使用创建了一个等效的作业网络 UI.
当我 运行 这些作业中的任何一个为我的参数 SERVICE_PASSWORD
设置了值 hello $BUILD_NUMBER $HOME world
时,输出扩展了变量,而不是我想要的文字值。
Started by user jeremy
Building in workspace /var/jenkins_home/workspace/jeremy
[jeremy] $ /bin/sh -xe /tmp/jenkins2451955822062381529.sh
+ echo hello 3 /var/jenkins_home world
hello 3 /var/jenkins_home world
Finished: SUCCESS
问题
有没有办法在 Jenkins 受到变量 expansion/interpolation 影响之前访问它们的原始参数值,或者以其他方式禁用或规避此行为?
我如何接受可能包含 $
美元字符的原始文本参数而不会有被损坏的风险?
相关链接
- JENKINS-10779 Non-existing parameters are not empty but replaced with parameter name
- JENKINS-16143 Jenkins escapes 2 dollar signs in the Parameterized Build fields
- JENKINS-26916 There should be an option to turn on/off variable substitution in build parameters
- Jenkins text parameter - special characters garbled (unwanted variable substitution)
作为解决方法,我们可以添加一个初始构建步骤,直接读取所有参数并在 base64 中对它们进行编码,然后将编码值导出为新的环境变量。 Base64 编码的值不能包含任何美元字符 $
,因此它们可以被后面的构建步骤安全地读取,这些步骤可以解码它们以获得原始值而无需任何扩展。
我们通过 the Groovy plugin, which runs a custom Groovy script with direct access to the build state (thanks to daspilker for the suggestion). If you're using the DSL, you could add this with a systemGroovyCommand("""…""")
调用的 "System Groovy Script" 构建步骤来实现这一点。
import hudson.EnvVars;
import hudson.model.Executor;
import hudson.model.Environment;
def build = Executor.currentExecutor().currentExecutable;
def newVariables = [:];
build.getBuildVariables().each { name, value ->
def encodedName = name + "_B64";
def encodedValue = value.bytes.encodeBase64().toString();
newVariables.put(encodedName, encodedValue);
}
build.getEnvironments().add(Environment.create(new EnvVars(newVariables)))
您的 Jenkins 管理员可能需要在 the In-process Script Approval page 首次加载此脚本时批准它。因为它已经对所有参数进行了编码,所以您永远不需要修改它,并且可以在其他作业中重复使用它而无需重新批准。
后续 "Execute Shell" 步骤现在将能够解码原始值。
set -eu +vx;
echo "directly from environment: $SERVICE_PASSWORD";
SERVICE_PASSWORD="$(echo "$SERVICE_PASSWORD_B64" | base64 --decode)";
echo "via base-64 encoded value: $SERVICE_PASSWORD";
我们可以使用我们的原始测试值 hello $BUILD_NUMBER $HOME world
来确认它是否有效:
directly from environment: hello 12 /var/jenkins_home world
via base-64 encoded value: hello $BUILD_NUMBER $HOME world
当您从环境变量中读取参数时,无法禁用变量扩展。此行为与具体参数无关,但与 Jenkins 一般处理环境变量的方式有关。在 hudson.model.AbstractBuild.getEnvironment(…)
method aggregates all of the environment variable for a build, it applies the hudson.EnvVars.resolve(…)
function to perform mutual variable expansion on the contents of all environment variables. Depending on the exact build configuration, it may also use hudson.EnvVars.overrideExpandingAll(…)
, which takes the additional step of topologically sorting the variables to ensure that non-cyclic references are all expanded in the correct order. The actual string manipulation is performed by hudson.util.replaceMacro(…)
之后,有一条注释解释了不存在变量的异常处理(非替换):
Unlike shell, undefined variables are left as-is (this behavior is the same as Ant.)
这些扩展是无条件执行的,所以如果不在 Jenkins 中修改或替换几个 类 就无法禁用它。
问题
我正在做一项 Jenkins 工作,它接受来自用户的一些参数。我遇到了一个不希望的行为:在我的脚本有机会读取它们之前,Jenkins 似乎正在扩展参数环境变量中的环境变量引用。
如果用户输入foo-$BUILD_NUMBER
作为参数,我的脚本实际看到的是foo-123
;环境变量被扩展。如果输入的值包含 $$
,我的脚本只会看到一个 $
。但是,如果它包含环境中不存在的 $variable
,则该值保持不变(不会引发任何类型的错误)。
这很不方便,因为它甚至发生在密码字段中,而且我通常使用可以包含 $
个字符的密码生成器。我不希望我的密码可能被无声地破坏。
例子
我的初始测试用例如下,使用 Jenkins Job Builder Groovy DSL。
new BaseJobBuilder(
jobName: 'example',
jobBuildName: 'example-${BUILD_NUMBER}',
).build(this).with {
parameters {
nonStoredPasswordParam('SERVICE_PASSWORD')
}
steps {
shell('echo "$SERVICE_PASSWORD";')
}
}
然而,为了更简化的测试用例,我从 jenkins/jenkins:lts
Docker 图像创建了一个新的 Jenkins 安装,在没有任何插件(甚至是默认插件)的情况下配置它,并使用创建了一个等效的作业网络 UI.
当我 运行 这些作业中的任何一个为我的参数 SERVICE_PASSWORD
设置了值 hello $BUILD_NUMBER $HOME world
时,输出扩展了变量,而不是我想要的文字值。
Started by user jeremy
Building in workspace /var/jenkins_home/workspace/jeremy
[jeremy] $ /bin/sh -xe /tmp/jenkins2451955822062381529.sh
+ echo hello 3 /var/jenkins_home world
hello 3 /var/jenkins_home world
Finished: SUCCESS
问题
有没有办法在 Jenkins 受到变量 expansion/interpolation 影响之前访问它们的原始参数值,或者以其他方式禁用或规避此行为?
我如何接受可能包含 $
美元字符的原始文本参数而不会有被损坏的风险?
相关链接
- JENKINS-10779 Non-existing parameters are not empty but replaced with parameter name
- JENKINS-16143 Jenkins escapes 2 dollar signs in the Parameterized Build fields
- JENKINS-26916 There should be an option to turn on/off variable substitution in build parameters
- Jenkins text parameter - special characters garbled (unwanted variable substitution)
作为解决方法,我们可以添加一个初始构建步骤,直接读取所有参数并在 base64 中对它们进行编码,然后将编码值导出为新的环境变量。 Base64 编码的值不能包含任何美元字符 $
,因此它们可以被后面的构建步骤安全地读取,这些步骤可以解码它们以获得原始值而无需任何扩展。
我们通过 the Groovy plugin, which runs a custom Groovy script with direct access to the build state (thanks to daspilker for the suggestion). If you're using the DSL, you could add this with a systemGroovyCommand("""…""")
调用的 "System Groovy Script" 构建步骤来实现这一点。
import hudson.EnvVars;
import hudson.model.Executor;
import hudson.model.Environment;
def build = Executor.currentExecutor().currentExecutable;
def newVariables = [:];
build.getBuildVariables().each { name, value ->
def encodedName = name + "_B64";
def encodedValue = value.bytes.encodeBase64().toString();
newVariables.put(encodedName, encodedValue);
}
build.getEnvironments().add(Environment.create(new EnvVars(newVariables)))
您的 Jenkins 管理员可能需要在 the In-process Script Approval page 首次加载此脚本时批准它。因为它已经对所有参数进行了编码,所以您永远不需要修改它,并且可以在其他作业中重复使用它而无需重新批准。
后续 "Execute Shell" 步骤现在将能够解码原始值。
set -eu +vx;
echo "directly from environment: $SERVICE_PASSWORD";
SERVICE_PASSWORD="$(echo "$SERVICE_PASSWORD_B64" | base64 --decode)";
echo "via base-64 encoded value: $SERVICE_PASSWORD";
我们可以使用我们的原始测试值 hello $BUILD_NUMBER $HOME world
来确认它是否有效:
directly from environment: hello 12 /var/jenkins_home world
via base-64 encoded value: hello $BUILD_NUMBER $HOME world
当您从环境变量中读取参数时,无法禁用变量扩展。此行为与具体参数无关,但与 Jenkins 一般处理环境变量的方式有关。在 hudson.model.AbstractBuild.getEnvironment(…)
method aggregates all of the environment variable for a build, it applies the hudson.EnvVars.resolve(…)
function to perform mutual variable expansion on the contents of all environment variables. Depending on the exact build configuration, it may also use hudson.EnvVars.overrideExpandingAll(…)
, which takes the additional step of topologically sorting the variables to ensure that non-cyclic references are all expanded in the correct order. The actual string manipulation is performed by hudson.util.replaceMacro(…)
之后,有一条注释解释了不存在变量的异常处理(非替换):
Unlike shell, undefined variables are left as-is (this behavior is the same as Ant.)
这些扩展是无条件执行的,所以如果不在 Jenkins 中修改或替换几个 类 就无法禁用它。