安装 scanner

用 .NET global tool:

dotnet tool install --global dotnet-sonarscanner --version 10.4.0

验证:

dotnet sonarscanner --version
scanner 版本应与 SonarQube Server 兼容;SonarQube 10.x 推荐 scanner 9+;SonarQube 9.x 用 scanner 5+。

三步分析:begin / build / end

三个命令把扫描嵌进 build 流程:

# 1. begin:准备分析环境,告诉 SonarQube "我要扫这个项目"
dotnet sonarscanner begin \
  /k:"my-dotnet-app" \
  /d:sonar.host.url="https://sonar.example.com" \
  /d:sonar.token="<REPLACE_WITH_USER_TOKEN>"

# 2. build:正常编译 / 测试
dotnet build -c Release
dotnet test  -c Release --no-build

# 3. end:收尾,上传报告
dotnet sonarscanner end /d:sonar.token="<REPLACE_WITH_USER_TOKEN>"

三个参数含义:

  • /k:"project-key":SonarQube 中项目的唯一标识
  • sonar.host.url:SonarQube Server 地址
  • sonar.token:用户的 token(在 SonarQube 个人安全页面生成)
token 不要写死在 CI 脚本里。用 CI 的凭据存储(GitHub Actions Secrets / GitLab CI Variables / Jenkins Credentials)。

全局配置(可选)

如果不想每次都指定 host/token,可以建一份 SonarQube.Analysis.xml:

<?xml version="1.0" encoding="utf-8"?>
<SonarQubeAnalysisProperties xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                             xmlns="http://www.sonarsource.com/msbuild/integration/2015/1">
  <Property Name="sonar.host.url">https://sonar.example.com</Property>
  <Property Name="sonar.token"><REPLACE_WITH_USER_TOKEN></Property>
</SonarQubeAnalysisProperties>

把文件放在 %LOCALAPPDATA%\SonarQube\SonarQube.Analysis.xml(Windows)或 ~/.sonar/SonarQube.Analysis.xml(Linux),scanner 会自动读。

覆盖率

用 coverlet 跑测试时同时生成 OpenCover 报告,scanner 自动收:

dotnet test -c Release \
  /p:CollectCoverage=true \
  /p:CoverletOutputFormat=opencover \
  /p:CoverletOutput=./TestResults/ \
  --logger "trx;LogFileName=test.trx"

测试结果(trx)与覆盖率(opencover.xml)会随 dotnet sonarscanner end 一同上传。

Web 端即可看到行 / 分支 / 测试覆盖率的细分。

排除规则

代码层排除

// .csproj
<PropertyGroup>
    <SonarQubeExclude>true</SonarQubeExclude>
</PropertyGroup>

全局规则

推荐放 sonar-project.properties 与项目根目录:

# sonar-project.properties
sonar.exclusions=**/Migrations/**,**/bin/**,**/obj/**
sonar.coverage.exclusions=**/Migrations/**,**/Program.cs
sonar.cpd.exclusions=**/Generated/**
  • sonar.exclusions:不分析
  • sonar.coverage.exclusions:不算覆盖率(仍在分析)
  • sonar.cpd.exclusions:不参与重复代码检测

自定义规则参数

在 .csproj 里给某个项目写特定参数:

<ItemGroup>
  <SonarQubeSetting Include="sonar.stylecop.projectFilePath">
    <Value>$(MSBuildProjectFullPath)</Value>
  </SonarQubeSetting>
</ItemGroup>

CI 集成示例

GitHub Actions

jobs:
  analyze:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with: { dotnet-version: '8.0.x' }

      - name: Install scanner
        run: dotnet tool install --global dotnet-sonarscanner --version 10.4.0

      - name: Begin
        run: |
          dotnet sonarscanner begin \
            /k:"my-dotnet-app" \
            /d:sonar.host.url="${{ secrets.SONAR_HOST }}" \
            /d:sonar.token="${{ secrets.SONAR_TOKEN }}"

      - name: Build & Test
        run: |
          dotnet build -c Release
          dotnet test -c Release --no-build \
            /p:CollectCoverage=true \
            /p:CoverletOutputFormat=opencover \
            /p:CoverletOutput=./TestResults/

      - name: End
        run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"

Jenkinsfile

stage('SonarQube Analysis') {
    steps {
        withCredentials([string(credentialsId: 'sonar-token', variable: 'SONAR_TOKEN')]) {
            sh """
              dotnet sonarscanner begin /k:'my-dotnet-app' \
                /d:sonar.host.url='${SONAR_HOST}' \
                /d:sonar.token='${SONAR_TOKEN}'
              dotnet build -c Release
              dotnet test -c Release --no-build \
                /p:CollectCoverage=true \
                /p:CoverletOutputFormat=opencover \
                /p:CoverletOutput=./TestResults/
              dotnet sonarscanner end /d:sonar.token='${SONAR_TOKEN}'
            """
        }
    }
}

使用准则

  1. Token 必须走 CI 凭据存储,禁止写进代码库。
  2. 迁移生成代码(**/Migrations/**、**/obj/**)加入排除列表。
  3. 覆盖率 ≠ 质量:90% 覆盖也能写出大量"测了空方法"的测试,SonarQube 的真正价值在静态规则。
  4. 质量门禁(Quality Gate)建议先放宽(覆盖率 60%+),逐步抬高,避免一刀切打击团队积极性。
参考资料