feat: CI refuses a release PR with no drill record #138
2 changed files with 49 additions and 9 deletions
34
.github/scripts/drill-recorded.sh
vendored
34
.github/scripts/drill-recorded.sh
vendored
|
|
@ -95,15 +95,31 @@ esac
|
|||
# humans reading the log.
|
||||
#
|
||||
# grab is re-armed by every '## ' line, so the section ends at the next one —
|
||||
# a record cannot borrow the body of the record below it. sed drops the blank
|
||||
# padding under the heading and the command substitution eats the trailing
|
||||
# blanks, which is what makes "present but EMPTY" distinguishable from
|
||||
# "present with content": a heading with nothing but whitespace under it
|
||||
# extracts to the empty string.
|
||||
record="$(awk -v ver="$ver" '
|
||||
/^## / { grab = ($2 == "Release" && $3 == "drill" && $4 == "—" && $5 == ver); next }
|
||||
grab { print }
|
||||
' "$runs" | sed '/./,$!d')"
|
||||
# a record cannot borrow the body of the record below it.
|
||||
#
|
||||
# `grab && NF` is the non-blank rule, and it is load-bearing rather than
|
||||
# tidiness. NF is 0 on a line that is empty OR contains only whitespace, so
|
||||
# `record` is non-empty exactly when a line with real content exists. The
|
||||
# first cut of this piped through `sed '/./,$!d'` and the comment here claimed
|
||||
# "a heading with nothing but whitespace under it extracts to the empty
|
||||
# string" — which is precisely what that pipeline did NOT guarantee, because
|
||||
# `.` matches a space. A heading followed by one tab satisfied the gate. The
|
||||
# comment documented the intended contract and the code did not meet it, which
|
||||
# on a gate is the whole ballgame: an evidence-free release for the price of an
|
||||
# invisible character. Found by all three reviewers on #138, independently.
|
||||
#
|
||||
# The '(NF == 5 || $6 == dash)' tail constraint keeps this in step with box's
|
||||
# twin: without it '## Release drill — 0.2.0 stray words' counts as a record
|
||||
# here and does not there. Two sibling guards disagreeing about what the same
|
||||
# heading means is the same trap as disagreeing with release-notes.sh.
|
||||
record="$(awk -v ver="$ver" -v dash="—" '
|
||||
/^## / {
|
||||
grab = ($2 == "Release" && $3 == "drill" && $4 == dash && $5 == ver \
|
||||
&& (NF == 5 || $6 == dash))
|
||||
next
|
||||
}
|
||||
grab && NF { print }
|
||||
' "$runs")"
|
||||
|
||||
if [ -z "$record" ]; then
|
||||
{
|
||||
|
|
|
|||
|
|
@ -798,6 +798,30 @@ describe("drill-recorded.sh — a release version has a drill record", () => {
|
|||
expect(r.output).toContain("no drill record for version '0.2.0'");
|
||||
});
|
||||
|
||||
// ...and whitespace is not evidence either. The first cut extracted with
|
||||
// `sed '/./,$!d'`, where `.` matches a space, so a heading followed by one
|
||||
// tab passed the gate — while the comment above the extractor claimed the
|
||||
// opposite. An evidence-free release for the price of an invisible
|
||||
// character, on the one check whose whole job is to demand evidence.
|
||||
// Found independently by all three reviewers on #138.
|
||||
it("a record body of only spaces and tabs fails (#138)", async () => {
|
||||
const runs = `# Drill runs\n\n${heading("0.2.0")}\n \n\t\n\n## Notes\n\nUnrelated.\n`;
|
||||
const r = await check(treeWith("blank", "0.2.0", runs));
|
||||
expect(r.code).toBe(1);
|
||||
expect(r.output).toContain("no drill record for version '0.2.0'");
|
||||
});
|
||||
|
||||
// The sibling guards must agree about what a heading IS, not just about the
|
||||
// version in it. box#149 requires the version to be the last field or be
|
||||
// followed by the em dash; without the same constraint here, this heading is
|
||||
// a record in cast and not in box.
|
||||
it("a stray tail after the version is not a heading — in step with box#149", async () => {
|
||||
const runs = `# Drill runs\n\n## Release drill — 0.2.0 stray words\n${legs}`;
|
||||
const r = await check(treeWith("tail", "0.2.0", runs));
|
||||
expect(r.code).toBe(1);
|
||||
expect(r.output).toContain("no drill record for version '0.2.0'");
|
||||
});
|
||||
|
||||
// The version is matched WHOLE, both directions — release-notes.sh's trap,
|
||||
// solved the same way (awk field equality, no regex, no dot-escaping). A
|
||||
// release candidate's drill is not the release's drill: different tree,
|
||||
|
|
|
|||
Loading…
Reference in a new issue