Skip to content

Commit 0eb1df3

Browse files
committed
feat(scripts): pre-pr-check.sh --tags-lint flag + --review-body alias
- --tags-lint: scan firstdata/sources/*.json for ASCII-uppercase tags and case-insensitive duplicate tags. Complements the tags casing rule from the style guide and catches issues the secrecy check cannot see. - --review-body: alias of --text for reviewer-side ergonomics. - Help text updated; exit 0 clean / 1 violation / 2 usage.
1 parent fc22266 commit 0eb1df3

1 file changed

Lines changed: 67 additions & 2 deletions

File tree

scripts/pre-pr-check.sh

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@
1919
# scripts/pre-pr-check.sh --text "any arbitrary text blob" # review body, comment, commit msg
2020
# scripts/pre-pr-check.sh --stdin < body.md
2121
# scripts/pre-pr-check.sh --scan-sources # same scan CI does for firstdata/sources
22+
# scripts/pre-pr-check.sh --tags-lint # scan firstdata/sources tags for ASCII
23+
# # uppercase or duplicate (case-insensitive)
24+
# # violations — complements CI / style guide
2225
#
23-
# Exit code: 0 = clean, 1 = confidential term found, 2 = usage error.
26+
# Exit code: 0 = clean, 1 = confidential term or tags violation found, 2 = usage error.
2427
#
2528
# Keep the BANNED_TERMS list in sync with .github/workflows/secrecy-check.yml.
2629
set -euo pipefail
@@ -43,9 +46,10 @@ BRANCH=""
4346
TEXT=""
4447
SCAN_SOURCES=0
4548
READ_STDIN=0
49+
TAGS_LINT=0
4650

4751
usage() {
48-
sed -n '2,25p' "$0"
52+
sed -n '2,28p' "$0"
4953
exit 2
5054
}
5155

@@ -56,8 +60,10 @@ while [[ $# -gt 0 ]]; do
5660
--title) TITLE="$2"; shift 2 ;;
5761
--branch) BRANCH="$2"; shift 2 ;;
5862
--text) TEXT="$2"; shift 2 ;;
63+
--review-body) TEXT="$2"; shift 2 ;; # alias of --text (reviewer-side)
5964
--stdin) READ_STDIN=1; shift ;;
6065
--scan-sources) SCAN_SOURCES=1; shift ;;
66+
--tags-lint) TAGS_LINT=1; shift ;;
6167
-h|--help) usage ;;
6268
*) echo "Unknown arg: $1" >&2; usage ;;
6369
esac
@@ -122,4 +128,63 @@ if [[ "$found" -eq 1 ]]; then
122128
exit 1
123129
fi
124130

131+
if [[ "$TAGS_LINT" -eq 1 ]]; then
132+
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
133+
src_dir="$repo_root/firstdata/sources"
134+
if [[ ! -d "$src_dir" ]]; then
135+
echo "::error::--tags-lint: sources dir not found at $src_dir" >&2
136+
exit 1
137+
fi
138+
python3 - "$src_dir" <<'PY'
139+
import json, pathlib, re, sys
140+
src = pathlib.Path(sys.argv[1])
141+
ascii_re = re.compile(r'^[\x20-\x7E]+$')
142+
violations_upper = [] # (file, tag)
143+
violations_dup = [] # (file, tag_lower, originals)
144+
parse_errors = []
145+
for f in sorted(src.rglob('*.json')):
146+
try:
147+
d = json.load(open(f, encoding='utf-8'))
148+
except Exception as e:
149+
parse_errors.append((str(f), str(e)))
150+
continue
151+
tags = d.get('tags') or []
152+
if not isinstance(tags, list):
153+
continue
154+
# ASCII uppercase check
155+
for t in tags:
156+
if isinstance(t, str) and ascii_re.match(t) and re.search(r'[A-Z]', t):
157+
violations_upper.append((str(f), t))
158+
# Duplicate (case-insensitive) check
159+
by_lower = {}
160+
for t in tags:
161+
if not isinstance(t, str): continue
162+
k = t.lower()
163+
by_lower.setdefault(k, []).append(t)
164+
for k, originals in by_lower.items():
165+
if len(originals) > 1:
166+
violations_dup.append((str(f), k, originals))
167+
168+
exit_code = 0
169+
if parse_errors:
170+
print('🔴 JSON parse errors:', file=sys.stderr)
171+
for f, e in parse_errors:
172+
print(f' {f}: {e}', file=sys.stderr)
173+
exit_code = 1
174+
if violations_upper:
175+
print(f'🔴 tags-lint: {len(violations_upper)} ASCII-uppercase tag(s) found:', file=sys.stderr)
176+
for f, t in violations_upper:
177+
print(f' {f}: {t!r}', file=sys.stderr)
178+
exit_code = 1
179+
if violations_dup:
180+
print(f'🔴 tags-lint: {len(violations_dup)} duplicate tag group(s) (case-insensitive):', file=sys.stderr)
181+
for f, k, originals in violations_dup:
182+
print(f' {f}: {originals} → {k!r}', file=sys.stderr)
183+
exit_code = 1
184+
if exit_code == 0:
185+
print('✅ tags-lint: all tags compliant (ASCII lowercase + no case-insensitive duplicates).')
186+
sys.exit(exit_code)
187+
PY
188+
fi
189+
125190
echo "✅ Pre-PR secrecy check passed."

0 commit comments

Comments
 (0)