From 6cd0de7ccf825f2a0e78a305e191331f30427f1d Mon Sep 17 00:00:00 2001 From: rootiest Date: Sun, 26 Jul 2026 00:47:44 -0400 Subject: [PATCH] fix(docs-site): publish function pages under /reference/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cloudflare Pages reserves a top-level `functions/` directory in the deploy output for Pages Functions (server-side handlers) and strips it from the static-asset upload. The wrangler log for run 812 shows the collision exactly: 159 files uploaded, which is precisely the number of files in dist/ outside functions/ — all 108 files under it were dropped. Nothing failed loudly. Astro built all 120 pages, wrangler reported success, and the site worked under `astro preview`; only the deployed copy 404'd on every function entry and category index. Move the generated tree to /reference/ and add a test asserting the site output never emits a name Pages reserves. --- docs/build-manual.py | 9 ++++++++- docs/verify-manual.py | 25 ++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/docs/build-manual.py b/docs/build-manual.py index 64cb612..959f0b4 100644 --- a/docs/build-manual.py +++ b/docs/build-manual.py @@ -279,7 +279,14 @@ def build_site(root: Path, out: Path) -> list[dict]: continue # Section 5: category index page keeps its slot; entries explode. - slug_dir = "functions" + # + # Deliberately NOT "functions": Cloudflare Pages reserves a top-level + # `functions/` directory in the deploy output for Pages Functions + # (server-side handlers) and silently drops it from the static-asset + # upload. The pages build fine and never arrive — every entry 404s in + # production while working locally. test_site_avoids_reserved_dir + # guards this. + slug_dir = "reference" if rel.name == "index.md": target = out / slug_dir / "index.md" target.parent.mkdir(parents=True, exist_ok=True) diff --git a/docs/verify-manual.py b/docs/verify-manual.py index 4ba56af..f904d4d 100644 --- a/docs/verify-manual.py +++ b/docs/verify-manual.py @@ -203,7 +203,7 @@ def test_site_build_produces_function_pages(): sidebar = build_manual.build_site(docs / "manual", out) pages = list(out.rglob("*.md*")) assert (out / "index.md").exists(), "landing page missing" - fn_pages = [p for p in pages if "functions" in p.parts and p.name != "index.mdx"] + fn_pages = [p for p in pages if "reference" in p.parts and p.name != "index.mdx"] assert len(fn_pages) > 80, f"expected >80 function pages, got {len(fn_pages)}" assert not (out / "00-name.md").exists(), "site:false page was published" assert sidebar, "sidebar structure is empty" @@ -293,6 +293,29 @@ def test_sidebar_has_no_duplicate_functions_entry(): assert len(labels) == 15, f"expected Overview + 14 categories, got {len(labels)}" +def test_site_avoids_reserved_dir(): + """No output directory may collide with a Cloudflare Pages reserved name. + + Pages treats a top-level `functions/` directory in the deploy output as + Pages Functions (server-side handlers) and drops it from the static-asset + upload. The build succeeds, wrangler reports success, and every page under + it 404s in production while working perfectly under `astro preview` — so + only a check on the emitted tree catches it. + """ + import build_manual + + reserved = {"functions", "_worker.js", "_routes.json"} + docs = Path(__file__).parent + with tempfile.TemporaryDirectory() as d: + out = Path(d) + build_manual.build_site(docs / "manual", out) + clashes = {p.name for p in out.iterdir()} & reserved + assert not clashes, ( + f"site output has Cloudflare Pages reserved name(s): {sorted(clashes)} " + "— these are silently excluded from the deployed assets" + ) + + TESTS = [v for k, v in sorted(globals().items()) if k.startswith("test_")]