--- name: sonarqube description: Use when working with SonarQube tech debt in this repo — querying the bermudalamb 9.9 server's web API from PowerShell, triaging open issues for the sql-utilities project, running scan-sonar.ps1 and verifying net-down after a cleanup commit. --- # Sonarqube (sql-utilities) ## Overview Two halves: **(A)** how to inspect the server from a PowerShell prompt, and **(B)** the per-rule-group cleanup loop that keeps tech-debt fixes from introducing new warnings. The repo has `SonarAnalyzer.CSharp` referenced in `Directory.Build.props`, so every `dotnet build` surfaces the same rule set the server reports — in the Error List / build output, not the SonarLint pane. Local build is the fast feedback loop; `scan-sonar.ps1` is what publishes results to the server. ## A — Inspecting the server Server: `https://snrqbe.bermudalamb.synology.me` (9.9 LTS). Project key: `sql-utilities`. Auth is **HTTP Basic with the token as the username and an empty password** — **not** Bearer (Bearer was added in 10.0 and returns 401 here). ```powershell $url = $env:SONARQUBE_URL.TrimEnd('/') # the env var is stored with a trailing / $tok = $env:SONARQUBE_TOKEN # squ_ user token, Windows user-scope $b64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${tok}:")) $h = @{ Authorization = "Basic $b64" } Invoke-RestMethod -Uri "$url/api/issues/search?componentKeys=sql-utilities&resolved=false&ps=500&facets=severities,types,rules" -Headers $h ``` **Gotcha:** `SONARQUBE_URL` is stored with a trailing `/`. Without `TrimEnd('/')` you build `//api/...` and the server answers with the SPA HTML shell — `Invoke-RestMethod` happily returns it and downstream `.issues`/`.facets` access yields blank output silently. **Useful endpoints:** - `/api/issues/search` — `?severities=MAJOR,MINOR`, `?rules=csharpsquid:S1168`, `?resolutions=WONTFIX`, `&facets=rules,severities,types` for triage views. - `/api/issues/add_comment` (POST: `issue`, `text`) — justification before a Won't Fix transition. - `/api/issues/do_transition` (POST: `issue`, `transition=wontfix`) — mark a known false-positive without changing code. - `/api/qualitygates/project_status?projectKey=sql-utilities` — current gate status. - `/api/measures/component?component=sql-utilities&metricKeys=code_smells,coverage,duplicated_lines_density,ncloc,sqale_index` — headline numbers. - `/api/ce/component?component=sql-utilities` — most-recent analysis task status (queued / in-progress / failed). `SONARQUBE_URL` / `SONARQUBE_TOKEN` are read here; `scan-sonar.ps1` reads the separate `SONAR_TOKEN` / `SONAR_HOST_URL` for the *upload* path — do not conflate them. ## B — Cleanup loop (tiered) ```dot digraph cleanup { query [label="Query open issues + facets=rules"]; triage [label="Tier each rule:\nmechanical / judgment / API-impact"]; t1 [label="Tier 1 — mechanical:\nbulk-fix in one commit"]; t2 [label="Tier 2 — judgment:\none commit per rule, audit diff"]; t3 [label="Tier 3 — API-impact:\nmanual, accept !breaking or skip"]; build [label="dotnet build + dotnet test\nafter EACH commit"]; push [label="git push + open PR"]; ci [label="CI runs SonarScanner\non Java 17"]; verify [label="Re-query API:\nconfirm rule counts dropped"]; query -> triage; triage -> t1 -> build; triage -> t2 -> build; triage -> t3 -> build; build -> push -> ci -> verify; } ``` **The loop has shifted from per-rule commits to *tiered batches* once a project has more than a handful of issues left.** Use `dotnet format analyzers --diagnostics --severity info` — it runs all the Roslyn-shipped code fixers for the listed diagnostics, including `CA*`, `IDE*`, and `NUnit*`. (The `csharpsquid:Sxxxx` family from SonarAnalyzer.CSharp usually has no `dotnet format` fixer; those still need manual edits.) ### Tier 1 — mechanical (one bulk commit) Rules where the fixer's rewrite is purely syntactic and can't change behavior — pile them into one `dotnet format` invocation, audit the diff for sanity, build, test, single commit: ```powershell dotnet format analyzers Strata.SqlTools.QueryBreakdown.sln ` --diagnostics IDE0028 CA1825 CA1834 CA1845 CA1847 CA1860 CA1866 CA1853 CA1830 CA2249 ` --severity info --verbosity normal ``` Typical safe rules: `IDE0028`, `CA1825`, `CA1834`, `CA1845`, `CA1847`, `CA1853`, `CA1860`, `CA1866`, `CA2249`, `CA1830`. Some rules in this family report "no associated code fix" — they'll need manual handling separately. ### Tier 2 — judgment (one commit per rule) Rules whose fixer can produce mediocre output or surprise the reader — bulk-fix but **audit before staging**: - `CA1510` — `ArgumentNullException.ThrowIfNull` rollup; always safe but voluminous, deserves its own commit. - `CA1822` — make-method-static; the fixer also rewrites internal callers to use type-name form. **Public methods becoming static are a binary-break for external NuGet consumers** — commit with `chore(sonar)!:` and a `BREAKING CHANGE:` footer naming each affected member, or revert those file diffs and apply only the private-helper changes. - `CA1861` — hoists constant array args to `static readonly` fields. The fixer's field names are sometimes cryptic (`stringArray`, `arg2`) or it emits duplicate `separator` fields in the same class. After running, rename ugly fields and dedupe collisions before committing. - `CA1854` — `TryGetValue`-style double-lookup elimination; trivial. - `CA1859` — concrete return type for perf. **No batch fixer**: edit each site manually after checking visibility. Apply only to private/internal/test; on public/protected, either revert or treat as a `!breaking` change (most cases are private helpers, so this is usually fine). ### Tier 3 — manual (no fixer) `CA1806`, `CA1846`, `CA1869`, the `csharpsquid:Sxxxx` family that has no `dotnet format` fixer, and individual public-API rules that need design judgment. Edit by hand, one rule at a time. If a fix would degrade clarity or break a tested contract, prefer **server-side Won't Fix with justification** — see [[sonarqube-wontfix-rules]] for the catalog already triaged that way (S1168, S3925, CS8601, S107). ### Guardrail After every commit (Tier 1, 2, or 3): ```powershell dotnet build Strata.SqlTools.QueryBreakdown.sln -c Release --nologo dotnet test Strata.SqlTools.QueryBreakdown.sln -c Release --no-build --nologo ``` Build succeeds with no new warnings beyond the baseline. Tests stay green. If either fails, fix or revert before continuing to the next tier — incremental builds can mask warning regressions, so re-run with `--no-incremental` if the warning count looks suspicious. ### Scan & verify **Local `./scan-sonar.ps1` is blocked here** — the system JRE is 8 and the SonarScanner CLI requires Java 17 (`UnsupportedClassVersionError`). CI handles the scan: `.gitea/workflows/sonarqube.yml` runs `actions/setup-java@v4` with `temurin` 17 and uploads on every push and PR. So the path is **push → open PR → CI scans → re-query the API to confirm net-down**: ```powershell $h = @{ Authorization = "Basic $b64" } Invoke-RestMethod -Uri "$url/api/issues/search?componentKeys=sql-utilities&resolved=false&facets=rules&ps=500" -Headers $h ``` The targeted rule IDs should each drop to 0 (or near-0 if the fixer left a few cases it couldn't batch-resolve). Commit subject style stays `chore(sonar): … (RuleId)` — bang (`!`) if the change is a deliberate breaking rename or visibility shift.