-
Notifications
You must be signed in to change notification settings - Fork 0
176 lines (163 loc) · 7.81 KB
/
Copy pathsonar.yml
File metadata and controls
176 lines (163 loc) · 7.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
name: Self-scan (sonar-predictor against itself)
permissions:
contents: read
# Runs the project's OWN scanner — the in-repo daemon, freshly built from this
# branch — against the repository on every PR and on pushes to main. The point
# is CI parity with the local self-scan we run during development: every change
# passes through the same gate, so the bar we apply to others applies to us.
#
# We deliberately do NOT use the previously released bundle from Maven Central.
# SONAR_PREDICTOR_HOME is repointed at the freshly-built exploded distribution
# under target/ so the scan exercises this branch's analyzer code, not
# yesterday's release.
on:
pull_request:
branches: [main]
push:
branches: [main]
workflow_dispatch:
jobs:
self-scan:
name: Self-scan
runs-on: ubuntu-latest
env:
# The assembly step packages the ~150 MB distribution bundle (CLI +
# daemon fat jars + 10 analyzer plugins). Maven's default heap is too
# small for that on ubuntu-latest — we'd get 'Execution exception:
# Java heap space' from maven-assembly-plugin:single. 2 GB is plenty
# and well under the runner's ~7 GB available memory.
MAVEN_OPTS: -Xmx2g
steps:
# fetch-depth: 0 keeps the full history available so we can switch to
# `--diff`-style semantics later without re-checking out the repo.
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# JDK 21 is the project's build/runtime target (required by
# sonarlint-analysis-engine 11.x / LTA 2026.1). Temurin is the safe default.
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
# The JS/TS analyzer plugin spawns Node at runtime to lint JS/TS sources,
# so Node must be on PATH when the scan runs (not just at build time).
- name: Set up Node.js 20
uses: actions/setup-node@v4
with:
node-version: '20'
# Cache the local Maven repo across runs. Keyed on the pom.xml so a
# dependency change invalidates cleanly; restore-keys lets a partial
# cache hit still seed most of ~/.m2.
- name: Cache Maven repository
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
# Single-module build. `verify` runs the integration tests AND produces
# the JaCoCo XML report we feed back into the scan as coverage evidence,
# plus the distribution bundle the self-scan invokes below.
- name: Build and test (generates JaCoCo XML + distribution bundle)
run: mvn -B -ntp -Dmaven.test.skip=false clean verify -Dsurefire.failIfNoSpecifiedTests=false
# Derive the project version from pom.xml so SONAR_PREDICTOR_HOME below
# tracks pom bumps without anyone touching CI.
- name: Derive project version
id: version
run: |
set -euo pipefail
VERSION=$(mvn -B -ntp -q -DforceStdout help:evaluate -Dexpression=project.version)
if [ -z "${VERSION}" ]; then
echo "::error::could not derive project.version from pom.xml"
exit 1
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "Project version: ${VERSION}"
# The actual self-scan. We point bin/sonar at THIS branch's
# freshly-built distribution under target/ (not whatever happens to
# be installed globally), and capture the scan as JSON to
# .sonar-predictor/scan.json via --save. One JaCoCo XML is passed
# in as coverage evidence (single module = one report).
#
# IMPORTANT: the CLI uses three-state exit codes
# 0 = clean (no findings at the floor)
# 1 = issues found (a normal scan outcome, not a failure)
# 2 = tool error (broken input, daemon unreachable, no Java)
# Step success means "the scanner ran". Whether the *result* should
# fail the build is decided by the Quality gate step below. We must
# not let `bash -e` propagate exit-1 from a healthy scan as a job
# failure; we propagate exit code only when it's >= 2.
- name: Run self-scan
run: |
set +e
export SONAR_PREDICTOR_HOME="$(pwd)/target/sonar-predictor-dist-${{ steps.version.outputs.version }}/sonar-predictor"
mkdir -p .sonar-predictor
"$SONAR_PREDICTOR_HOME/bin/sonar" \
--format json --save .sonar-predictor/scan.json \
analyze . --coverage target/site/jacoco/jacoco.xml
rc=$?
set -e
echo "Self-scan exit code: $rc (0=clean, 1=issues found, 2+=tool error)"
if [ "$rc" -ge 2 ]; then
echo "::error::Self-scan tool error (exit $rc) — see step log."
exit "$rc"
fi
# Render headline counts into the GitHub job summary so reviewers see
# the scan result inline on the run page without downloading artifacts.
# `// ([.files[]?.issues[]?]|length)` is the fallback path when an older
# JSON shape doesn't carry a top-level issueCount.
- name: Render scan summary
if: always()
run: |
J=.sonar-predictor/scan.json
if [ ! -f "$J" ]; then
echo "## Sonar self-scan" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "Scan JSON not produced — see the **Run self-scan** step log." >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
{
echo "## Sonar self-scan"
echo
echo "| Metric | Value |"
echo "| --- | --- |"
echo "| Total issues | $(jq -r '.issueCount // ([.files[]?.issues[]?]|length)' "$J") |"
echo "| Coverage (line, overall) | $(jq -r '.coverage.overallPercent // "n/a"' "$J")% |"
echo "| Severity | $(jq -rc '[.files[]?.issues[]?.severity]|group_by(.)|map("\(.[0])=\(length)")|join(" ")' "$J") |"
echo "| Type | $(jq -rc '[.files[]?.issues[]?.type]|group_by(.)|map("\(.[0])=\(length)")|join(" ")' "$J") |"
echo "| Warnings | $(jq -r '.warnings // [] | length' "$J") |"
} >> "$GITHUB_STEP_SUMMARY"
# Always upload the scan JSON, even on failure, so a broken scan is
# still debuggable from the run page. 14-day retention is enough to
# cover a typical PR review cycle without pinning storage forever.
- name: Upload scan JSON
if: always()
uses: actions/upload-artifact@v4
with:
name: sonar-scan-${{ github.run_id }}
path: .sonar-predictor/scan.json
retention-days: 14
# Informational gate. We compute the count of CRITICAL bugs and security
# hotspots and emit a `::warning::` so they show on the PR's Checks tab,
# but we deliberately do NOT `exit 1` yet — first runs are baseline data
# while we work the existing findings down to zero.
#
# TODO: once the backlog is clear, flip this to `exit 1` on any
# CRITICAL bug or security hotspot and rename this step to "Quality gate".
- name: Quality gate (informational only)
if: always()
run: |
J=.sonar-predictor/scan.json
if [ ! -f "$J" ]; then
echo "No scan JSON found — skipping gate."
exit 0
fi
CRIT=$(jq -r '[.files[]?.issues[]? | select(.severity=="CRITICAL" and .type=="BUG")] | length' "$J")
HOT=$(jq -r '[.files[]?.issues[]? | select(.type=="SECURITY_HOTSPOT")] | length' "$J")
echo "Critical bugs: $CRIT"
echo "Security hotspots: $HOT"
if [ "$CRIT" -gt 0 ] || [ "$HOT" -gt 0 ]; then
echo "::warning::Self-scan found $CRIT critical bug(s) and $HOT security hotspot(s). Gate is informational for now; will enforce later."
fi