From 8def71468389278df275aeda41397c38e2667e4f Mon Sep 17 00:00:00 2001 From: Thom Lamb Date: Wed, 27 May 2026 15:30:47 -0500 Subject: [PATCH] ci(sonar): collect and upload OpenCover coverage from CI scan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Gitea Actions sonarqube workflow was running `dotnet build` and sonarscanner begin/end without ever invoking `dotnet test`, so Sonar was reporting coverage = 0% on the server despite a 1180-test suite already exercising the code locally. `scan-sonar.ps1` (the local equivalent) has had the coverage flags + test step the whole time — this aligns CI with it. Changes: - `sonarscanner begin` now passes `/d:sonar.cs.opencover.reportsPaths="tests/**/TestResults/**/coverage.opencover.xml"` so the scanner picks up the per-project OpenCover reports coverlet emits, and `/d:sonar.coverage.exclusions="tests/**,**/*.Tests/**"` so the tests themselves don't count toward the coverage denominator. - The dormant step 8 (a disabled `if: false` curl-debug block from an earlier auth-troubleshooting session) is replaced with the actual test run: `dotnet test ... --no-build --settings coverlet.runsettings --collect "XPlat Code Coverage"`. - Step 9 (`sonarscanner end`) unchanged — it now has coverage data available to upload. Once this lands, the next scan will surface the *real* coverage number for sql-utilities. Until we see that number we don't know which files actually need test investment vs. were already covered by the existing suite. Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitea/workflows/sonarqube.yml | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/.gitea/workflows/sonarqube.yml b/.gitea/workflows/sonarqube.yml index bd93f3e..15d073a 100644 --- a/.gitea/workflows/sonarqube.yml +++ b/.gitea/workflows/sonarqube.yml @@ -50,29 +50,19 @@ jobs: /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}" \ /d:sonar.login="${{ secrets.SONAR_TOKEN }}" \ /d:sonar.exclusions="**/bin/**,**/obj/**" \ + /d:sonar.coverage.exclusions="tests/**,**/*.Tests/**" \ + /d:sonar.cs.opencover.reportsPaths="tests/**/TestResults/**/coverage.opencover.xml" \ /d:sonar.verbose=true - name: 7. Build run: dotnet build Strata.SqlTools.QueryBreakdown.sln --configuration Release - - name: 8. Test plugins endpoint from runner - if: false + - name: 8. Test with coverage run: | - echo "Test 1: curl with token via Basic auth" - curl -s -w "\nHTTP %{http_code}\n" \ - -u "${{ secrets.SONAR_TOKEN }}:" \ - "${{ secrets.SONAR_HOST_URL }}/api/plugins/installed" | tail -2 - echo "---" - echo "Test 2: curl with token via Bearer auth" - curl -s -w "\nHTTP %{http_code}\n" \ - -H "Authorization: Bearer ${{ secrets.SONAR_TOKEN }}" \ - "${{ secrets.SONAR_HOST_URL }}/api/plugins/installed" | tail -2 - echo "---" - echo "Test 3: same Java User-Agent the scanner uses" - curl -s -w "\nHTTP %{http_code}\n" \ - -u "${{ secrets.SONAR_TOKEN }}:" \ - -H "User-Agent: ScannerCLI/5.0.2.4997" \ - "${{ secrets.SONAR_HOST_URL }}/api/plugins/installed" | tail -2 - + dotnet test Strata.SqlTools.QueryBreakdown.sln \ + --configuration Release --no-build \ + --settings coverlet.runsettings \ + --collect "XPlat Code Coverage" + - name: 9. SonarScanner End run: dotnet sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"