在Azure DevOps Pipelines中跨阶段共享变量

64

我正在尝试弄清如何在我的脚本中跨ADO管道共享自定义变量。以下是我的带有2个阶段的脚本。

我将curProjVersion设置为输出变量,并尝试从不同的阶段访问它。我这样做对吗?

stages:
- stage: Build
  displayName: Build stage
  jobs:
  - job: VersionCheck
    pool:
      vmImage: 'ubuntu-latest'
    displayName: Version Check
    continueOnError: false
    steps:

      - script: |
          echo "##vso[task.setvariable variable=curProjVersion;isOutput=true]1.4.5"
        name: setCurProjVersion
        displayName: "Collect Application Version ID"

- stage: Deploy
  displayName: Deploy stage
  dependsOn: Build
  variables:
    curProjVersion1: $[ dependencies.Build.VersionCheck.outputs['setCurProjVersion.curProjVersion'] ]
  jobs:
  - job: 
    steps: 
      - script: |
          echo $(curProjVersion1)

更详细的解释和示例源代码可以在此处找到:https://rollendxavier.medium.com/sharing-variables-between-stages-in-azure-devops-pipelines-59eb8decf3b0 - Raygun
10个回答

56

更新:

现在已经在Sprint 168中发布了跨阶段共享变量的功能。

请使用以下格式访问前一阶段的输出变量:

stageDependencies.{stageName}.{jobName}.outputs['{stepName}.{variableName}'] 

翻译结果:

在Azure DevOps Pipelines中跨阶段共享变量

很抱歉告诉您,在一个阶段中定义的变量无法共享到另一个阶段。

这是我们计划添加的功能,但目前还不支持。您可以关注Github issue,许多人都有与您相同的需求。

目前,我们仅支持设置多作业输出变量,但这仅支持YAML。对于经典编辑器,没有任何计划在发布中添加此功能。

为了解决问题,您可以在阶段之前预定义变量。但是有一件重要的事情是,如果您在一个阶段中更改其值,则新值无法传递到下一个阶段。具有新值的变量的生命周期仅存在于该阶段中。


6
它适用于普通工作,但不适用于部署工作 - 有没有查看的机会? - scorpio
6
最初记录的语法在阶段条件下无效。正确的语法(用于阶段条件)是 stageDependencies.{stageName}.outputs['{jobName}.{stepName}.{variableName}']。有关更多信息,请参见 跨阶段变量的 GitHub 问题 - crimbo
1
我有多个与主管道yml相关联的模板。每个模板都包含一个阶段和至少一个作业。在这种情况下,我注意到的一件事是阶段依赖性只会读取先前模板中的输出变量。 - Shehan Weerasooriya
1
更详细的解释和示例源代码可以在此处找到:https://rollendxavier.medium.com/sharing-variables-between-stages-in-azure-devops-pipelines-59eb8decf3b0 - Raygun
3
@scorpio 对于部署作业,您需要使用略有不同的语法!您需要再次添加作业名称。详情请参见:https://learn.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops#job-to-job-dependencies-across-stages - Sielu
显示剩余3条评论

26

需要提到的重要内容是,在阶段层面上,stageDependencies 不可用于 condition。它适用于作业,但不能直接用于阶段(至少目前如此)。

stages:
- stage: A
  jobs:
  - job: JA
    steps:
    - script: |
        echo "This is job Foo."
        echo "##vso[task.setvariable variable=doThing;isOutput=true]Yes" #The variable doThing is set to true
      name: DetermineResult
    - script: echo $(DetermineResult.doThing)
      name: echovar
  - job: JA_2
    dependsOn: JA
    condition: eq(dependencies.JA.outputs['DetermineResult.doThing'], 'Yes')
    steps:
    - script: |
        echo "This is job Bar."

#stage B runs if DetermineResult task set doThing variable n stage A
- stage: B
  dependsOn: A
  jobs:
  - job: JB
    condition: eq(stageDependencies.A.JA.outputs['DetermineResult.doThing'], 'Yes') #map doThing and check if true
    variables:
      varFromStageA: $[ stageDependencies.A.JA.outputs['DetermineResult.doThing'] ]
    steps:
    - bash: echo "Hello world stage B first job"
    - script: echo $(varFromStageA)

这似乎不再起作用,因为引用应该是 $[ stageDependencies.A.JA.outputs['JA.DetermineResult.doThing'] ] - Rhys Bevilaqua
我正在尝试将它作为一个包含JB的模板的参数传递,但是我无法让它工作。 - undefined
@Roelant,请为此创建一个新的问题。在评论中回复你会很困难。 - undefined

17

2020年5月4日开始提供以下功能:

作业可以访问先前阶段的输出变量:

基于 YAML 的管道现在允许跨阶段使用输出变量。这有助于您将有用的信息,例如“是/否”决策或生成的输出 ID,从一个阶段传递到下一个阶段。还可以使用上一个阶段及其作业的结果(状态)。

输出变量仍由作业内部的步骤生成。阶段引用stageDependencies.stageName.jobName.outputs['stepName.variableName']而不是dependencies.jobName.outputs['stepName.variableName']

注意

默认情况下,管道中的每个阶段都依赖于 YAML 文件中紧接在它之前的阶段。因此,每个阶段都可以使用前一个阶段的输出变量。您可以更改依赖关系图,这也将更改可用的输出变量。例如,如果第三个阶段需要来自第一个阶段的变量,则需要显式声明对第一个阶段的依赖关系。


4
你复制自文档的那个注释帮了我很多。我的依赖项出了问题,但我没有在之前的阶段上添加显式依赖。 - PaulVrugt

17

8
在Azure DevOps版本Dev17.M153.5和代理版本2.153.1上,以下内容适用于舞台条件:
stages:
- stage: A
  jobs:
  - job: JA
    steps:
    - script: |
        echo "This is job Foo."
        echo "##vso[task.setvariable variable=doThing;isOutput=true]Yes" #The variable doThing is set to 'Yes'
      name: DetermineResult

#stage B runs if DetermineResult task set doThing variable on stage A
- stage: B
  dependsOn: A
  condition: eq(dependencies.A.outputs['JA.DetermineResult.doThing'], 'Yes')
  jobs:
  - job: JB
    steps:
    - bash: echo "Hello world stage B first job"

注意: 属性的布局在阶段和作业中是不同的:

dependencies.{阶段名称}.outputs['{作业名称}.{脚本名称}.{变量名称}']

注意: 使用“stageDependencies”表达式时会出现以下错误信息:

加载YAML构建管道时发生错误。无法识别的值:“stageDependencies”。 位于表达式内的位置XX:and(always(), eq(stageDependencies.A.outputs['JA.DetermineResult.doThing'], 'Yes'))。 如需更多帮助,请参阅https://go.microsoft.com/fwlink/?linkid=842996

额外信息:

请参阅以下文档,了解如何访问依赖阶段的状态: link

相应的文档:

要使用来自不同阶段的输出,必须根据您处于阶段级别还是作业级别使用以下语法:

  • 在阶段级别,从其他阶段引用变量的格式为dependencies.STAGE.outputs['JOB.TASK.VARIABLE']。 您可以在条件中使用这些变量。
  • 在作业级别,从其他阶段引用变量的格式为stageDependencies.STAGE.JOB.outputs['TASK.VARIABLE']

注意: 默认情况下,管道中的每个阶段都依赖于YAML文件中紧接其前面的阶段。 如果需要引用不是当前阶段之前的阶段,则可以通过添加“dependsOn”部分来覆盖此自动默认设置。


以上描述仍适用于 Azure DevOps Server 版本 2020 Update 1.1,以及代理版本 2.191.1(包括 Windows 和 Linux 代理)。 - minus one
1
对我来说很好用,Azure DevOps(云)/自托管Linux代理。注意!!!如此答案所述,请确保使用name: DetermineResult而不是displayName: DetermineResult。浪费了30分钟才弄明白... - mh8020

2
是的,这是可能的,您需要像下面这样使用 stageDependencies:
stages:
- stage: Build
  displayName: Build stage
  jobs:
  - job: VersionCheck
    pool:
      vmImage: 'ubuntu-latest'
    displayName: Version Check
    continueOnError: false
    steps:

      - script: |
          echo "##vso[task.setvariable variable=curProjVersion;isOutput=true]1.4.5"
        name: setCurProjVersion
        displayName: "Collect Application Version ID"

- stage: Deploy
  displayName: Deploy stage
  dependsOn: Build
  variables:
    curProjVersion1: $[ stageDependencies.Build.VersionCheck.outputs['setCurProjVersion.curProjVersion'] ]
  jobs:
  - job: 
    steps: 
      - script: |
          echo $(curProjVersion1)

请注意,我已经进行了更改。
$[ dependencies.Build.VersionCheck.outputs['setCurProjVersion.curProjVersion'] ]

$[ stageDependencies.Build.VersionCheck.outputs['setCurProjVersion.curProjVersion'] ]

来源:https://jimferrari.com/2023/01/05/pass-variables-across-jobs-and-stages-in-azure-devops-pipelines/


如果我有 - template: jobs/checkprojectype.yaml@devops 而不是 - job: VersionCheck,我该怎么做? - ITBYD
我猜您是在尝试将同一阶段的作业传递给作业?可以请您发送完整的代码片段吗? - jfdevops

0

链接很棒,但它们不应该是你的答案中唯一的信息。 - derekbaker783

0

重要通知至解决方案:

只适用于1直接后续阶段。

假设您有:

  • 阶段A - 设置变量X
  • 阶段B - 通过阶段依赖消耗变量X - 工作正常
  • 阶段C - 除非指定dependsOn阶段A 阶段B,否则无法消耗变量X。

这是我最近遇到的一个坑,阶段C消耗的变量X的值为空。当您指定dependsOn时,您不能只添加阶段A,否则它会变成并行阶段(什么?),所以您需要添加同时阶段A和阶段B。但这样的流水线看起来很傻。

因此,我建议使用构件而不是这种方式。我认为这是一种更可靠的解决方案。


-1
你可以定义一个全局变量,并使用Powershell将舞台变量的值赋给全局变量。
Write-Output ("##vso[task.setvariable variable=globalVar;]$stageVar")

全局变量可以在yaml本身或变量组中定义。使用空值初始化变量。

例如,yaml:

  variables:
    globalVar: ''

谢谢!我会试一下。 - zooes
嘿@zooes,那个问题解决了吗?我在GitHub上找到了这个讨论,似乎唯一的方法是将其保存到AKV,或者在某个地方使用DB /文件。https://github.com/microsoft/azure-pipelines-tasks/issues/4743 - bla9x
3
不行,因为看起来 MS-ADO 不允许在不同阶段之间共享变量。你说得对,唯一的共享方式是使用 AKV/DB/File。 - zooes
3
它为每个作业创建快照。因此,在一个阶段中的globalVar与不同作业中的globalVar是不同的。你可以使用这种机制在所有阶段中传递相同的值,但不能将值从一个阶段传递到另一个阶段。为此,您应该使用“输出变量”,这些变量可用于在各个阶段共享值(在编写此注释时)。 - Krzysztof Madej

-3

您可以在定义触发器之后和定义阶段之前定义变量。

trigger:
- master
  variables:
    VarA: aaaaa
    VarB: bbbbb
stages:
- stage: Build  
  jobs:
  - job: Build
    pool:
      vmImage: 'vs2017-win2016'

1
从文档中看到,变量声明应该在根级别的文件顶部。当我将其添加到触发器中时,我的VSCode扩展会抛出错误“意外属性变量”。 - Michael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接