安装 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}'
"""
}
}
}
使用准则
- Token 必须走 CI 凭据存储,禁止写进代码库。
- 迁移生成代码(
**/Migrations/**、**/obj/**)加入排除列表。 - 覆盖率 ≠ 质量:90% 覆盖也能写出大量"测了空方法"的测试,SonarQube 的真正价值在静态规则。
- 质量门禁(Quality Gate)建议先放宽(覆盖率 60%+),逐步抬高,避免一刀切打击团队积极性。