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

Azure Pipeline中PR的Sonar扫描无法识别.NET特性分支新代码

问题

使用Azure Pipeline对.NET代码的Pull Request执行Sonar扫描时,扫描无法识别特性分支中的新增代码,显示零覆盖率且无新增代码记录,需要实现PR触发流水线中新增代码的分析。当前Azure Pipeline配置如下:

- script: |   
    git config --global http.https://dev.azure.com/<company>/.extraheader "AUTHORIZATION: bearer $(System.AccessToken)"
    
    # Now fetch the PR source branch using the token
    git fetch origin $(System.PullRequest.SourceBranch):$(System.PullRequest.SourceBranch)
    git checkout $(System.PullRequest.SourceBranch)
  displayName: 'Fetch and Checkout PR Source Branch'
  env:
    # This exposes the system access token to the script
    SYSTEM_ACCESSTOKEN: $(System.AccessToken)

- script: |
    git checkout $(System.PullRequest.SourceBranch)
  displayName: 'Checkout PR Source Branch'

- task: DotNetCoreCLI@2
  displayName: New Manifest for tool
  inputs:
    command: custom
    custom: 'new '
    arguments: tool-manifest

- task: PowerShell@2
  displayName: Set the prereleaseVersionNumber variable value
  inputs:
    targetType: 'inline'
    script: |
      [string] $dateTime = (Get-Date -Format 'yyyyMMddTHHmmss')
      [string] $prereleaseVersionNumber = "$(stableVersionNumber)-$(Build.SourceVersion)"
      Write-Host "Setting the prerelease version number variable to '$prereleaseVersionNumber'."
      Write-Host "##vso[task.setvariable variable=prereleaseVersionNumber]$prereleaseVersionNumber"

- task: PowerShell@2
  displayName: Set the versionNumber to the stable or prerelease version number based on if the 'main' branch is being built or not
  inputs:
    targetType: 'inline'
    script: |
      [bool] $isMainBranch = $$(isMainBranch)
      [string] $versionNumber = "$(prereleaseVersionNumber)"
      if ($isMainBranch)
      {
        $versionNumber = "$(stableVersionNumber)"
      }
      Write-Host "Setting the version number to use to '$versionNumber'."
      Write-Host "##vso[task.setvariable variable=versionNumber]$versionNumber"

- task: PowerShell@2
  displayName: Set the name of the build (i.e. the Build.BuildNumber)
  inputs:
    targetType: 'inline'
    script: |
      [string] $buildName = "$(versionNumber)_$(Build.SourceBranchName)"
      Write-Host "Setting the name of the build to '$buildName'."
      Write-Host "##vso[build.updatebuildnumber]$buildName"
- task: NuGetToolInstaller@1
  displayName: 'Install NuGet 5.8'
  inputs:
    versionSpec: '5.8.x'
    
- task: NuGetCommand@2
  displayName: 'Restore packages using NuGet'
  inputs:
    restoreSolution: '**/*.csproj'
     
- task: UseDotNet@2
  displayName: Use .NET 6.0
  inputs:
    packageType: 'sdk'
    version: '6.0.x'

- task: DotNetCoreCLI@2
  displayName: 'Add Coverlet.msbuild package'
  inputs:
    command: 'custom'
    custom: 'add'
    projects: '$(Build.SourcesDirectory)/Virtue/Virtue.API/Virtue.API.csproj'
    arguments: 'package coverlet.msbuild'

- task: DotNetCoreCLI@2
  displayName: 'dotnet restore Virtue.API'
  inputs:
    command: 'restore'
    projects: '$(Build.SourcesDirectory)/Virtue/Virtue.API/Virtue.API.csproj'
    includeNuGetOrg: true
    noCache: true

- task: SonarCloudPrepare@1
  inputs:
    SonarCloud: 'SonarCloud Service Connection'
    organization: '<company_name>'
    scannerMode: 'MSBuild'
    projectKey: 'dotnet'
    projectName: 'Dotnet'
    extraProperties: |
      sonar.projectBaseDir=$(Build.SourcesDirectory)
      sonar.exclusions=**/README.md,**/obj/**,**/*.dll
      sonar.inclusions=**/*.csproj
      sonar.cs.vscoveragexml.reportsPaths=$(Build.SourcesDirectory)/**/coverage.xml
      sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/**/coverage.opencover.xml
      sonar.cs.vstest.reportsPaths=$(Agent.TempDirectory)/*.trx
      sonar.pullrequest.key=$(System.PullRequest.PullRequestId)
      sonar.pullrequest.branch=$(System.PullRequest.SourceBranch)
      sonar.pullrequest.base=$(System.PullRequest.TargetBranch)

- task: DotNetCoreCLI@2
  inputs:
    command: 'build'
    projects: '**/*.csproj'
    arguments: '--configuration $(buildConfiguration)'

- task: DotNetCoreCLI@2
  displayName: 'Test ...'
  inputs:
    command: test
    projects: '**/*Tests/*.csproj'
    arguments: '--configuration $(BuildConfiguration) /p:CollectCoverage=true /p:CoverletOutputFormat=opencover --logger trx'
    testRunTitle: 'dotnet test'

- task: PublishCodeCoverageResults@1
  inputs:
    codeCoverageTool: 'Cobertura'
    summaryFileLocation: '$(Build.SourcesDirectory)/**/coverage.opencover.xml'
    reportDirectory: '$(Build.SourcesDirectory)/**/coverage/'

- script: |
    dotnet tool install -g dotnet-reportgenerator-globaltool
    reportgenerator -reports:$(Build.SourcesDirectory)/**/coverage.opencover.xml -targetdir:$(Build.SourcesDirectory)/CoverageReport -reporttypes:"HtmlInline;Cobertura"
  displayName: 'Generate Coverage Report'
  
- task: PublishTestResults@2
  displayName: "Publish Test Results"
  inputs:
    testResultsFormat: VSTest
    testResultsFiles: "$(Build.SourcesDirectory)/**.*trx"
    mergeTestResults: true
    condition: succeededOrFailed()

- task: SonarCloudAnalyze@1
  inputs:
    sonar.analysis.level: 'DOTNET'

- task: SonarCloudPublish@1
  inputs:
    pollingTimeoutSec: '300'

修复方案

1. 修正Git分支拉取逻辑

当前流水线重复执行分支检出,且未拉取PR目标分支,导致Sonar无法对比分支差异。调整Git步骤:

- script: |   
    git config --global http.https://dev.azure.com/<company>/.extraheader "AUTHORIZATION: bearer $(System.AccessToken)"
    # 拉取PR源分支和目标分支,用于Sonar差异分析
    git fetch origin $(System.PullRequest.SourceBranch):$(System.PullRequest.SourceBranch)
    git fetch origin $(System.PullRequest.TargetBranch):$(System.PullRequest.TargetBranch)
    git checkout $(System.PullRequest.SourceBranch)
  displayName: 'Fetch and Checkout PR Branches'
  env:
    SYSTEM_ACCESSTOKEN: $(System.AccessToken)

移除重复的Checkout PR Source Branch步骤,避免分支切换冲突。

2. 调整SonarCloudPrepare核心配置

  • sonar.inclusions错误设置为项目文件,改为扫描源代码文件
  • 使用分支名称而非完整路径,避免格式不兼容
  • 移除未使用的覆盖率报告路径配置
- task: SonarCloudPrepare@1
  inputs:
    SonarCloud: 'SonarCloud Service Connection'
    organization: '<company_name>'
    scannerMode: 'MSBuild'
    projectKey: 'dotnet'
    projectName: 'Dotnet'
    extraProperties: |
      sonar.projectBaseDir=$(Build.SourcesDirectory)
      sonar.exclusions=**/README.md,**/obj/**,**/*.dll
      sonar.inclusions=**/*.cs  # 替换原**/*.csproj,确保扫描源代码
      sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/**/coverage.opencover.xml
      sonar.cs.vstest.reportsPaths=$(Agent.TempDirectory)/*.trx
      sonar.pullrequest.key=$(System.PullRequest.PullRequestId)
      sonar.pullrequest.branch=$(System.PullRequest.SourceBranchName)  # 使用分支名称
      sonar.pullrequest.base=$(System.PullRequest.TargetBranchName)    # 使用分支名称

3. 确保测试覆盖率报告路径可被Sonar识别

在测试步骤中指定覆盖率报告输出目录,避免Sonar无法定位文件:

- task: DotNetCoreCLI@2
  displayName: 'Test ...'
  inputs:
    command: test
    projects: '**/*Tests/*.csproj'
    arguments: '--configuration $(BuildConfiguration) /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput=$(Build.SourcesDirectory)/Coverage/ --logger trx'
    testRunTitle: 'dotnet test'

4. 移除冗余步骤

  • 删除重复的dotnet restore Virtue.API步骤,全局NuGet Restore已覆盖所有项目
  • 若未使用工具清单,删除New Manifest for tool步骤

内容的提问来源于stack exchange,提问作者Sneha Reddy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 14:00:56