Skip to content

Commit 818e8d4

Browse files
etrclaude
andcommitted
TASK-042: validation fixes — check script
Applies the load-bearing review findings on scripts/check-release-notes.sh that materially affect what the gate catches: - A5 allow_ip pair: anchor v1 names with \b so the 'disallow_ip' prose line cannot satisfy the allow_ip check via substring overlap. Mirror the same anchoring on ban_ip for consistency. - A2 v1 token set: add DEFAULT_WS_TIMEOUT, decorate_response, and enqueue_response — RELEASE_NOTES.md already mentions them, but they were not pinned by the gate. - A5 rename pairs: add 'webserver(create_webserver const&)' pair so the explicit-constructor rename is enforced on a single line. Refactor: extract three helpers — check_tokens_present (A2/A3 loops), extract_section_body (was inline), and check_section_cites (A6 pair) — to remove the duplicated "loop + accumulate missing + print + exit" shape and the duplicated "extract section + empty guard + grep + fail" shape. No behavior change beyond the additions above. Minor: drop a no-op `${var:-0}` default inside `$((…))`, silence the markdownlint advisory's stderr explicitly (was '2>&1' which folded error lines into the discarded stream), and add a why-not-shared note above REQUIRED_V2_TOKENS explaining the deliberate duplication with check-readme.sh. `./scripts/check-release-notes.sh` still passes: A1 exists; A2 35 v1 tokens; A3 26 v2 tokens; A4 7 sections; A5 26 rename pairs; A6 threading+error citations; A7 disclaimer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c84d7fd commit 818e8d4

1 file changed

Lines changed: 60 additions & 55 deletions

File tree

scripts/check-release-notes.sh

Lines changed: 60 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,45 @@ fail() {
4646
exit 1
4747
}
4848

49+
# Helper: check that every token in the given array appears at least once in
50+
# the target file. Usage: check_tokens_present <label> <file> "${ARRAY[@]}"
51+
check_tokens_present() {
52+
local label="$1" file="$2"; shift 2
53+
local missing=()
54+
for tok in "$@"; do
55+
grep -qE "$tok" "$file" || missing+=("$tok")
56+
done
57+
if [ "${#missing[@]}" -gt 0 ]; then
58+
echo "check-release-notes: FAIL: ${label}: missing tokens:" >&2
59+
printf ' %s\n' "${missing[@]}" >&2
60+
exit 1
61+
fi
62+
}
63+
64+
# Helper: extract body of a named section (between its H2 heading and the next H2/EOF).
65+
# $1 = file, $2 = regex matching the heading line (case-insensitive).
66+
extract_section_body() {
67+
awk -v re="$2" '
68+
BEGIN { in_section = 0 }
69+
/^##[ \t]+/ {
70+
if (in_section) { exit }
71+
if (tolower($0) ~ tolower(re)) { in_section = 1; next }
72+
}
73+
in_section { print }
74+
' "$1"
75+
}
76+
77+
# Helper: check that a named section exists and contains a required citation.
78+
# Usage: check_section_cites <label> <file> <heading_re> <citation_re> <section_name>
79+
check_section_cites() {
80+
local label="$1" file="$2" heading_re="$3" citation_re="$4" section_name="$5"
81+
local body
82+
body="$(extract_section_body "$file" "$heading_re")"
83+
[ -n "$body" ] || fail "${label}: RELEASE_NOTES.md is missing a '${section_name}' section"
84+
echo "$body" | grep -qE "$citation_re" \
85+
|| fail "${label}: ${section_name} section must cite ${citation_re}"
86+
}
87+
4988
# ---- A1: RELEASE_NOTES.md exists --------------------------------------------
5089

5190
[ -f "$NOTES" ] || fail "A1: RELEASE_NOTES.md does not exist at $NOTES"
@@ -87,23 +126,18 @@ REQUIRED_V1_TOKENS=(
87126
'\bgnutls_session_t\b'
88127
'\bget_raw_response\b'
89128
'\bDEFAULT_WS_PORT\b'
129+
'\bDEFAULT_WS_TIMEOUT\b'
130+
'\bdecorate_response\b'
131+
'\benqueue_response\b'
90132
'\bHAVE_GNUTLS\b'
91133
)
92134

93-
missing_v1=()
94-
for tok in "${REQUIRED_V1_TOKENS[@]}"; do
95-
if ! grep -qE "$tok" "$NOTES"; then
96-
missing_v1+=("$tok")
97-
fi
98-
done
99-
if [ "${#missing_v1[@]}" -gt 0 ]; then
100-
echo "check-release-notes: FAIL: A2: RELEASE_NOTES.md is missing required v1-era tokens:" >&2
101-
for tok in "${missing_v1[@]}"; do echo " $tok" >&2; done
102-
exit 1
103-
fi
135+
check_tokens_present "A2: RELEASE_NOTES.md is missing required v1-era tokens" "$NOTES" "${REQUIRED_V1_TOKENS[@]}"
104136

105137
# ---- A3: required v2-era tokens appear --------------------------------------
106-
# Replacement list. Kept in lock-step with check-readme.sh A3.
138+
# Replacement list. Deliberately duplicated from check-readme.sh A3 (not sourced)
139+
# to keep each script independently runnable without cross-script dependencies;
140+
# with only two consumers a shared lib would be over-engineering.
107141

108142
REQUIRED_V2_TOKENS=(
109143
'\bon_get\b'
@@ -134,17 +168,7 @@ REQUIRED_V2_TOKENS=(
134168
'\biovec_entry\b'
135169
)
136170

137-
missing_v2=()
138-
for tok in "${REQUIRED_V2_TOKENS[@]}"; do
139-
if ! grep -qE "$tok" "$NOTES"; then
140-
missing_v2+=("$tok")
141-
fi
142-
done
143-
if [ "${#missing_v2[@]}" -gt 0 ]; then
144-
echo "check-release-notes: FAIL: A3: RELEASE_NOTES.md is missing required v2 tokens:" >&2
145-
for tok in "${missing_v2[@]}"; do echo " $tok" >&2; done
146-
exit 1
147-
fi
171+
check_tokens_present "A3: RELEASE_NOTES.md is missing required v2 tokens" "$NOTES" "${REQUIRED_V2_TOKENS[@]}"
148172

149173
# ---- A4: required H2 sections present ---------------------------------------
150174

@@ -183,9 +207,9 @@ fi
183207

184208
RENAME_PAIRS=(
185209
'sweet_kill;stop_and_wait'
186-
'ban_ip;block_ip'
210+
'\bban_ip\b;block_ip'
187211
'unban_ip;unblock_ip'
188-
'allow_ip;(block_ip|unblock_ip)'
212+
'\ballow_ip\b;(block_ip|unblock_ip)'
189213
'disallow_ip;(block_ip|unblock_ip)'
190214
'not_found_resource;not_found_handler'
191215
'method_not_allowed_resource;method_not_allowed_handler'
@@ -207,6 +231,7 @@ RENAME_PAIRS=(
207231
'deferred_response;http_response::deferred'
208232
'basic_auth_fail_response;http_response::unauthorized'
209233
'digest_auth_fail_response;http_response::unauthorized'
234+
'webserver.*create_webserver;explicit'
210235
)
211236

212237
missing_pairs=()
@@ -226,35 +251,15 @@ fi
226251

227252
# ---- A6: threading & error sections cite architecture sources ---------------
228253

229-
extract_section_body() {
230-
# $1 = file, $2 = regex matching the heading line (case-insensitive).
231-
# Captures lines after the first matching heading up to the next H2 or EOF.
232-
awk -v re="$2" '
233-
BEGIN { in_section = 0 }
234-
/^##[ \t]+/ {
235-
if (in_section) { exit }
236-
if (tolower($0) ~ tolower(re)) { in_section = 1; next }
237-
}
238-
in_section { print }
239-
' "$1"
240-
}
241-
242-
thread_body="$(extract_section_body "$NOTES" '^##[ \t]+.*threading')"
243-
err_body="$(extract_section_body "$NOTES" '^##[ \t]+.*error[ \t-]+propag')"
254+
check_section_cites "A6" "$NOTES" \
255+
'^##[ \t]+.*threading' \
256+
'(DR-008|§[[:space:]]*5\.1|specs/architecture/05-cross-cutting\.md|specs/architecture/11-decisions/DR-008)' \
257+
'Threading'
244258

245-
if [ -z "$thread_body" ]; then
246-
fail "A6: RELEASE_NOTES.md is missing a '## Threading...' section"
247-
fi
248-
if ! echo "$thread_body" | grep -qE '(DR-008|§[[:space:]]*5\.1|specs/architecture/05-cross-cutting\.md|specs/architecture/11-decisions/DR-008)'; then
249-
fail "A6: Threading section must cite DR-008 or §5.1"
250-
fi
251-
252-
if [ -z "$err_body" ]; then
253-
fail "A6: RELEASE_NOTES.md is missing an '## Error propagation' section"
254-
fi
255-
if ! echo "$err_body" | grep -qE '(DR-009|§[[:space:]]*5\.2|specs/architecture/05-cross-cutting\.md|specs/architecture/11-decisions/DR-009)'; then
256-
fail "A6: Error-propagation section must cite DR-009 or §5.2"
257-
fi
259+
check_section_cites "A6" "$NOTES" \
260+
'^##[ \t]+.*error[ \t-]+propag' \
261+
'(DR-009|§[[:space:]]*5\.2|specs/architecture/05-cross-cutting\.md|specs/architecture/11-decisions/DR-009)' \
262+
'Error propagation'
258263

259264
# ---- A7: explicit "not a compatibility commitment" disclaimer ---------------
260265

@@ -265,7 +270,7 @@ fi
265270
# ---- Markdown sanity --------------------------------------------------------
266271
# (S1) Balanced ``` fences (count of ``` lines must be even).
267272
fence_count="$(grep -cE '^```' "$NOTES" || true)"
268-
if [ "$(( ${fence_count:-0} % 2 ))" -ne 0 ]; then
273+
if [ "$((fence_count % 2))" -ne 0 ]; then
269274
fail "S1: RELEASE_NOTES.md has an odd number of \`\`\` fence lines ($fence_count); fences are unbalanced"
270275
fi
271276

@@ -285,7 +290,7 @@ awk '
285290

286291
# ---- Optional: markdownlint advisory ----------------------------------------
287292
if command -v markdownlint >/dev/null 2>&1; then
288-
if ! markdownlint -q "$NOTES" 2>&1; then
293+
if ! markdownlint -q "$NOTES" 2>/dev/null; then
289294
echo "check-release-notes: NOTE: markdownlint reported issues (advisory only, not gating)" >&2
290295
fi
291296
fi

0 commit comments

Comments
 (0)