summaryrefslogtreecommitdiff
path: root/scripts/docs_gates/test_gates.py
diff options
context:
space:
mode:
authorYuriy Andamasov <yuriy@vyos.io>2026-09-25 17:11:19 +0200
committerGitHub <noreply@github.com>2026-09-25 16:11:19 +0100
commita783e56b774795c1f4bd8a6088dfcce96a307c5f (patch)
treedcd0c88ff02969351840c8222ea97c72d9258e18 /scripts/docs_gates/test_gates.py
parent4c8d1d1a0f23e2106a97f4b672607121ff03a091 (diff)
downloadvyos-documentation-a783e56b774795c1f4bd8a6088dfcce96a307c5f.tar.gz
vyos-documentation-a783e56b774795c1f4bd8a6088dfcce96a307c5f.zip
ci: port the Cloudflare Workers docs pipeline to circinus (slug 1.5) (#2208)
* ci: port Cloudflare Workers docs pipeline files to circinus (verbatim) Copies the branch-agnostic half of the docs.vyos.io Cloudflare Workers pipeline from `rolling` at 8cb568bf, byte-identical: - .github/workflows/docs-build.yml - workers/ (entire tree) - scripts/docs_gates/ - docker/im-convert.sh - docs/_static/js/version-picker.js, js/pagefind-wrapper.js, css/version-picker.css (new files, no circinus counterpart) - docs/_templates/breadcrumbs.html, searchbox.html (new files) docs-build.yml already triggers on push to [rolling, circinus, sagitta] and resolves `circinus` -> worker vyos-docs-v15-en / slug 1.5 from workers/matrix.json; the files simply did not exist on this branch, so slug 1.5 still serves the bootstrap placeholder. workers/versions.json + workers/matrix.json are deliberately identical across all three branches and must be kept in sync. Advances: IS-572 * ci: wire circinus docs build into the Cloudflare Workers pipeline Hand-merges the CF-specific hunks onto circinus's own docker/Dockerfile and docs/conf.py rather than clobbering them with rolling's versions — circinus keeps its own content-driven history in both files. docker/Dockerfile: - imagemagick + librsvg2-bin (sphinx.ext.imgconverter backend) and poppler-utils (pdfinfo, used by docs-build.yml's PDF page-count completeness check), installed --no-install-recommends - install docker/im-convert.sh as /usr/local/bin/im-convert docs/conf.py: - enable sphinx.ext.imgconverter + image_converter = 'im-convert' so the LaTeX/PDF builder stops silently dropping .webp/.svg images - register js/version-picker.js + css/version-picker.css unconditionally (degrades silently on ReadTheDocs) - _vyos_cf_build gate off the raw DOCS_VERSION_SLUG env var; only CF builds load js/pagefind-wrapper.js, and html_context['vyos_cf_build'] lets _templates/searchbox.html fall back to the stock Sphinx searchbox via the "!" bang-include on RTD The RTD path stays the default in both files: circinus continues building on ReadTheDocs until RTD sunset, and every CF feature activates only when DOCS_VERSION_SLUG is present. .readthedocs.yml is untouched. circinus keeps its own version/release/html_title/source_suffix and its hardcoded html_baseurl (its CF slug is also `1.5`, so rolling's DOCS_VERSION_SLUG/READTHEDOCS_VERSION resolution block is a no-op here and was deliberately not ported). Advances: IS-572 * ci: IS-572: re-sync ported Cloudflare Workers pipeline files with rolling 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/test_gates.py')
-rw-r--r--scripts/docs_gates/test_gates.py122
1 files changed, 122 insertions, 0 deletions
diff --git a/scripts/docs_gates/test_gates.py b/scripts/docs_gates/test_gates.py
new file mode 100644
index 00000000..e65be3af
--- /dev/null
+++ b/scripts/docs_gates/test_gates.py
@@ -0,0 +1,122 @@
+import json
+import sys
+from pathlib import Path
+
+import pytest
+from scripts.docs_gates import gates
+
+
+@pytest.fixture()
+def artifact(tmp_path: Path) -> Path:
+ root = tmp_path / "en" / "rolling"
+ root.mkdir(parents=True)
+ for i in range(50):
+ (root / f"p{i}.html").write_text(
+ f'<html><head><link rel="canonical" href="https://docs.vyos.io/en/rolling/p{i}.html"/></head><body>x</body></html>'
+ )
+ (root / "index.html").write_text(
+ '<html><head><link rel="canonical" href="https://docs.vyos.io/en/rolling/index.html"/></head></html>'
+ )
+ (root / "vyos-documentation.pdf").write_bytes(b"%PDF-1.4 fake")
+ (root / "pagefind").mkdir()
+ (root / "pagefind" / "pagefind.js").write_text("// index")
+ (root / "installation").mkdir()
+ (root / "installation" / "index.html").write_text(
+ '<html><head><link rel="canonical" href="https://docs.vyos.io/en/rolling/installation/index.html"/></head></html>'
+ )
+ return tmp_path
+
+
+def versions_arg(tmp_path: Path) -> Path:
+ """Hermetic stand-in for workers/versions.json — writes a fixture-local
+ manifest with the minimal schema the gates consume (mirrors the real
+ file's shape for the rolling entry) so tests never depend on, or break
+ from, edits to the repo file."""
+ p = tmp_path / "versions.json"
+ p.write_text(json.dumps({
+ "schema_version": 2,
+ "default_lang": "en",
+ "default_version": "rolling",
+ "languages": [{"code": "en", "label": "English"}],
+ "versions": [
+ {"slug": "rolling", "label": "Rolling (development)", "status": "dev",
+ "binding": "DOCS_ROLLING", "aliases": ["latest"],
+ "pdf": "/en/rolling/vyos-documentation.pdf"},
+ ],
+ }))
+ return p
+
+
+@pytest.fixture()
+def versions(tmp_path: Path) -> Path:
+ return versions_arg(tmp_path)
+
+
+def test_pass_on_good_artifact(artifact: Path, versions: Path):
+ rc = gates.run(artifact=artifact, slug="rolling", versions=versions,
+ previous_meta=None, critical=["index.html", "installation/index.html"])
+ assert rc == 0
+
+
+def test_fail_on_missing_critical_page(artifact: Path, versions: Path):
+ (artifact / "en/rolling/installation/index.html").unlink()
+ rc = gates.run(artifact=artifact, slug="rolling", versions=versions,
+ previous_meta=None, critical=["index.html", "installation/index.html"])
+ assert rc == 1
+
+
+def test_fail_on_count_collapse(artifact: Path, versions: Path, tmp_path: Path):
+ meta = tmp_path / "meta.json"
+ meta.write_text(json.dumps({"sha": "old", "page_count": 5000})) # previous build 100x bigger
+ rc = gates.run(artifact=artifact, slug="rolling", versions=versions,
+ previous_meta=meta, critical=["index.html"])
+ assert rc == 1
+
+
+def test_fail_on_alias_canonical(artifact: Path, versions: Path):
+ (artifact / "en/rolling/bad.html").write_text(
+ '<html><head><link rel="canonical" href="https://docs.vyos.io/en/latest/bad.html"/></head></html>'
+ )
+ rc = gates.run(artifact=artifact, slug="rolling", versions=versions,
+ previous_meta=None, critical=["index.html"])
+ assert rc == 1
+
+
+def test_fail_on_missing_canonical(artifact: Path, versions: Path):
+ (artifact / "en/rolling/nocanon.html").write_text(
+ '<html><head></head><body>no canonical link at all</body></html>'
+ )
+ rc = gates.run(artifact=artifact, slug="rolling", versions=versions,
+ previous_meta=None, critical=["index.html"])
+ assert rc == 1
+
+
+def test_fail_on_oversize_file(artifact: Path, versions: Path):
+ big = artifact / "en/rolling/huge.bin"
+ big.write_bytes(b"\0" * (26 * 1024 * 1024)) # > 25 MiB
+ rc = gates.run(artifact=artifact, slug="rolling", versions=versions,
+ previous_meta=None, critical=["index.html"])
+ assert rc == 1
+
+
+def test_fail_when_declared_pdf_missing(artifact: Path, versions: Path):
+ (artifact / "en/rolling/vyos-documentation.pdf").unlink()
+ rc = gates.run(artifact=artifact, slug="rolling", versions=versions,
+ previous_meta=None, critical=["index.html"])
+ assert rc == 1
+
+
+def test_critical_list_strips_before_testing_for_comments(monkeypatch, tmp_path, artifact):
+ # An INDENTED comment used to survive the `line.startswith("#")` test (applied to the
+ # UNSTRIPPED line) and become a live critical-page entry. A comment can never exist as a
+ # file, so it would block every deploy with "critical page missing: en/rolling/ # ...".
+ crit = tmp_path / "critical.txt"
+ crit.write_text("# leading comment\n # indented comment\n\n index.html \n")
+ seen: list[str] = []
+ monkeypatch.setattr(gates, "run",
+ lambda art, slug, versions, prev, critical: seen.extend(critical) or 0)
+ monkeypatch.setattr(sys, "argv", [
+ "gates", "--artifact", str(artifact), "--slug", "rolling",
+ "--versions", str(versions_arg(tmp_path)), "--critical-list", str(crit)])
+ assert gates.main() == 0
+ assert seen == ["index.html"]