summaryrefslogtreecommitdiff
path: root/scripts/docs_gates/smoke.py
diff options
context:
space:
mode:
authorYuriy Andamasov <yuriy@vyos.io>2026-08-21 23:43:26 +0300
committerYuriy Andamasov <yuriy@vyos.io>2026-08-21 23:43:26 +0300
commitbeec730d3743687482c6516dec8c15cc2bcea63b (patch)
treed0f2ab96c5b547b493fc517e42b95f37801ce48f /scripts/docs_gates/smoke.py
parent0f69d0846fef313f23c84d7dff8b5ae8e24ec04c (diff)
downloadvyos-documentation-claude/cf-port-circinus.tar.gz
vyos-documentation-claude/cf-port-circinus.zip
ci: IS-572: re-sync ported Cloudflare Workers pipeline files with rollingclaude/cf-port-circinus
The category-1 files in this port are byte-identical copies from `rolling`. `rolling` has since moved: [vyos-documentation#2209](https://github.com/vyos/vyos-documentation/pull/2209) merged as `3a1c6c30`, thirteen rounds of hardening on exactly these files. Re-take all 14 category-1 paths from `origin/rolling` via `git checkout origin/rolling -- <paths>`, so byte-identity holds by construction rather than by hand-editing: .github/workflows/docs-build.yml scripts/docs_gates/{gates,parity,smoke,test_gates,test_parity,test_smoke}.py workers/.gitignore workers/apex/src/{index,special,uagate}.ts workers/apex/test/{router,uagate}.test.ts workers/apex/ua-policy.json Thirteen of the fourteen carry [vyos-documentation#2209](https://github.com/vyos/vyos-documentation/pull/2209) exactly โ€” the pre-change tree was byte-identical to `3a1c6c30^` for those paths. `workers/.gitignore` additionally picks up the one-line `test-results/` entry from [vyos-documentation#2212](https://github.com/vyos/vyos-documentation/pull/2212); inert on circinus, since only the deliberately-unported `apex-deploy.yml` writes that directory. Deliberate exclusions are unchanged: `docs-canary-qa.yml` (cron runs on the default branch only, so it is not ported even though [vyos-documentation#2209](https://github.com/vyos/vyos-documentation/pull/2209) touched it on `rolling`), `apex-deploy.yml`, and the `docs-preview-*` workflows. `docs/conf.py` stays hand-merged and circinus-specific, with its ReadTheDocs fallback intact. ๐Ÿค– Generated by [robots](https://vyos.io)
Diffstat (limited to 'scripts/docs_gates/smoke.py')
-rw-r--r--scripts/docs_gates/smoke.py44
1 files changed, 37 insertions, 7 deletions
diff --git a/scripts/docs_gates/smoke.py b/scripts/docs_gates/smoke.py
index 770e93e3..6c526d98 100644
--- a/scripts/docs_gates/smoke.py
+++ b/scripts/docs_gates/smoke.py
@@ -15,9 +15,11 @@ from __future__ import annotations
import argparse
import dataclasses
import json
+import os
import sys
import time
import urllib.request
+from pathlib import Path
APEX_PATHS = ["/versions.json", "/healthz", "/robots.txt", "/sitemap.xml"]
SEARCH_MOUNT_MARKER = 'id="vyos-search"'
@@ -73,7 +75,14 @@ def probe_plan(slug: str, pdf: str | None, critical: list[str]) -> list[Probe]:
plan = [Probe(f"/en/{slug}/{rel}", 200, True, False) for rel in ["index.html", *critical]]
plan.append(Probe(f"/en/{slug}/pagefind/pagefind.js", 200, True, False))
if pdf:
- plan.append(Probe(pdf, 200, True, False))
+ # assert_docs_build=False: the PDF is the ONE content path that can legitimately be
+ # answered by the apex Worker instead of a branch content Worker. 1.3's PDF (29.2 MiB)
+ # exceeds the 25 MiB static-asset cap, so apex serves it straight from R2 (spec ยง5) and
+ # that response carries only etag / accept-ranges / content-type / content-length โ€”
+ # X-Docs-Build is a content-Worker header apex never sets on it. Asserting it made the
+ # probe structurally unpassable for 1.3 (observed nightly: "detail=status+docs-build").
+ # The build SHA is still gated for this version: every HTML probe above asserts it.
+ plan.append(Probe(pdf, 200, False, False))
plan.append(Probe(f"/en/{slug}/definitely-missing-page-xyz.html", 404, False, False))
plan += [Probe(p, 200, False, True) for p in APEX_PATHS]
plan[0].assert_search_mount = True # plan[0] is always /en/<slug>/index.html
@@ -192,14 +201,35 @@ def main() -> int:
ap.add_argument("--host", required=True)
ap.add_argument("--slug", required=True)
ap.add_argument("--expect-sha", required=True)
- ap.add_argument("--access-id", required=True)
- ap.add_argument("--access-secret", required=True)
ap.add_argument("--pdf", default=None)
- ap.add_argument("--critical-list", default="scripts/docs_gates/critical-pages.txt")
+ ap.add_argument("--critical-list", type=Path,
+ default=Path("scripts/docs_gates/critical-pages.txt"))
a = ap.parse_args()
- critical = [line.strip() for line in open(a.critical_list).read().splitlines()
- if line.strip() and not line.startswith("#")]
- return run(a.host, a.slug, a.expect_sha, a.access_id, a.access_secret, a.pdf, critical)
+ # CF Access service-token credentials are read ONLY from the environment. They were also
+ # accepted as --access-id/--access-secret flags; that is removed rather than merely
+ # discouraged, because a value passed in argv publishes it in the process command line โ€”
+ # readable from the process table for the lifetime of the process, and captured verbatim
+ # by `set -x` shell traces, crash dumps and process-listing tooling. No call site used
+ # the flags (docs-build.yml and docs-canary-qa.yml both export the env vars), so there is
+ # nothing to migrate. Neither value nor its length is ever echoed.
+ access_id = os.environ.get("CF_ACCESS_CLIENT_ID", "")
+ access_secret = os.environ.get("CF_ACCESS_CLIENT_SECRET", "")
+ missing = [name for name, value in (
+ ("CF_ACCESS_CLIENT_ID", access_id),
+ ("CF_ACCESS_CLIENT_SECRET", access_secret)) if not value]
+ if missing:
+ # The canary host is Access-gated, so an empty credential would turn every probe into
+ # an indistinguishable 403 โ€” fail loudly on the cause instead. Names only, no values.
+ print(f"missing CF Access credentials: {', '.join(missing)}", file=sys.stderr)
+ return 2
+ # Strip BEFORE the comment test: an indented " # note" line is a comment, not a page that
+ # every deployable build must contain (it would fail the probe as a missing critical page).
+ # read_text() (rather than a bare open().read()) closes the handle deterministically,
+ # matching gates.py; the bare form leaked the descriptor until GC on any interpreter
+ # without CPython's refcounting.
+ lines = (line.strip() for line in a.critical_list.read_text().splitlines())
+ critical = [line for line in lines if line and not line.startswith("#")]
+ return run(a.host, a.slug, a.expect_sha, access_id, access_secret, a.pdf, critical)
if __name__ == "__main__":