@@ 0,0 1,27 @@
+image: ubuntu/lts
+secrets:
+ - 661293bf-f6d3-4ee1-8e74-34ea2fb70598
+environment:
+ VERCEL_ORG_ID: team_OoMniSEFMV1Arj9XJTTyujIC
+ VERCEL_PROJECT_ID: prj_Xj05rp3qZR6CY9iOHtDkuYXQfx3A
+sources:
+ - https://git.sr.ht/~krisyotam/krisyotam.net
+tasks:
+ - setup: |
+ sudo apt-get update
+ sudo apt-get install -y 9base python3
+ curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
+ sudo apt-get install -y nodejs
+ sudo npm install -g vercel@latest
+ - build: |
+ cd krisyotam.net
+ python3 scripts/build_static.py . dist
+ - deploy: |
+ cd krisyotam.net
+ source ~/.config/vercel/auth.env
+ vercel deploy dist \
+ --prod \
+ --yes \
+ --token="$VERCEL_TOKEN" \
+ --meta source=sourcehut \
+ --meta commit="$(git rev-parse HEAD)"
@@ 0,0 1,109 @@
+#!/usr/bin/env python3
+"""Render the krisyotam.net Werc tree into a Vercel-ready static directory."""
+
+from __future__ import annotations
+
+import os
+from pathlib import Path
+import shutil
+import subprocess
+import sys
+
+
+def route_for(markdown_file: Path, site_root: Path) -> str:
+ relative = markdown_file.relative_to(site_root)
+ if relative.name == "index.md":
+ parent = relative.parent.as_posix()
+ return "/" if parent == "." else f"/{parent}/"
+ return f"/{relative.with_suffix('').as_posix()}"
+
+
+def output_for(route: str, output_root: Path) -> Path:
+ if route == "/":
+ return output_root / "index.html"
+ return output_root / route.strip("/") / "index.html"
+
+
+def render(repo: Path, route: str) -> bytes:
+ plan9 = Path("/usr/lib/plan9")
+ environment = os.environ.copy()
+ environment.update(
+ {
+ "PLAN9": str(plan9),
+ "PATH": ":".join(
+ [
+ str(plan9 / "bin"),
+ str(repo / "bin"),
+ str(repo / "bin" / "contrib"),
+ "/bin",
+ "/usr/bin",
+ ]
+ ),
+ "SERVER_NAME": "plan9.krisyotam.com",
+ "SERVER_PORT": "443",
+ "REQUEST_METHOD": "GET",
+ "REQUEST_URI": route,
+ "PATH_INFO": route,
+ "QUERY_STRING": "",
+ "HTTP_COOKIE": "",
+ "HTTP_HOST": "plan9.krisyotam.com",
+ "HTTPS": "on",
+ }
+ )
+ result = subprocess.run(
+ [str(plan9 / "bin" / "rc"), "./werc.rc"],
+ cwd=repo / "bin",
+ env=environment,
+ capture_output=True,
+ check=True,
+ timeout=20,
+ )
+ if b"\n\n" not in result.stdout:
+ raise RuntimeError(f"No CGI header separator while rendering {route}")
+ _, body = result.stdout.split(b"\n\n", 1)
+ return body
+
+
+def main() -> int:
+ if len(sys.argv) != 3:
+ print(f"usage: {sys.argv[0]} REPOSITORY OUTPUT", file=sys.stderr)
+ return 2
+
+ repo = Path(sys.argv[1]).resolve()
+ output_root = Path(sys.argv[2]).resolve()
+ site_root = repo / "sites" / "plan9.krisyotam.com"
+
+ if output_root.exists():
+ shutil.rmtree(output_root)
+ output_root.mkdir(parents=True)
+
+ markdown_files = sorted(
+ path
+ for path in site_root.rglob("*.md")
+ if "_werc" not in path.relative_to(site_root).parts
+ )
+ routes = [route_for(path, site_root) for path in markdown_files]
+ routes.extend(["/images/", "/sitemap"])
+
+ for route in routes:
+ destination = output_for(route, output_root)
+ destination.parent.mkdir(parents=True, exist_ok=True)
+ destination.write_bytes(render(repo, route))
+ print(f"rendered {route} -> {destination.relative_to(output_root)}")
+
+ (output_root / "404.html").write_bytes(render(repo, "/__not_found__"))
+ shutil.copytree(repo / "pub", output_root / "pub")
+ shutil.copytree(
+ repo / "images",
+ output_root / "images",
+ ignore=shutil.ignore_patterns("images"),
+ dirs_exist_ok=True,
+ )
+ shutil.copy2(repo / "pub" / "favicon.ico", output_root / "favicon.ico")
+ shutil.copy2(site_root / "sitemap.txt", output_root / "sitemap.txt")
+ print(f"rendered {len(routes)} content routes and copied static assets")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())