在构建链中使用参数
这个主题演示了您如何使用 TeamCity 构建参数 在 构建链 的配置之间交换简单数据。
参数 页面的 构建配置设置 允许您在输入参数和输出参数之间切换,这两者都是类似于名称/值对的功能。 它们的主要区别在于预期的使用场景和可见性设置。
note
输入/输出参数切换仅在构建配置设置中可用。 对于项目,您只能创建输入参数。
- 输入参数
输入参数旨在由定义它们的同一配置使用。 例如,一个存储默认分支名称并在 VCS 根设置 中引用的输入参数。 此值对其他配置没有用处,因此不应对它们可见。
输出参数可以共享现有参数(原样)、修改后的参数和常量。
outputParams {
// Expose predefined parameter as is
param("originConfName", "%system.teamcity.buildConfName%")
// Expose modified parameter value
param("buildNumber", "Build %\system.build.number%")
// Expose input parameter as is
param("name", "%existingInputParam%")
// Expose static value
param("number", "54")
}
输出参数值中的 %parameterName%
可以引用现有的输入参数或动态创建的参数。 例如,以下配置使用 输入 "DateFormat" 参数来计算通过 setParameter 服务消息创建的 "Date" 参数的值:
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.buildSteps.csharpScript
object CalculateDate : BuildType({
name = "Calculate Date"
params {
param("DateFormat", "dd/MM/yyyy") // Input parameter
}
steps {
csharpScript {
id = "csharpScript"
content = """
var date = DateTime.Now;
// Calculate and add a new 'Date' parameter
// Its value is shared by the output parameter
Console.WriteLine("##teamcity[setParameter name='Date' value='" + date.ToString("%DateFormat%") + "']");
""".trimIndent()
tool = "%teamcity.tool.TeamCity.csi.DEFAULT%"
}
}
})
现在,您可以创建一个共享 "Date" 参数值的输出参数。
object CalculateDate : BuildType({
params {
param("DateFormat", "dd/MM/yyyy")
}
outputParams {
exposeAllParameters = false
param("OutputDate", "%Date%")
}
})
tip
在 TeamCity UI 中, 添加新的输出参数 对话框有两个选项:手动输入值或选择现有的输入参数。 由于我们的 "Date" 参数在构建步骤执行之前不存在,因此我们需要手动输入
%Date%
值。否则,如果 "Date" 是一个常规输入参数,您可以从列表中选择它:
现在,输出参数可以被 依赖于原始配置的另一个配置访问。 要访问参数,请使用 dep.<origin configuration ID>.<output parameter name>
语法。

object PrintDate : BuildType({
name = "Print Date"
// Retrieve the 'OutputDate' value and print it
steps {
script {
id = "simpleRunner"
scriptContent = """echo "The date is ${CalculateDate.depParamRefs["OutputDate"]}""""
}
}
// An artifact or snapshot dependency is required
// to access another configuration's parameter
dependencies { snapshot(CalculateDate) {} }
})
note
出于安全原因, 密码类型 的输入参数不能通过输出参数公开。
输出参数 选项卡显示 所有参数对其他构建配置均可用 设置。

如果启用了此设置,则所有输入参数都可以通过 dep.<config ID>.<parameter name>
语法被依赖配置访问。 为了向后兼容,此设置默认启用。 但是,我们强烈建议您执行以下操作:
检查所有被其他配置使用的输入参数的情况。
通过在新的输出参数中引用您希望继续共享的输入参数来公开它们。
禁用 所有参数对其他构建配置均可用 设置。
这样可以确保配置保持易于维护:您可以根据需要编辑和删除输入参数,而不会意外破坏通过 dep...
语法使用这些参数的下游配置。 此外,这通过隐藏从未设计为共享的参数来增强安全性。
依赖构建可以以 dep.<bcID>.<parameter_name>
的形式访问前一个链构建的输出参数,其中 bcID
是您需要访问其参数值的源构建配置的 ID。

您可以使用 dep...
参数,即使当前配置仅具有间接依赖关系,也可以从配置中访问参数。 例如,在 A → B → C 的链中,C 依赖于 B,B 依赖于 A,配置 C 可以访问 A 的参数。
以下构建配置用于构建并推送 Docker 镜像。 这个镜像的名称被写入到 DockerImageName
参数中。
TAG=v1
docker build -f Dockerfile --tag your.registry/MyApp:${TAG}
docker push your.registry/MyApp:v1
echo "##teamcity[setParameter name='DockerImageName' value='MyApp:${TAG}']"
如果此配置的 ID 是“ConfigA”,则构建链下游执行的构建可以访问图像名称为 dep.ConfigA.DockerImageName
:
docker run -d your.registry/%\dep.ConfigA.DockerImageName%
添加一个具有 reverse.dep.<build_conf_ID>.<parameter_name>
名称语法的参数,以覆盖当前配置之前的目标配置中定义的输入 <parameter_name>
参数。
例如,以下的 Kotlin 代码定义了一个项目,该项目包含三个构建配置,这些配置在一个单一的构建链中统一(ConfigA → ConfigB → ConfigC)。 每个构建配置都有一个 chain.ConfigX.param
参数,并带有其自定义值。 最后的配置有额外的 reverse.dep.ChainConfigA.chain.ConfigA.param
参数。
import jetbrains.buildServer.configs.kotlin.*
project {
buildType(ChainConfigA)
buildType(ChainConfigB)
buildType(ChainConfigC)
}
object ChainConfigA : BuildType({
name = "ChainConfigA"
params {
param("chain.ConfigA.param", "Config A")
}
steps {
script {
scriptContent = """echo "Parameter value is: %chain.ConfigA.param%""""
}
}
})
object ChainConfigB : BuildType({
name = "ChainConfigB"
params {
param("chain.ConfigB.param", "Config B")
}
steps {
script {
scriptContent = """echo "Parameter value is: %chain.ConfigB.param%""""
}
}
dependencies {
snapshot(ChainConfigA) {
reuseBuilds = ReuseBuilds.NO
}
}
})
object ChainConfigC : BuildType({
name = "ChainConfigC"
params {
param("chain.ConfigC.param", "Config C")
param("reverse.dep.ChainConfigA.chain.ConfigA.param", "Value Overridden in ConfigC")
}
steps {
script {
scriptContent = """echo "Parameter value is: %chain.ConfigC.param%""""
}
}
dependencies {
snapshot(ChainConfigB) {
reuseBuilds = ReuseBuilds.NO
}
}
})
如果您运行 ConfigA 或 ConfigA → ConfigB 子链,第一个配置将会报告其原始参数值。
# ConfigA build log
Parameter value is: Config A
然而,如果您运行一个以 ConfigC 结束的完整构建链,这最后的配置将为 ConfigA 提供一个定制的参数值。
# ConfigA build log
Parameter value is: Value Overridden in ConfigC
您可以在参数名称中使用 *
通配符,以便在多个前面的配置中使用相同的参数。 例如,以下示例中的 ConfigC 具有 reverse.dep.ChainConfig*.MyParam
参数,该参数覆盖了 ConfigA 和 ConfigB 中的 MyParam
。
object ChainConfigA : BuildType({
params {
param("MyParam", "OriginalValue_A")
}
})
object ChainConfigB : BuildType({
params {
param("MyParam", "OriginalValue_B")
}
dependencies {
snapshot(ChainConfigA) {
reuseBuilds = ReuseBuilds.NO
}
}
})
object ChainConfigC : BuildType({
params {
param("reverse.dep.ChainConfig*.MyParam", "CustomValue_C")
}
dependencies {
snapshot(ChainConfigB) {
reuseBuilds = ReuseBuilds.NO
}
}
})
如果在 ConfigA → … → ConfigB → … → ConfigC 配置链中,ConfigB 和 ConfigC 配置试图覆盖 ConfigA 的参数,由于 ConfigC 依赖于 ConfigB (直接或通过中间配置),所以 ConfigC 有更高的优先级。
object ChainConfigA : BuildType({
params {
param("MyParam", "OriginalValue_A")
}
})
object ChainConfigB : BuildType({
params {
// Lower priority
param("reverse.dep.ChainConfigA.MyParam", "CustomValue_B")
}
// Depends on config A
dependencies {
snapshot(ChainConfigA) {
reuseBuilds = ReuseBuilds.NO
}
}
})
object ChainConfigC : BuildType({
params {
// Higher priority
param("reverse.dep.ChainConfigA.MyParam", "CustomValue_C")
}
// Depends on config B
dependencies {
snapshot(ChainConfigB) {
reuseBuilds = ReuseBuilds.NO
}
}
})
然而,如果 ConfigB 和 ConfigC 彼此间没有依赖关系,关于哪种配置应具有优先级的问题就会出现。 TeamCity 试图通过比较参数名称并优先考虑具有最具体构建配置 ID 的参数来解决这种歧义。
最高优先级:构建配置 ID 中没有通配符的参数(例如,
reverse.dep.ChainConfigA.MyParam
)。中等优先级:具有部分配置 ID 的参数(例如,
reverse.dep.Chain*A.MyParam
)。 目标配置 ID 越具体,此参数的优先级就越高。 例如,ChainConf*A
ID 优先于Chain*A
ID,因为它被认为更为特定。最低优先级:参数使用
*
通配符,而非配置 ID (例如,reverse.dep.*.MyParam
)。
如果所有冲突的配置都有相似的参数名称,且没有一个明显的赢家,那么 TeamCity 就会报告一个冲突并创建额外的 conflict.<build_config_ID>.<parameter_name>=<value>
参数(每个冲突的配置都有一个)。
object ChainConfigA : BuildType({
params {
param("MyParam", "OriginalValue_A")
}
})
object ChainConfigB : BuildType({
params {
// Equal priority
param("reverse.dep.ChainConfigA.MyParam", "CustomValue_B")
}
// Depends on config A
dependencies {
snapshot(ChainConfigA) {
reuseBuilds = ReuseBuilds.NO
}
}
})
object ChainConfigC : BuildType({
params {
// Equal priority
param("reverse.dep.ChainConfigA.MyParam", "CustomValue_C")
}
// Depends on config A
dependencies {
snapshot(ChainConfigA) {
reuseBuilds = ReuseBuilds.NO
}
}
})
// Composite build configuration that runs the entire chain
object ChainABC : BuildType({
type = BuildTypeSettings.Type.COMPOSITE
dependencies {
snapshot(ChainConfigB) {}
snapshot(ChainConfigC) {}
}
})

在队列构建中处理
reverse.dep.*
参数,这些参数在此处定义。 既然此阶段应已知道参数值,那么必须在构建配置或者 自定义构建对话框 中分配这些值。 将参数设置为在构建期间计算的值将无效。将新参数推送到构建中会覆盖 "如果存在合适的版本,则不运行新版本 " 快照依赖项选项,并可能在参数设置为非默认值时触发新的构建。
reverse.dep.
参数的值会“原样”推送到依赖性构建中,没有 引用解析。%
-引用,如果有的话,将在目标(target)构建的范围内得到解决。
Thanks for your feedback!