You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于提交记录递增Azure Pipelines版本号的实现问题

实现双仓库驱动的Azure DevOps流水线版本号规则

问题背景

我有一个关联两个仓库的Azure DevOps流水线:

  • code仓库:存储应用代码
  • config仓库:存储环境变量配置文件

需要实现以下版本号规则,格式为{major}.{minor}.{codeVersion}.{configVersion}:

  • major和minor由开发者手动指定
  • 当major.minor不变时,code仓库有新提交则自动递增codeVersion
  • 当major.minor.codeVersion不变时,config仓库有新提交则自动递增configVersion
  • 无代码/配置变更时,重复运行流水线生成相同版本号

示例版本序列:

  • MyApp 0.1.0.0(首次运行)
  • MyApp 0.1.1.0(code仓库有提交)
  • MyApp 0.1.1.1(config仓库有提交)
  • MyApp 0.1.2.0(code仓库有提交)
  • MyApp 0.1.2.0(无变更重复运行)

原方案使用counter函数,但每次运行流水线不管code仓库是否有提交,codeVersion都会递增,导致configVersion始终为0,无法满足需求。核心问题是counter函数只要输入键不变就会自动递增,无法关联仓库提交的变更。

解决方案

核心思路是基于仓库提交哈希的变化来触发版本号递增,通过查询历史流水线的运行记录,对比当前与上一次的提交哈希,决定是否递增对应版本段。

完整流水线实现

name: '$(Build.DefinitionName)-$(major).$(minor).$(codeVersion).$(configVersion)'

variables:
  - name: major
    value: 0
  - name: minor
    value: 1
  # 声明后续步骤会赋值的变量
  - name: codeCommitHash
    value: ''
  - name: codeVersion
    value: ''
  - name: configCommitHash
    value: ''
  - name: configVersion
    value: ''

resources:
  repositories:
    - repository: configRepo
      type: git
      name: YourProject/ConfigRepo # 替换为你的config仓库项目路径

stages:
  - stage: CalculateVersion
    displayName: 计算版本号
    jobs:
      - job: VersionCalculationJob
        displayName: 生成版本组件
        steps:
          # 拉取code仓库(默认仓库)
          - checkout: self
            fetchDepth: 1
            displayName: 拉取代码仓库

          # 拉取config仓库
          - checkout: configRepo
            fetchDepth: 1
            displayName: 拉取配置仓库

          # 计算版本号的PowerShell脚本
          - powershell: |
              $orgUrl = "$(System.TeamFoundationCollectionUri)"
              $projectName = "$(System.TeamProject)"
              $definitionId = "$(System.DefinitionId)"
              $pat = "$(System.AccessToken)"

              # 构建API请求头
              $headers = @{
                  Authorization = "Bearer $pat"
              }

              # 查询最近一次成功的流水线运行记录
              $runsUrl = "$orgUrl/$projectName/_apis/build/builds?definitions=$definitionId&statusFilter=completed&resultFilter=succeeded&`$top=1&api-version=7.1-preview.7"
              $response = Invoke-RestMethod -Uri $runsUrl -Headers $headers -Method Get

              # 初始化历史变量
              $prevCodeCommit = ""
              $prevCodeVersion = 0
              $prevConfigCommit = ""
              $prevConfigVersion = 0

              # 获取上一次运行的变量值
              if ($response.count -gt 0) {
                  $prevRun = $response.value[0]
                  $varsUrl = "$orgUrl/$projectName/_apis/build/builds/$($prevRun.id)/variables?api-version=7.1-preview.1"
                  $prevVars = Invoke-RestMethod -Uri $varsUrl -Headers $headers -Method Get

                  $prevCodeCommit = $prevVars.value.codeCommitHash.value
                  $prevCodeVersion = [int]$prevVars.value.codeVersion.value
                  $prevConfigCommit = $prevVars.value.configCommitHash.value
                  $prevConfigVersion = [int]$prevVars.value.configVersion.value
              }

              # 获取当前仓库的提交哈希
              $currentCodeCommit = $(git rev-parse HEAD)
              $currentConfigCommit = $(git -C "$(Build.SourcesDirectory)/configRepo" rev-parse HEAD)

              # 计算codeVersion:代码提交变更则递增,否则沿用历史值
              $currentCodeVersion = $prevCodeVersion
              if ($currentCodeCommit -ne $prevCodeCommit) {
                  $currentCodeVersion = $prevCodeVersion + 1
              }

              # 计算configVersion:代码版本不变且配置提交变更则递增,否则重置为0或沿用历史值
              $currentConfigVersion = 0
              if ($currentCodeVersion -eq $prevCodeVersion) {
                  if ($currentConfigCommit -ne $prevConfigCommit) {
                      $currentConfigVersion = $prevConfigVersion + 1
                  } else {
                      $currentConfigVersion = $prevConfigVersion
                  }
              }

              # 设置当前流水线的输出变量
              Write-Host "##vso[task.setvariable variable=codeCommitHash;isOutput=true]$currentCodeCommit"
              Write-Host "##vso[task.setvariable variable=codeVersion;isOutput=true]$currentCodeVersion"
              Write-Host "##vso[task.setvariable variable=configCommitHash;isOutput=true]$currentConfigCommit"
              Write-Host "##vso[task.setvariable variable=configVersion;isOutput=true]$currentConfigVersion"
            displayName: 计算版本号组件
            name: VersionCalc

  - stage: BuildAndDeploy
    displayName: 构建与部署
    dependsOn: CalculateVersion
    variables:
      # 传递版本变量到当前阶段
      codeVersion: $[ stageDependencies.CalculateVersion.VersionCalculationJob.outputs['VersionCalc.codeVersion'] ]
      configVersion: $[ stageDependencies.CalculateVersion.VersionCalculationJob.outputs['VersionCalc.configVersion'] ]
      codeCommitHash: $[ stageDependencies.CalculateVersion.VersionCalculationJob.outputs['VersionCalc.codeCommitHash'] ]
      configCommitHash: $[ stageDependencies.CalculateVersion.VersionCalculationJob.outputs['VersionCalc.configCommitHash'] ]
    jobs:
      - job: BuildJob
        displayName: 构建应用
        steps:
          - script: |
              echo "当前应用版本:$(major).$(minor).$(codeVersion).$(configVersion)"
              echo "代码提交哈希:$(codeCommitHash)"
              echo "配置提交哈希:$(configCommitHash)"
            displayName: 打印版本信息
          # 这里添加你的构建、打包、部署步骤

关键逻辑说明

  1. 提交哈希获取:通过git rev-parse HEAD获取两个仓库的最新提交ID,作为判断变更的依据。
  2. 历史流水线查询:调用Azure DevOps REST API获取最近一次成功运行的流水线变量,包括历史提交哈希和版本号。
  3. 版本号计算:
    • codeVersion:仅当当前code提交哈希与历史不同时递增,否则沿用历史值。
    • configVersion:若codeVersion未变化,且config提交哈希与历史不同则递增;若codeVersion变化,直接重置为0。
  4. 变量传递:通过阶段输出变量将计算好的版本号传递到构建部署阶段。

权限说明

确保流水线的服务账号拥有读取构建记录的权限,否则无法查询历史流水线变量。可以在项目设置的权限管理中,为构建服务账号分配对应的权限。

内容的提问来源于stack exchange,提问作者Colin Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 18:17:02