TeamCity On-Premises 2025.07 Help

在构建链中使用参数

这个主题演示了您如何使用 TeamCity 构建参数构建链 的配置之间交换简单数据。

输入和输出参数

参数 页面中的 构建配置设置 允许您在输入参数和输出参数之间切换。 它们都是手动创建的名称/值对。 关键区别在于预期的使用场景和可访问性设置。

输入参数

输入参数旨在由定义它们的同一配置使用。 例如,一个存储默认分支名称并在 VCS 根设置 中引用的输入参数。 此值对其他配置没有用处,因此不应对它们可见。

输出参数

输出参数在一个构建配置中配置,但可以通过 快照工件 依赖由另一个配置访问。 例如,一个构建 Docker 镜像的构建配置可以将此镜像名称写入其参数。 该参数随后由下游配置使用,该配置将此镜像部署到注册表。

请参阅以下部分,了解有关如何创建和使用输出参数的更多信息。

创建输出参数

输出参数可以共享现有参数(原样)、修改后的参数和常量。

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 } outputParams { // TODO } 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({ name = "Calculate Date" params { param("DateFormat", "dd/MM/yyyy") // Input parameter } outputParams { exposeAllParameters = false param("OutputDate", "%Date%") // Output parameter that exposes "Date" } steps { // ... } })

现在,输出参数可以被 依赖于原始配置的另一个配置访问。 要访问参数,请使用 dep.<origin configuration ID>.<output parameter name> 语法。

访问输出参数
object PrintDate : BuildType({ name = "Print Date" // Retrieve the date from the 'OutputDate' configuration 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) {} } })

公开所有输入参数

输出参数 选项卡显示 所有参数对其他构建配置均可用 设置。

公开所有输出参数

如果启用了此设置,则所有输入参数都可以通过 dep.<config ID>.<parameter name> 语法被依赖配置访问。 为了向后兼容,此设置默认启用。 但是,我们强烈建议您执行以下操作:

  1. 检查所有被其他配置使用的输入参数的情况。

  2. 通过在新的输出参数中引用您希望继续共享的输入参数来公开它们。

  3. 禁用 所有参数对其他构建配置均可用 设置。

这样可以确保配置保持易于维护:您可以根据需要编辑和删除输入参数,而不会意外破坏通过 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.ProjectID_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.${DslContext.projectId}_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.${DslContext.projectId}_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.${DslContext.projectId}_ChainConfigA.MyParam", "CustomValue_B") } // Depends on config A dependencies { snapshot(ChainConfigA) { reuseBuilds = ReuseBuilds.NO } } }) object ChainConfigC : BuildType({ params { // Higher priority param("reverse.dep.${DslContext.projectId}_ChainConfigA.MyParam", "CustomValue_C") } // Depends on config B dependencies { snapshot(ChainConfigB) { reuseBuilds = ReuseBuilds.NO } } })

然而,如果 ConfigB 和 ConfigC 彼此间没有依赖关系,关于哪种配置应具有优先级的问题就会出现。 TeamCity 试图通过比较参数名称并优先考虑具有最具体构建配置 ID 的参数来解决这种歧义。

  • 最高优先级:构建配置 ID 中没有通配符的参数(例如, reverse.dep.MyConfigID.MyParam)。

  • 中等优先级:具有部分配置 ID 的参数(例如, reverse.dep.Build*.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.${DslContext.projectId}_ChainConfigA.MyParam", "CustomValue_B") } // Depends on config A dependencies { snapshot(ChainConfigA) { reuseBuilds = ReuseBuilds.NO } } }) object ChainConfigC : BuildType({ params { // Equal priority param("reverse.dep.${DslContext.projectId}_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)构建的范围内得到解决。

最后修改日期: 2025年 8月 12日