mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 03:38:23 +00:00
fix(lint): provision the mermaid browser in install-lint, not in the parallel lint run
make lint runs install-lint once and then dispatches its fifteen targets in parallel. scripts/lint/mermaid.sh provisioned chrome-headless-shell inside that parallel phase with `npx puppeteer browsers install --install-deps`, whose `apt-get satisfy` carries no lock timeout. In a fresh stack container another target held the apt lock, the shared libraries stayed missing, the script only warned, and all 289 Markdown files then failed on libnspr4.so (#795). utils/install/lint/mermaid_cli.py now provisions the browser right after mmdc, inside the serial install-lint step, as root with --install-deps and as a user without it, because puppeteer refuses to install libraries without root. A failed install deletes the chrome-headless-shell cache folder and retries once under a 900 second timeout per attempt; a cut-off download otherwise leaves a folder without its executable, and puppeteer refuses every later install of that build. The install-lint stamp counts as fresh only while the browser executable exists, so a recreated container provisions it again. mermaid.sh no longer calls apt-get or npx. It renders one minimal diagram first and, when that fails, exits with mmdc's output instead of reporting every Markdown file. Verified: tests/unit/python/scripts/lint/test_mermaid.py stubs mmdc, npx and apt-get. With a browser that cannot load libnspr4.so the script exits 1 with that line, before any Markdown file renders, and mmdc is the only tool it calls. Against main's mermaid.sh the same test fails: it calls apt-get and npx and reports the files one by one. The installer and stamp cases pass, shellcheck is clean. Not verified: a full make lint in a recreated stack container. The race reproduced in one of two runs in the issue. feature/store-i18n changed the provisioning in mermaid.sh with2f5f52bd1aanddc67bce5f0(timeout, deleting a cut-off browser). Both behaviours moved into mermaid_cli.py here, so its merge takes this version of mermaid.sh. Closes #795 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
789c1a1b63
commit
c8cb7fda8c
7 files changed
+269
-34
No files matched your search
Whitespace-only changes.
@@ -0,0 +1,62 @@
|
||||
"""``scripts/lint/mermaid.sh`` renders with the browser install-lint provisioned.
|
||||
|
||||
``make lint`` runs its targets in parallel, so the script must not reach for a
|
||||
package manager, and a browser that cannot start must fail the lint once with
|
||||
its reason instead of once per Markdown file.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from utils.cache.files import PROJECT_ROOT
|
||||
|
||||
SCRIPT = Path(PROJECT_ROOT) / "scripts" / "lint" / "mermaid.sh"
|
||||
LIBRARY_ERROR = (
|
||||
"chrome-headless-shell: error while loading shared libraries: libnspr4.so"
|
||||
)
|
||||
RECORDING_STUB = '#!/bin/sh\necho "$(basename "$0")" >> "$STUB_CALLS"\n'
|
||||
|
||||
|
||||
class TestMermaidLint(unittest.TestCase):
|
||||
def test_a_browser_that_cannot_start_fails_before_any_file_renders(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
bin_dir = Path(tmp)
|
||||
calls = bin_dir / "calls"
|
||||
calls.touch()
|
||||
for name in ("npx", "apt-get"):
|
||||
(bin_dir / name).write_text(RECORDING_STUB)
|
||||
(bin_dir / "mmdc").write_text(
|
||||
f"{RECORDING_STUB}echo '{LIBRARY_ERROR}' >&2\nexit 1\n"
|
||||
)
|
||||
for stub in bin_dir.iterdir():
|
||||
stub.chmod(0o755)
|
||||
env = {
|
||||
k: v
|
||||
for k, v in os.environ.items()
|
||||
if not k.startswith("INFINITO_") and k != "BASH_ENV"
|
||||
}
|
||||
env["PATH"] = f"{bin_dir}{os.pathsep}{env['PATH']}"
|
||||
env["STUB_CALLS"] = str(calls)
|
||||
proc = subprocess.run(
|
||||
["bash", str(SCRIPT)],
|
||||
env=env,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
recorded = calls.read_text().split()
|
||||
|
||||
self.assertEqual(proc.returncode, 1, proc.stderr)
|
||||
self.assertIn("cannot render a minimal diagram", proc.stderr)
|
||||
self.assertIn(LIBRARY_ERROR, proc.stderr)
|
||||
self.assertNotIn("❌", proc.stdout)
|
||||
self.assertEqual(recorded, ["mmdc"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user