From e0fe442373431af8dd8c70b5e37e275faa158f82 Mon Sep 17 00:00:00 2001 From: "Youri T. K. K. Mattar" Date: Tue, 11 Aug 2026 17:39:19 -0300 Subject: [PATCH] fix(release): an empty code block in the notes, and a check for the next one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported by the first person to read the v0.1.0 release notes. It came from editing the heredoc that writes them by string substitution: the insertion ended with an opening fence directly above a closing fence that was already there, so the two paired up around nothing. The diff looked fine. Nothing rendered the result, and Markdown has no compiler to complain. So CI has one. check-release-notes.py renders the header the way the workflow does and fails on unbalanced fences or empty blocks — the two mistakes that survive review by looking correct in a diff. The published v0.1.0 notes were edited in place. The release itself worked on its first run, and is verified against what was published rather than what was built: both tarballs' checksums match, and the binaries out of both report 0.1.0 — the aarch64 half under emulation. The versioned image pulls anonymously. Third time in this project that the failure was editing text without looking at the output, and the second time it reached somebody. Named as a pattern in ROADMAP rather than left as three separate slips. --- .github/scripts/check-release-notes.py | 56 ++++++++++++++++++++++++++ .github/workflows/ci.yml | 6 +++ .github/workflows/release.yml | 3 -- ROADMAP.md | 25 ++++++++++++ 4 files changed, 87 insertions(+), 3 deletions(-) create mode 100755 .github/scripts/check-release-notes.py diff --git a/.github/scripts/check-release-notes.py b/.github/scripts/check-release-notes.py new file mode 100755 index 0000000..c7333ef --- /dev/null +++ b/.github/scripts/check-release-notes.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +"""Render the release notes header the way the workflow does, and check it. + +The v0.1.0 notes shipped with an empty ```bash block in them. It came from +editing the heredoc by string substitution — inserting a closing fence, some +prose and an opening fence directly above a closing fence that was already +there. Nothing rendered the result before it was published. + +Markdown has no compiler, so this is the compiler: unbalanced fences and empty +code blocks are the two failures that survive review by looking fine in a diff. +""" + +import pathlib +import sys + +WORKFLOW = pathlib.Path(".github/workflows/release.yml") +START = "cat > header.md <<'EOF'\n" +END = "\n EOF\n" + + +def main() -> int: + text = WORKFLOW.read_text() + if START not in text or END not in text: + print(f"{WORKFLOW}: no release-notes heredoc to check") + return 1 + + body = text.split(START, 1)[1].split(END, 1)[0] + # The heredoc is indented ten spaces inside the YAML. + header = "\n".join( + line[10:] if line.startswith(" " * 10) else line for line in body.split("\n") + ) + header = header.replace("OWNER", "example").replace("TAG", "v0.0.0") + + lines = header.split("\n") + fences = [n for n, line in enumerate(lines) if line.strip().startswith("```")] + + problems = [] + if len(fences) % 2: + problems.append(f"{len(fences)} code fences — unbalanced, so one block never closes") + + for opened, closed in zip(fences[0::2], fences[1::2]): + if not [line for line in lines[opened + 1 : closed] if line.strip()]: + problems.append(f"empty code block at lines {opened + 1}-{closed + 1}") + + if problems: + print("release notes would render wrong:") + for problem in problems: + print(f" {problem}") + return 1 + + print(f"release notes: {len(fences) // 2} code blocks, all balanced and non-empty") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a02e1b..8ade4f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,6 +68,12 @@ jobs: fi echo "README and CI agree on $WANT" + # The v0.1.0 notes shipped with an empty ```bash block, from editing the + # heredoc by string substitution and never rendering the result. + # Markdown has no compiler; this is the compiler. + - name: The release notes would render + run: python3 .github/scripts/check-release-notes.py + - name: Host tests run: cargo test -p oxinit-unit -p oxinit-graph -p oxinit-service -p oxinit-cgroup -p oxinit-user -p oxinit-ipc -p oxinit-log -p oxinit-timer diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f3a8106..e9d1893 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -130,9 +130,6 @@ jobs: `ghcr.io/OWNER/oxinit:demo` tracks `main` if you would rather have the latest. - ```bash - ``` - ### Install Download the tarball for your architecture below, check it, and put diff --git a/ROADMAP.md b/ROADMAP.md index e570fc7..ccdd6ce 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1000,6 +1000,31 @@ and runs. The lesson is the same one the bug taught, applied to the fix: the reason to run the command is that reasoning about what it will do is not the same thing. +### v0.1.0, and an empty code block in its notes + +The release workflow worked on its first run: both architectures' tarballs with +checksums, the versioned image, and notes generated from the merged pull +requests. Verified by downloading what was published rather than trusting what +was built — both checksums match, and the binaries out of both tarballs report +`0.1.0` under emulation. + +The notes shipped with an empty ```` ```bash ```` block in them, reported by +the first person to read them. + +It came from editing the heredoc that writes those notes by string +substitution: the insertion ended with an opening fence directly above a +closing fence that was already there. The diff looked fine. Nothing rendered +the result, and Markdown has no compiler to complain. + +So CI has one now. `.github/scripts/check-release-notes.py` renders the header +the way the workflow does and fails on unbalanced fences or empty blocks — +which are exactly the two mistakes that survive review by looking correct in a +diff. The published v0.1.0 notes were edited in place. + +Third time in this project that the failure was *editing text without looking +at the output*, and the second time it reached somebody. It is worth naming as +a pattern rather than as three separate slips. + ## Not doing, and why These were on the list. They are coming off it with a reason rather than