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

Azure DevOps下.NET MAUI应用自动版本化:Patch号重置与格式问题

解决方案:Azure DevOps管道中.NET MAUI应用版本自动管理

问题原因分析

  1. 计数器不重置:你定义buildNumber时使用的是初始固定值'will be filled in later'作为计数器键,这个值不会随csproj中的版本修改而变化,因此计数器始终基于同一个键,自然不会重置。
  2. 版本号带空格:大概率是从csproj读取的ApplicationDisplayVersion包含前后空格,或者脚本拼接时未做格式处理。

方案一:使用管道模板预提取版本号(推荐)

利用Azure DevOps管道模板在初始化阶段从csproj中提取Major.Minor版本,以此作为计数器的键,实现Patch号随主/次版本变更自动重置。

  1. 创建版本管理模板文件version-template.yml:
parameters:
  # 传入csproj文件的路径
  csprojPath: ''

variables:
  # 从csproj中提取Major.Minor版本(自动去除前后空格)
  - name: majorMinorVersion
    value: $[replace(readFile('${{ parameters.csprojPath }}'), '(?s).*<ApplicationDisplayVersion>\s*(.*?)\s*</ApplicationDisplayVersion>.*', '$1')]
  # 以Major.Minor为计数器键,主/次版本变更时Patch重置为0
  - name: patchVersion
    value: $[counter(variables['majorMinorVersion'], 0)]
  # 组合完整的Display Version
  - name: fullDisplayVersion
    value: '${{ variables.majorMinorVersion }}.${{ variables.patchVersion }}'
  1. 在主管道中引用该模板:
# 引用当前仓库的模板
resources:
  repositories:
    - repository: self

extends:
  template: version-template.yml
  parameters:
    csprojPath: './MyApp/MyApp.csproj'

variables:
  # 保留原有的Version Code计数器
  - name: versionCode
    value: $[counter('versionCode', 1)]

steps:
# 验证生成的版本号
- task: PowerShell@2
  inputs:
    targetType: 'inline'
    script: |
      Write-Host "完整Display Version: $(fullDisplayVersion)"
      Write-Host "Version Code: $(versionCode)"

# 后续构建、签名、发布步骤,传入自动生成的版本号
- task: DotNetCoreCLI@2
  inputs:
    command: 'build'
    projects: './MyApp/MyApp.csproj'
    arguments: '/p:ApplicationDisplayVersion=$(fullDisplayVersion) /p:ApplicationVersion=$(versionCode)'

方案二:PowerShell脚本手动维护Patch号(无需模板)

如果不想使用模板,可以通过脚本查询历史构建记录,根据Major.Minor版本自动计算Patch号:

variables:
  - name: versionCode
    value: $[counter('versionCode', 1)]

steps:
# 第一步:从csproj提取Major.Minor版本(去除空格)
- task: PowerShell@2
  name: getMajorMinor
  inputs:
    targetType: 'inline'
    script: |
      $xml = [Xml](Get-Content ./MyApp/MyApp.csproj)
      $majorMinor = $xml.Project.PropertyGroup.ApplicationDisplayVersion.Trim()
      Write-Host "##vso[task.setvariable variable=majorMinor;isoutput=true]$majorMinor"

# 第二步:查询历史构建,计算Patch号
- task: PowerShell@2
  name: calculatePatch
  inputs:
    targetType: 'inline'
    script: |
      # 获取当前流水线的历史构建记录
      $builds = az pipelines build list --project $(System.TeamProject) --definition-name $(Build.DefinitionName) --query "[?contains(tags, 'MajorMinor:$(getMajorMinor.majorMinor)')]" | ConvertFrom-Json
      
      # 提取历史Patch号,最大值加1;无历史则设为0
      if ($builds.Count -gt 0) {
        $maxPatch = ($builds | ForEach-Object { $_.tags -match 'Patch:(\d+)'; $matches[1] } | Measure-Object -Maximum).Maximum
        $patch = [int]$maxPatch + 1
      } else {
        $patch = 0
      }
      
      # 组合完整版本号并设置为输出变量
      $fullVersion = "$(getMajorMinor.majorMinor).$patch"
      Write-Host "##vso[task.setvariable variable=fullDisplayVersion;isoutput=true]$fullVersion"
      
      # 给当前构建打标签,方便后续查询
      Write-Host "##vso[build.addbuildtag]MajorMinor:$(getMajorMinor.majorMinor)"
      Write-Host "##vso[build.addbuildtag]Patch:$patch"

# 验证版本号
- task: PowerShell@2
  inputs:
    targetType: 'inline'
    script: |
      Write-Host "完整Display Version: $(calculatePatch.fullDisplayVersion)"
      Write-Host "Version Code: $(versionCode)"

# 后续构建步骤传入版本号
- task: DotNetCoreCLI@2
  inputs:
    command: 'build'
    projects: './MyApp/MyApp.csproj'
    arguments: '/p:ApplicationDisplayVersion=$(calculatePatch.fullDisplayVersion) /p:ApplicationVersion=$(versionCode)'

版本号空格问题的额外处理

无论使用哪种方案,都需要确保从csproj读取的版本号无多余空格:

  • 在PowerShell脚本中添加.Trim()方法去除前后空格;
  • 检查csproj文件中ApplicationDisplayVersion标签的内容,确保格式为X.X(无空格)。

内容的提问来源于stack exchange,提问作者Mathias Peene

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 20:11:08