Make catalog PDF links host-relative and add a hostname rewrite script.
Some checks are pending
offline / test (push) Waiting to run
Some checks are pending
offline / test (push) Waiting to run
New builds write path-relative PDF annotations so the tree can be served from docs.verae-time.net (or any host at the site root) without a rebuild. scripts/rewrite-docs-host.py retargets an already-built site when absolute URLs are preferred. Live service doors stay on their own hostnames.
This commit is contained in:
parent
85b5b53ea9
commit
20155de96b
4 changed files with 246 additions and 40 deletions
|
|
@ -7,6 +7,7 @@ defaults to PDF links; index-md.html is the Markdown/HTML index.
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
|
|
@ -17,6 +18,11 @@ from urllib.parse import unquote
|
|||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SITE = ROOT / "site"
|
||||
CSS = ROOT / "scripts" / "docs-print.css"
|
||||
# Display label only. Catalog file links are relative so the tree can move hosts.
|
||||
DOCS_PUBLIC_HOST = os.environ.get("DOCS_PUBLIC_HOST", "zapier.georgelambert.org")
|
||||
# If set (e.g. https://docs.verae-time.net), PDF annotations become absolute.
|
||||
# Empty (default) writes path-relative URIs that work on any hostname at site root.
|
||||
DOCS_PUBLIC_URL = os.environ.get("DOCS_PUBLIC_URL", "").rstrip("/")
|
||||
|
||||
SECTIONS = [
|
||||
(
|
||||
|
|
@ -180,7 +186,7 @@ def render_md(src: Path, html_dest: Path, pdf_dest: Path, title: str) -> str:
|
|||
header.write_text(f"<style>{css_text}</style>\n", encoding="utf-8")
|
||||
banner.write_text(
|
||||
f'<div class="doc-banner">'
|
||||
f'<nav class="site"><a href="/">zapier.georgelambert.org</a>'
|
||||
f'<nav class="site"><a href="/">{DOCS_PUBLIC_HOST}</a>'
|
||||
f' · <a href="/index-md.html">Markdown indexes</a></nav>'
|
||||
f'<div class="kicker">Verae Time × Zapier</div>'
|
||||
f"<h1>{title}</h1>"
|
||||
|
|
@ -318,45 +324,68 @@ def _web_target(site: Path, rel: str, index: dict[str, list[str]] | None = None)
|
|||
return rel
|
||||
|
||||
|
||||
_DOCS_ORIGINS = (
|
||||
"https://zapier.georgelambert.org/",
|
||||
"http://zapier.georgelambert.org/",
|
||||
)
|
||||
|
||||
|
||||
def _href_for(pdf: Path, site: Path, target: str, frag: str = "") -> str:
|
||||
target = (target or "index.html").lstrip("/") or "index.html"
|
||||
dest = site / target
|
||||
if DOCS_PUBLIC_URL:
|
||||
return DOCS_PUBLIC_URL + "/" + target + frag
|
||||
rel = os.path.relpath(dest.resolve(), pdf.parent.resolve())
|
||||
return Path(rel).as_posix() + frag
|
||||
|
||||
|
||||
def rewrite_pdf_uris(site: Path) -> None:
|
||||
"""Turn WeasyPrint file:///… annotations into https://zapier.georgelambert.org/…"""
|
||||
"""Turn WeasyPrint file:///… annotations into host-relative catalog paths.
|
||||
|
||||
Default is a path relative to the PDF (works on any hostname at the site
|
||||
root). Set DOCS_PUBLIC_URL to emit absolute URLs instead.
|
||||
"""
|
||||
try:
|
||||
from pypdf import PdfReader, PdfWriter
|
||||
from pypdf.generic import NameObject, create_string_object
|
||||
except ImportError:
|
||||
print("pypdf missing; PDF URI rewrite skipped")
|
||||
return
|
||||
web = "https://zapier.georgelambert.org/"
|
||||
prefix = site.resolve().as_uri().rstrip("/") + "/"
|
||||
site = site.resolve()
|
||||
prefix = site.as_uri().rstrip("/") + "/"
|
||||
origins = list(_DOCS_ORIGINS)
|
||||
if DOCS_PUBLIC_URL:
|
||||
origins.append(DOCS_PUBLIC_URL.rstrip("/") + "/")
|
||||
index = _file_index(site)
|
||||
|
||||
def resolve(raw: str) -> str:
|
||||
def resolve(pdf: Path, raw: str) -> str:
|
||||
frag = ""
|
||||
if "#" in raw:
|
||||
raw, frag = raw.split("#", 1)
|
||||
frag = "#" + frag
|
||||
mapped = raw
|
||||
target = None
|
||||
if raw.startswith(prefix):
|
||||
mapped = web + _web_target(site, raw[len(prefix) :], index)
|
||||
target = _web_target(site, raw[len(prefix) :], index)
|
||||
elif raw.startswith("file:"):
|
||||
name = unquote(raw.rsplit("/", 1)[-1] if "/" in raw else raw)
|
||||
if not name or name in {".", "/", "file:"}:
|
||||
mapped = web
|
||||
target = "index.html"
|
||||
else:
|
||||
hit = _web_target(site, name, index)
|
||||
if (site / hit).exists():
|
||||
mapped = web + hit
|
||||
target = hit
|
||||
elif "index-md" in raw:
|
||||
mapped = web + "index-md.html"
|
||||
target = "index-md.html"
|
||||
else:
|
||||
mapped = web
|
||||
elif raw.startswith(web):
|
||||
hit = _web_target(site, raw[len(web) :], index)
|
||||
if (site / hit).is_file() or (site / hit).is_dir():
|
||||
mapped = web + hit
|
||||
if mapped != raw:
|
||||
return mapped + frag
|
||||
return raw + frag
|
||||
target = "index.html"
|
||||
else:
|
||||
for origin in origins:
|
||||
if raw.startswith(origin):
|
||||
target = _web_target(site, raw[len(origin) :], index)
|
||||
break
|
||||
if target is None:
|
||||
return raw + frag
|
||||
return _href_for(pdf, site, target, frag)
|
||||
|
||||
n_pdf = 0
|
||||
n_fix = 0
|
||||
|
|
@ -379,7 +408,7 @@ def rewrite_pdf_uris(site: Path) -> None:
|
|||
if not uri:
|
||||
continue
|
||||
raw = str(uri)
|
||||
new = resolve(raw)
|
||||
new = resolve(pdf, raw)
|
||||
if new != raw:
|
||||
action[NameObject("/URI")] = create_string_object(new)
|
||||
changed = True
|
||||
|
|
@ -391,7 +420,8 @@ def rewrite_pdf_uris(site: Path) -> None:
|
|||
writer.write(fh)
|
||||
tmp.replace(pdf)
|
||||
n_pdf += 1
|
||||
print(f"rewrote {n_fix} PDF URIs in {n_pdf} files → {web}")
|
||||
mode = DOCS_PUBLIC_URL + "/" if DOCS_PUBLIC_URL else "relative paths"
|
||||
print(f"rewrote {n_fix} PDF URIs in {n_pdf} files → {mode}")
|
||||
|
||||
|
||||
def convert_all_markdown(copied: list[tuple[Path, Path, str]]) -> list[str]:
|
||||
|
|
@ -489,7 +519,7 @@ def page_shell(now: str, body: str, switch_to_md: bool, title: str) -> str:
|
|||
<a class="skip" href="#main">Skip to content</a>
|
||||
<header>
|
||||
{switch}
|
||||
<div class="kicker">zapier.georgelambert.org</div>
|
||||
<div class="kicker">{DOCS_PUBLIC_HOST}</div>
|
||||
<h1>Verae Time × Zapier documentation</h1>
|
||||
<p>Project reference: setup, architecture, NATS, module APIs, user guide, and operator console.
|
||||
Catalog defaults to <strong>colored PDFs</strong>. Generated {now}.</p>
|
||||
|
|
@ -849,7 +879,7 @@ def main() -> None:
|
|||
"Zapier /zapier/v1, jobs, archive, chain (MOCK_VERAE in lab)."),
|
||||
("fleet.png", "https://fleet.zapier.georgelambert.org/", "Operator console",
|
||||
"Fleet replicas, Trace, Docs. Pause/stop honored by keep."),
|
||||
("docs.png", "https://zapier.georgelambert.org/", "This catalog",
|
||||
("docs.png", "/", "This catalog",
|
||||
"Colored PDF documentation site."),
|
||||
("git.png", "https://git.georgelambert.org/", "Forgejo",
|
||||
"Independent module repos (SSH 2223)."),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue