-
Notifications
You must be signed in to change notification settings - Fork 0
218 lines (197 loc) · 8.62 KB
/
Copy pathparity.yml
File metadata and controls
218 lines (197 loc) · 8.62 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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
name: Scan parity (self-scan ↔ SonarQube Cloud)
# Runs BOTH scans on the same commit and diffs their issue lists. Every PR
# answers: "does our daemon find what SonarSource's own pipeline finds?".
#
# The standalone self-scan (sonar.yml) is kept because it works on fork PRs
# (no SONAR_TOKEN needed), whereas this one requires the token and so skips
# for forks.
#
# Setup required (one-time, by the repo admin):
# 1. Sign in to https://sonarcloud.io with the repo's GitHub org.
# 2. Import this repo as a SonarQube Cloud project.
# Project key: RandomCodeSpace_sonar-predict, organisation: randomcodespace
# (adjust the env values below if SonarCloud assigns different ones).
# 3. Configure "Analysis Method" → "With GitHub Actions"; copy SONAR_TOKEN.
# 4. Add SONAR_TOKEN as a repo secret.
permissions:
contents: read
pull-requests: read
on:
pull_request:
branches: [main]
push:
branches: [main]
workflow_dispatch:
env:
SONAR_PROJECT_KEY: RandomCodeSpace_sonar-predict
SONAR_ORGANIZATION: randomcodespace
SONAR_HOST_URL: https://sonarcloud.io
jobs:
parity:
name: Scan parity
runs-on: ubuntu-latest
# Skip when the token isn't reachable (fork PRs) — the standalone
# self-scan workflow still gates fork PRs on our daemon's findings.
if: ${{ github.event_name == 'workflow_dispatch' || github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }}
env:
# The assembly step packages the ~150 MB distribution bundle (CLI +
# daemon fat jars + 10 analyzer plugins). 2 GB heap keeps maven-assembly-plugin
# from OOMing while it zips the bundle.
MAVEN_OPTS: -Xmx2g
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
- name: Set up Node.js 20
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Cache Maven repository
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Cache SonarQube Cloud packages
uses: actions/cache@v4
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: |
${{ runner.os }}-sonar
# Fail fast if the SONAR_TOKEN secret isn't configured. The Cloud step
# below would just error obscurely; this is a clearer signal.
- name: Verify SONAR_TOKEN
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
if [ -z "${SONAR_TOKEN:-}" ]; then
echo "::error::SONAR_TOKEN not set — parity workflow needs SonarQube Cloud access. See the header of this workflow for setup."
exit 1
fi
echo "SONAR_TOKEN configured."
# Single build that both scans then consume. `verify` produces the
# JaCoCo XML that both scans use as coverage evidence, plus the
# distribution bundle the self-scan invokes via its bin/sonar launcher.
- name: Build and test (generates JaCoCo XML + distribution bundle)
run: mvn -B -ntp 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}"
# --- (A) Self-scan ------------------------------------------------------
- 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)"
exit "$rc"
fi
# --- (B) SonarQube Cloud scan ------------------------------------------
- name: Run SonarQube Cloud scan
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mvn -B -ntp \
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar \
-Dsonar.projectKey="${SONAR_PROJECT_KEY}" \
-Dsonar.organization="${SONAR_ORGANIZATION}" \
-Dsonar.host.url="${SONAR_HOST_URL}" \
-Dsonar.qualitygate.wait=false \
-Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml
# SonarQube Cloud processes the scan asynchronously after upload. Poll
# the last completed analysis until the timestamp moves past the start
# of this run, or give up after ~3 minutes (most analyses complete in
# 30-60s; longer polls aren't worth holding the runner for).
- name: Wait for SonarQube Cloud processing
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
START_TS=$(date +%s)
for attempt in $(seq 1 18); do
# Constrain both the initial request (--proto =https) AND any redirect
# (--proto-redir =https) to HTTPS. Without --proto, githubactions:S6506
# still fires even when SONAR_HOST_URL is already https://; the rule
# wants both belts explicit at the curl-call site.
RESP=$(curl -fsSL --proto =https --proto-redir =https -u "$SONAR_TOKEN:" \
"${SONAR_HOST_URL}/api/project_analyses/search?project=${SONAR_PROJECT_KEY}&ps=1" || echo '{}')
ANALYSIS_TS=$(echo "$RESP" | jq -r '.analyses[0].date // empty' || true)
if [ -n "$ANALYSIS_TS" ]; then
ANALYSIS_EPOCH=$(date -d "$ANALYSIS_TS" +%s 2>/dev/null || echo 0)
if [ "$ANALYSIS_EPOCH" -ge "$START_TS" ]; then
echo "Latest analysis ($ANALYSIS_TS) is from this run."
exit 0
fi
echo "Attempt $attempt: latest analysis is $ANALYSIS_TS (before run start), waiting…"
else
echo "Attempt $attempt: no analyses yet on SonarQube Cloud, waiting…"
fi
sleep 10
done
echo "::warning::Timed out waiting for SonarQube Cloud to publish this run's analysis. Parity diff will use the most-recent published state."
# --- (C) Parity diff ----------------------------------------------------
- name: Diff self-scan vs SonarQube Cloud
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
PR_ARG=""
BRANCH_ARG=""
if [ "${{ github.event_name }}" = "pull_request" ]; then
PR_ARG="--pull-request ${{ github.event.pull_request.number }}"
else
BRANCH_ARG="--branch ${{ github.ref_name }}"
fi
python3 scripts/scan_parity.py \
--self-scan .sonar-predictor/scan.json \
--project-key "${SONAR_PROJECT_KEY}" \
--organization "${SONAR_ORGANIZATION}" \
--host "${SONAR_HOST_URL}" \
$PR_ARG $BRANCH_ARG \
--out .sonar-predictor/parity.json
- name: Upload scan + parity artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: scan-parity-${{ github.run_id }}
path: |
.sonar-predictor/scan.json
.sonar-predictor/parity.json
retention-days: 14
- name: Link to SonarQube Cloud dashboard
if: always()
run: |
{
echo ""
echo "---"
echo ""
echo "**SonarQube Cloud dashboard:** ${SONAR_HOST_URL}/project/overview?id=${SONAR_PROJECT_KEY}"
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo ""
echo "**PR-scoped view:** ${SONAR_HOST_URL}/project/issues?id=${SONAR_PROJECT_KEY}&pullRequest=${{ github.event.pull_request.number }}"
fi
} >> "$GITHUB_STEP_SUMMARY"