From d705dfb6316e8766d3e8d9519c65d91749c109a5 Mon Sep 17 00:00:00 2001 From: yeasy Date: Thu, 23 Jul 2026 07:40:28 -0700 Subject: [PATCH] fix(ci): make the Mermaid fail-closed guard actually fire MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guard added earlier could never trigger. tools/render_mermaid.py has no nonzero exit path — its own docstring said "Exits 0 even if some/all fail (non-fatal)" — so `if ! render_mermaid.py ...; then exit 1; fi` never ran its body, and a build that rendered zero diagrams still passed. Reproduced by running the script with no Chrome on PATH: it printed the fallback warning and exited 0. Adds a --strict flag that exits 1 when Chrome is missing or any diagram fails to render, and passes it from every workflow invocation. The default stays lenient so local mobile-reader builds keep working, which is what the exit-0 behaviour was actually for. Verified both directions: with no Chrome, --strict exits 1 and the default exits 0; against the real book with Chrome, --strict renders every diagram and exits 0. --- .github/workflows/auto-release.yml | 2 +- tools/render_mermaid.py | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml index d4a0b8c..d9c7c50 100644 --- a/.github/workflows/auto-release.yml +++ b/.github/workflows/auto-release.yml @@ -100,7 +100,7 @@ jobs: run: | title=$(python3 -c 'import json; print(json.load(open("book.json", encoding="utf-8"))["title"])') PATH="$GITHUB_WORKSPACE/node_modules/.bin:$PATH" \ - python3 tools/render_mermaid.py --book-dir . --svg-out "$RUNNER_TEMP/mmsvg" + python3 tools/render_mermaid.py --book-dir . --svg-out "$RUNNER_TEMP/mmsvg" --strict python3 tools/build_html_reader.py \ --book-dir . \ --title "$title" \ diff --git a/tools/render_mermaid.py b/tools/render_mermaid.py index 5124136..035d138 100644 --- a/tools/render_mermaid.py +++ b/tools/render_mermaid.py @@ -5,7 +5,8 @@ Extracts ```mermaid blocks in SUMMARY.md order and renders them with mermaid-cli pointing puppeteer at a system Chrome (CHROME_BIN env or auto-detected). Chunked + retried because a single large mmdc pass can crash headless Chrome. Diagrams that still fail are simply left out — build_mobile_book.py shows their source as fallback. -Writes d-1.svg .. d-N.svg into --svg-out. Exits 0 even if some/all fail (non-fatal). +Writes d-1.svg .. d-N.svg into --svg-out. Exits 0 even if some/all fail, so local +mobile-reader builds still work; pass --strict (CI does) to exit 1 instead. """ import os, re, sys, glob, time, shutil, subprocess, argparse @@ -13,6 +14,8 @@ ap = argparse.ArgumentParser() ap.add_argument("--book-dir", default=".") ap.add_argument("--svg-out", required=True) ap.add_argument("--chunk", type=int, default=25) +ap.add_argument("--strict", action="store_true", + help="exit 1 if Chrome is missing or any diagram fails to render") a = ap.parse_args() BOOK, SVG = os.path.abspath(a.book_dir), os.path.abspath(a.svg_out) shutil.rmtree(SVG, ignore_errors=True); os.makedirs(SVG) @@ -39,7 +42,10 @@ if N == 0: chrome = os.environ.get("CHROME_BIN") or next( (shutil.which(n) for n in ["google-chrome-stable","google-chrome","chromium-browser","chromium","chrome"] if shutil.which(n)), None) if not chrome: - print("WARNING: no Chrome found -> all diagrams will fall back to source"); sys.exit(0) + msg = "no Chrome found -> all diagrams would fall back to source" + if a.strict: + print(f"Mermaid rendering failed: {msg}", file=sys.stderr); sys.exit(1) + print(f"WARNING: {msg}"); sys.exit(0) print(f"using Chrome: {chrome}") pptr = os.path.join(SVG, "_pptr.json") open(pptr, "w").write('{"executablePath":"%s","args":["--no-sandbox","--disable-gpu","--disable-dev-shm-usage"]}' % chrome) @@ -74,3 +80,7 @@ for att in range(4): for f in glob.glob(os.path.join(SVG, "*.json")) + glob.glob(os.path.join(SVG, "_chunk.md")): os.remove(f) print(f"RENDERED {done()}/{N} diagrams") +if a.strict and done() < N: + missing = [i + 1 for i in range(N) if not os.path.isfile(os.path.join(SVG, f"d-{i+1}.svg"))] + print(f"Mermaid rendering failed for diagrams: {missing}", file=sys.stderr) + sys.exit(1)