Azure DevOps部署流水线触发源分支识别异常求助
项目结构与流水线逻辑
MyProject下有两个Git仓库:
- MyProject.Web:Angular应用的.NET Core后端代码仓库,存放Docker-Build和Kubernetes-Deploy流水线的YAML文件
- MyProject.UI:关联的Angular前端项目仓库
触发规则:向两个仓库指定分支提交的Pull Request完成后,会触发Docker-Build构建流水线,构建完成后自动触发Kubernetes-Deploy部署流水线。当前构建流程正常,但触发源为MyProject.UI的release分支时,部署行为不符合预期。
部署规则说明
现有Dev、QA、Test三个部署环境,分支与环境的对应规则:
- 构建流水线触发条件:MyProject.Web的master/release/*分支,或MyProject.UI的main/release/*分支的PR完成;构建时会自动匹配对应分支(如master对应main,release/1.0对应release/1.0)
- 部署流水线规则:
- master/main分支:部署至Dev,经审批后部署至QA,禁止部署至Test
- release/*分支:经审批后部署至Test,禁止部署至Dev/QA
触发场景预期与实际差异
| 触发项目 | 触发分支 | 待构建MyProject.Web分支 | 待构建MyProject.UI分支 | 待部署MyProject.Web分支 | 预期部署环境 | 实际情况 |
|---|---|---|---|---|---|---|
| MyProject.Web | master | master | main | master | Dev、QA | 符合预期 |
| MyProject.Web | release/1.0 | release/1.0 | release/1.0 | release/1.0 | Test | 符合预期 |
| MyProject.UI | main | master | main | master | Dev、QA | 符合预期 |
| MyProject.UI | release/1.0 | release/1.0 | release/1.0 | release/1.0(预期) | Test(预期) | 实际拉取master分支,部署至Dev/QA |
问题核心
构建日志显示所有场景下分支选择均正确,但当触发源为MyProject.UI的release/1.0分支时,部署流水线错误关联MyProject.Web的master分支构建产物,部署至Dev/QA而非Test环境。
涉及流水线配置代码
1. docker-build.yml(MyProject.Web仓库)
# This is the YAML for the "Docker-Build" pipeline. It resides in the MyProject.Web repository. resources: repositories: - repository: self type: git name: MyProject.Web trigger: - master - release/* - repository: UiRepo type: git name: MyProject.UI trigger: - main - release/* - repository: PipelineRepo type: git name: MyProject.Pipeline # [variables and pool omitted] steps: - ${{ if in(variables['Build.SourceBranchName'], 'master', 'main') }}: - checkout: git://MyProject/MyProject.Web@refs/heads/master - checkout: git://MyProject/MyProject.UI@refs/heads/main - ${{ if not(in(variables['Build.SourceBranchName'], 'master', 'main')) }}: - checkout: git://MyProject/MyProject.Web@${{ variables['Build.SourceBranch'] }} - checkout: git://MyProject/MyProject.UI@${{ variables['Build.SourceBranch'] }} # [some details omitted] - template: build-pipeline-template.yml@PipelineRepo parameters: relativeSolutionPath: MyProject.Web relativeProjectPath: MyProject.Web/MyProject.Web # [other parameters omitted]
2. kubernetes-deploy.yml(MyProject.Web仓库)
repositories: - repository: PipelineRepo type: git name: MyProject.Pipeline pipelines: - pipeline: buildPipeline source: 'Docker-Build' trigger: true trigger: - none # [pool omitted] stages: - template: deploy-pipeline-template.yml@PipelineRepo parameters: buildPipelineId: '123' # I can probably replace '123' with variables['resources.pipeline.buildPipeline.PipelineID'] or the # same thing with another one of Azure DevOps' multitudinous syntaxes, but I haven't tested it yet.
3. deploy-pipeline-template.yml(MyProject.Pipeline仓库)
# This is a pipeline template that resides in the MyProject.Pipeline repository. parameters: - name: buildPipelineId displayName: ID of the pipeline that produced the artifacts to download type: string stages: - template: deploy-pipeline-job-template.yml parameters: stageName: Development canRun: and(not(or(failed(), canceled())), in(variables['resources.pipeline.buildPipeline.sourceBranch'], 'refs/heads/master', 'refs/heads/main')) variableGroup: myproject-web-variables-dev buildPipelineId: ${{ parameters.buildPipelineId }} devOpsEnvironment: myproject-dev kubernetesServiceConnection: myproject-dev-kubeconfig - template: deploy-pipeline-job-template.yml parameters: stageName: QA canRun: and(not(or(failed(), canceled())), in(variables['resources.pipeline.buildPipeline.sourceBranch'], 'refs/heads/master', 'refs/heads/main')) variableGroup: myproject-web-variables-qa buildPipelineId: ${{ parameters.buildPipelineId }} devOpsEnvironment: myproject-qa kubernetesServiceConnection: myproject-dev-kubeconfig - template: deploy-pipeline-job-template.yml parameters: stageName: Test canRun: and(not(or(failed(), canceled())), startsWith(variables['resources.pipeline.buildPipeline.sourceBranch'], 'refs/heads/release/')) variableGroup: myproject-web-variables-test buildPipelineId: ${{ parameters.buildPipelineId }} devOpsEnvironment: myproject-test kubernetesServiceConnection: myproject-dev-kubeconfig
问题原因分析
核心问题出在部署流水线的环境判断逻辑:
部署模板中使用resources.pipeline.buildPipeline.sourceBranch来识别触发分支,但这个变量指向的是Docker-Build流水线定义所在的分支(即MyProject.Web的master分支,因为流水线YAML存放在该分支),而不是实际触发构建的源分支(MyProject.UI的release/1.0)。当触发源是MyProject.UI的release分支时,这个变量仍会返回MyProject.Web的master分支路径,导致部署逻辑错误匹配到Dev/QA环境。
另外,kubernetes-deploy.yml中的buildPipelineId硬编码为123,无法动态关联正确的构建流水线实例,也可能导致产物匹配错误。
解决方案
步骤1:在构建流水线中传递触发源分支信息
修改docker-build.yml,添加步骤将触发源分支保存为流水线输出变量,确保后续部署流水线能获取该值:
# 在docker-build.yml的steps中添加(建议放在checkout步骤之后) - script: echo "##vso[task.setvariable variable=triggerSourceBranch;isOutput=true]$(Build.SourceBranch)" name: setTriggerBranchVar
步骤2:修正部署流水线的buildPipelineId
修改kubernetes-deploy.yml,将硬编码的123替换为动态变量,确保关联正确的构建实例:
stages: - template: deploy-pipeline-template.yml@PipelineRepo parameters: buildPipelineId: $(resources.pipeline.buildPipeline.pipelineId)
步骤3:修改部署模板的环境判断逻辑
更新deploy-pipeline-template.yml,使用构建流水线传递的triggerSourceBranch变量替代原有的分支判断变量:
stages: - template: deploy-pipeline-job-template.yml parameters: stageName: Development canRun: and(not(or(failed(), canceled())), in(variables['resources.pipeline.buildPipeline.outputs['setTriggerBranchVar.triggerSourceBranch']'], 'refs/heads/master', 'refs/heads/main')) variableGroup: myproject-web-variables-dev buildPipelineId: ${{ parameters.buildPipelineId }} devOpsEnvironment: myproject-dev kubernetesServiceConnection: myproject-dev-kubeconfig - template: deploy-pipeline-job-template.yml parameters: stageName: QA canRun: and(not(or(failed(), canceled())), in(variables['resources.pipeline.buildPipeline.outputs['setTriggerBranchVar.triggerSourceBranch']'], 'refs/heads/master', 'refs/heads/main')) variableGroup: myproject-web-variables-qa buildPipelineId: ${{ parameters.buildPipelineId }} devOpsEnvironment: myproject-qa kubernetesServiceConnection: myproject-dev-kubeconfig - template: deploy-pipeline-job-template.yml parameters: stageName: Test canRun: and(not(or(failed(), canceled())), startsWith(variables['resources.pipeline.buildPipeline.outputs['setTriggerBranchVar.triggerSourceBranch']'], 'refs/heads/release/')) variableGroup: myproject-web-variables-test buildPipelineId: ${{ parameters.buildPipelineId }} devOpsEnvironment: myproject-test kubernetesServiceConnection: myproject-dev-kubeconfig
补充说明
- 如果部署流水线无法直接引用构建流水线的输出变量,可以在构建流水线中将
triggerSourceBranch设置为流水线全局变量(添加isReadOnly=true参数),或者附加到构建工件的元数据中 - 验证时可在部署流水线中添加打印变量的步骤,确认
triggerSourceBranch的值是否正确对应触发源的分支路径
内容的提问来源于stack exchange,提问作者Green Grasso Holm

