tenseleyflow/shithub / 8159bf2

Browse files

lint: lint-unused script + CI step to block shim regrowth (SR2 M1)

scripts/lint-unused.sh fails the build when any non-test Go file
under internal/ or cmd/ carries 'var _ = symbol' (the dead-code
shim shape). Allows the legitimate 'var _ Type = (*X)(nil)'
interface-assertion pattern by anchoring the regex to '= [A-Za-z]'
without a type name in between.

Wired into:
- Makefile 'lint-unused' target + the 'ci' alias
- .github/workflows/ci.yml as a dedicated step (the rest of the
bash lints are still local-only; lint-unused gets first-class CI
treatment because the audit caught regrowth twice in 3 days).
Authored by espadonne
SHA
8159bf2172d02beb14ab616ebce42c048226e26f
Parents
a73b2e2
Tree
e7e4f58

3 changed files

StatusFile+-
M .github/workflows/ci.yml 5 0
M Makefile 5 2
A scripts/lint-unused.sh 62 0
.github/workflows/ci.ymlmodified
@@ -37,6 +37,11 @@ jobs:
3737
           version: latest
3838
           args: --timeout=5m
3939
 
40
+      - name: Lint — unused (no var-_-equals-symbol shims)
41
+        # Block the dead-code 'silence unused import' shims that
42
+        # accumulated during S00–S40 refactors (audit 2026-05-10 M1).
43
+        run: scripts/lint-unused.sh
44
+
4045
       - name: Test
4146
         run: go test -trimpath ./...
4247
 
Makefilemodified
@@ -2,7 +2,7 @@
22
 # Targets mirror what CI runs. The Makefile is the source of truth.
33
 
44
 .DEFAULT_GOAL := help
5
-.PHONY: help dev build test test-race lint lint-policy lint-markdown lint-secret-logs lint-spdx verify-api-docs fmt tidy clean ci assets install-tools version deploy deploy-check restore-drill bench-staging docs docs-serve docs-verify gen-third-party-notices audit-a11y audit-a11y-pa11y audit-a11y-axe load-test
5
+.PHONY: help dev build test test-race lint lint-policy lint-markdown lint-secret-logs lint-spdx lint-unused verify-api-docs fmt tidy clean ci assets install-tools version deploy deploy-check restore-drill bench-staging docs docs-serve docs-verify gen-third-party-notices audit-a11y audit-a11y-pa11y audit-a11y-axe load-test
66
 
77
 # Build metadata embedded into the binary via -ldflags.
88
 VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
@@ -70,7 +70,7 @@ assets: ## Copy Primer CSS into internal/web/static/ for embedding.
7070
 		echo "warn: .refs/primer-css/dist not found; run 'git clone https://github.com/primer/css .refs/primer-css' first"; \
7171
 	fi
7272
 
73
-ci: lint lint-policy lint-markdown lint-secret-logs lint-spdx verify-api-docs test build ## Full CI pipeline (matches .github/workflows/ci.yml).
73
+ci: lint lint-policy lint-markdown lint-secret-logs lint-spdx lint-unused verify-api-docs test build ## Full CI pipeline (matches .github/workflows/ci.yml).
7474
 	@echo "ci: ok"
7575
 
7676
 lint-policy: ## Enforce policy-package boundary (no inline auth checks in handlers/git/cmd).
@@ -85,6 +85,9 @@ lint-secret-logs: ## Fail when source emits log lines containing token-prefix pa
8585
 lint-spdx: ## Verify every Go + shell source carries the SPDX license header.
8686
 	@scripts/verify-spdx-headers.sh
8787
 
88
+lint-unused: ## Fail when source carries dead-code 'silence unused import' shims (var _ = symbol).
89
+	@scripts/lint-unused.sh
90
+
8891
 verify-api-docs: ## Fail when an /api/v1 route in code is missing from docs/public/api/.
8992
 	@scripts/verify-api-docs.sh
9093
 
scripts/lint-unused.shadded
@@ -0,0 +1,62 @@
1
+#!/usr/bin/env bash
2
+# SPDX-License-Identifier: AGPL-3.0-or-later
3
+#
4
+# Fail when any Go source file carries `var _ = <symbol>` "silence
5
+# unused import" shims. The pattern accumulated during S00–S40
6
+# refactors (audit 2026-05-08 found 3 instances; audit 2026-05-10
7
+# found 8 — the same shape kept growing because nothing failed CI
8
+# on it).
9
+#
10
+# False positives:
11
+# - Test files use `var _ = ...` interface assertions legitimately
12
+#   (e.g. `var _ http.Handler = (*X)(nil)`); we exclude *_test.go.
13
+# - Genuine type contracts of the form `var _ TypeName = (*X)(nil)`
14
+#   are useful and explicitly allowed by the grep below — we only
15
+#   match the `var _ = SOMETHING` (no type) form, which is the
16
+#   shim shape.
17
+#
18
+# Allowlist: rare cases where a true contract assertion happens
19
+# to take this exact shape. Add a path here and explain WHY in a
20
+# comment if you really need an exception.
21
+
22
+set -euo pipefail
23
+
24
+ALLOWED_FILES=(
25
+  # (none currently)
26
+)
27
+
28
+cd "$(git rev-parse --show-toplevel)"
29
+
30
+# Match: `var _ = <ident>` at line start, possibly indented.
31
+# Reject: `var _ Type = ...` (the legitimate interface-assertion form).
32
+matches=$(grep -rE '^[[:space:]]*var _ = [A-Za-z]' \
33
+  --include='*.go' \
34
+  --exclude='*_test.go' \
35
+  --exclude-dir='vendor' \
36
+  --exclude-dir='.git' \
37
+  internal/ cmd/ 2>/dev/null || true)
38
+
39
+if [ -z "$matches" ]; then
40
+  echo "lint-unused: ok"
41
+  exit 0
42
+fi
43
+
44
+# Strip allowlisted files.
45
+for allowed in "${ALLOWED_FILES[@]}"; do
46
+  matches=$(echo "$matches" | grep -v "^${allowed}:" || true)
47
+done
48
+
49
+if [ -z "$matches" ]; then
50
+  echo "lint-unused: ok (allowlisted hits skipped)"
51
+  exit 0
52
+fi
53
+
54
+echo "lint-unused: dead-code 'silence unused import' shims found:" >&2
55
+echo >&2
56
+echo "$matches" >&2
57
+echo >&2
58
+echo "Drop the line and the import it was silencing. If the import is" >&2
59
+echo "still needed, the shim was lying — investigate the actual usage." >&2
60
+echo "If you need to keep one for a legitimate reason, allowlist it in" >&2
61
+echo "scripts/lint-unused.sh with a comment explaining why." >&2
62
+exit 1