-
Notifications
You must be signed in to change notification settings - Fork 0
194 lines (178 loc) · 9.03 KB
/
Copy pathsonar-bulk-accept.yml
File metadata and controls
194 lines (178 loc) · 9.03 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
name: SonarCloud Bulk Accept
# Marks remaining open code smells in well-defined buckets as
# Accepted (formerly "Won't Fix") with a deliberate comment, so the
# Sonar issue list reflects only smells we actually want to act on.
#
# Trigger manually from the Actions tab — never runs on push/PR. The
# rule + filter pairs are explicit; adding a new bucket means editing
# this file (visible in PR review) rather than mass-suppressing in code.
#
# Each bucket sends ONE bulk_change call to the SonarCloud API:
# POST /api/issues/bulk_change
# issues=<comma-separated keys>
# do_transition=accept
# comment=<bucket-specific justification>
#
# Why "Accepted" and not "False Positive": these are real findings
# under their respective rules — we just don't intend to act on them.
# False Positive is reserved for cases where the rule has misfired
# (only godre:S8239 in shutdown handling here qualifies).
on:
workflow_dispatch:
inputs:
dry_run:
description: "Print buckets and counts without calling the API"
type: boolean
default: true
jobs:
bulk-accept:
runs-on: ubuntu-latest
permissions:
contents: read
env:
SONAR_HOST: https://sonarcloud.io
SONAR_PROJECT: RandomCodeSpace_ctm
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
steps:
- name: Verify SONAR_TOKEN
run: |
if [ -z "${SONAR_TOKEN}" ]; then
echo "::error::SONAR_TOKEN secret is not set"
exit 1
fi
- name: Install jq
run: sudo apt-get update -qq && sudo apt-get install -y -qq jq
- name: Process buckets
env:
DRY_RUN: ${{ inputs.dry_run }}
run: |
set -euo pipefail
# Helper: fetch all open CODE_SMELL issue keys matching a filter
# (rule + optional path glob via componentKeys). Paginates.
fetch_keys() {
local rules="$1"
local file_filter="${2:-}"
local page=1
local keys=()
while :; do
local url="${SONAR_HOST}/api/issues/search?componentKeys=${SONAR_PROJECT}&types=CODE_SMELL&statuses=OPEN,CONFIRMED,REOPENED&rules=${rules}&ps=500&p=${page}"
if [ -n "${file_filter}" ]; then
url="${url}&files=${file_filter}"
fi
local resp
resp=$(curl -sSf -u "${SONAR_TOKEN}:" "${url}")
local batch
batch=$(echo "${resp}" | jq -r '.issues[].key')
if [ -z "${batch}" ]; then break; fi
while IFS= read -r k; do keys+=("$k"); done <<< "${batch}"
local total
total=$(echo "${resp}" | jq -r '.total')
local fetched=$(( page * 500 ))
if [ "${fetched}" -ge "${total}" ]; then break; fi
page=$(( page + 1 ))
done
(IFS=,; echo "${keys[*]}")
}
# Helper: bulk-accept a set of keys with a comment.
bulk_accept() {
local label="$1"
local keys="$2"
local comment="$3"
if [ -z "${keys}" ]; then
echo "[${label}] no matching issues — skipping"
return
fi
local count
count=$(echo "${keys}" | tr ',' '\n' | wc -l)
echo "[${label}] ${count} issues"
if [ "${DRY_RUN}" = "true" ]; then
echo "[${label}] DRY_RUN — not calling bulk_change"
return
fi
# SonarCloud bulk_change accepts at most ~500 keys per call.
# Split into chunks of 400 to stay safe.
local chunk
local rest="${keys}"
while [ -n "${rest}" ]; do
chunk=$(echo "${rest}" | cut -d',' -f1-400)
rest=$(echo "${rest}" | cut -d',' -f401- || true)
if [ "${rest}" = "${chunk}" ]; then rest=""; fi
curl -sSf -u "${SONAR_TOKEN}:" -X POST \
--data-urlencode "issues=${chunk}" \
--data-urlencode "do_transition=accept" \
--data-urlencode "comment=${comment}" \
"${SONAR_HOST}/api/issues/bulk_change" > /dev/null
done
echo "[${label}] accepted"
}
# ─────────────────────────────────────────────────────────────
# Bucket 1: godre:S8205 — named struct types
# Anonymous struct types are intentional in test scaffolding and
# request-decode shapes that aren't reused.
KEYS=$(fetch_keys "godre:S8205")
bulk_accept "S8205 named-struct" "${KEYS}" \
"One-shot decode/scratch structs; naming would scatter the type. Accepted."
# Bucket 2: godre:S8196 — interface naming
# Existing names are domain-aligned (InputSessionSource, ProjRefresher).
# Renaming would touch a wide blast radius for a stylistic nit.
KEYS=$(fetch_keys "godre:S8196")
bulk_accept "S8196 interface-name" "${KEYS}" \
"Names are domain-aligned and tested; rename has too broad a blast radius. Accepted."
# Bucket 3: godre:S8193 — receiver naming
# Receiver names are short and consistent within each type;
# the rule's "first-letter" preference doesn't add value here.
KEYS=$(fetch_keys "godre:S8193")
bulk_accept "S8193 receiver-name" "${KEYS}" \
"Receiver names are consistent within each type. Accepted."
# Bucket 4: godre:S8242 — context.Context as struct field
# Used in long-lived components where ctx genuinely lives on
# the struct (cancellation propagates through the lifecycle).
KEYS=$(fetch_keys "godre:S8242")
bulk_accept "S8242 ctx-field" "${KEYS}" \
"Lifecycle-scoped ctx travels with the struct. Accepted."
# Bucket 5: go:S107 + go:S117 — too many params / variable name
# Existing shape mirrors cobra signatures.
KEYS=$(fetch_keys "go:S107,go:S117")
bulk_accept "S107/S117 signature" "${KEYS}" \
"Signature mirrors cobra contracts. Accepted."
# Bucket 6: go:S3776 — cognitive complexity
# Remaining occurrences are in tailers, attention engine eval,
# and other linear-branching lifecycle code where the
# complexity comes from breadth of cases, not nesting depth.
# Extracting helpers used once would create indirection without
# meaningfully reducing reader load. Accepted as a project-wide
# judgement call rather than per-function.
KEYS=$(fetch_keys "go:S3776")
bulk_accept "S3776 cognitive-complexity" "${KEYS}" \
"Production engines: complexity is breadth of cases, not nesting. Extracting single-use helpers harms readability. Accepted."
# ─────────────────────────────────────────────────────────────
# Bucket 7: ALL remaining smells in *_test.go
# Test code is intentionally dense (table-driven cases, mock
# plumbing). The cognitive-complexity / etc. rules are noise here.
test_keys=$(curl -sSf -u "${SONAR_TOKEN}:" \
"${SONAR_HOST}/api/issues/search?componentKeys=${SONAR_PROJECT}&types=CODE_SMELL&statuses=OPEN,CONFIRMED,REOPENED&ps=500" \
| jq -r '.issues[] | select(.component | test("_test\\.go$")) | .key' \
| paste -sd, -)
bulk_accept "test-file smells" "${test_keys}" \
"Test code: table-driven density / mock plumbing are by design. Accepted."
# ─────────────────────────────────────────────────────────────
# FALSE POSITIVE bucket — the rule has misfired here.
# Keep this list explicit; do not append-only.
fp_keys=$(fetch_keys "godre:S8239")
if [ -n "${fp_keys}" ]; then
count=$(echo "${fp_keys}" | tr ',' '\n' | wc -l)
echo "[S8239 false-positive] ${count} issues"
if [ "${DRY_RUN}" != "true" ]; then
curl -sSf -u "${SONAR_TOKEN}:" -X POST \
--data-urlencode "issues=${fp_keys}" \
--data-urlencode "do_transition=falsepositive" \
--data-urlencode "comment=Shutdown handler: the parent ctx is already Done at this point (we just received from <-ctx.Done()), so deriving from it would give a zero-grace shutdown. context.Background() is required for the grace deadline." \
"${SONAR_HOST}/api/issues/bulk_change" > /dev/null
echo "[S8239 false-positive] marked"
fi
fi
- name: Summary
if: always()
run: |
echo "Run with dry_run=false to actually apply the transitions."
echo "Re-run after the next Sonar scan to clean up any new findings in the same buckets."