Commit 3cd375119f

3cd375119f0ebe4fe3e851e547d0cd5ff8683c82

parent: fc693369d1

Unsigned ci/build: success

cmc <hello@cleberg.net> · 2026-08-31 05:06 UTC

Add a powered-by-orgo footer note, versioned at build time (!1)

Adds `Powered by orgo <version>` to the site footer, linking to
https://gitbay.org/krz/orgo. Every HTML page extends `base.html`, so all
198 carry it.

orgo exposes no version to templates and `[site]` in `orgo.toml` rejects
unknown keys, so the version is substituted into the built HTML by
`build.py`, which reads it from `orgo --version` after the build. The
template holds the current version as a seed and fallback for builds that
bypass `build.py` (`orgo serve`).

The rewrite runs on every page rather than only the re-rendered ones: the
build cache keys on the cache format version, not orgo's release version,
so pages carried over from a build by an older orgo would otherwise keep
printing it. A build where the note matches nothing exits 1.

Verified: clean prod build (199 pages, 0 unresolved links) substitutes on
198 pages; a page hand-edited to an old version is corrected on a
fully-cached rebuild; removing the note fails the build.

Layout: unified · split

.gitbay/ci.yml +16 −8
@@ -45,13 +45,20 @@ jobs:
4545 # orgo is installed from crates.io and takes minutes to compile, so it is
4646 # rebuilt only when this version changes. Nothing bumps it automatically:
4747 # raise it by hand, which is also how you find out orgo broke the site.
48 #
49 # --root pins where it lands, like ruff and lychee below. Without it cargo
50 # installs under whatever cargo home the runner sets, which is not $HOME:
51 # the binary goes somewhere this file does not name, and the next run's
52 # `cargo install` declines to repeat itself because its metadata already
53 # records the version as installed.
4854 - |
4955 set -eu
5056 v=0.22.0
51 if ! "$HOME/.cargo/bin/orgo" --version 2>/dev/null | grep -qx "orgo $v"; then
52 "$HOME/.cargo/bin/cargo" install orgo --version "$v" --locked
57 if ! "$HOME/tools/orgo/bin/orgo" --version 2>/dev/null | grep -qx "orgo $v"; then
58 "$HOME/.cargo/bin/cargo" install orgo --version "$v" --locked \
59 --root "$HOME/tools/orgo"
5360 fi
54 "$HOME/.cargo/bin/orgo" --version
61 "$HOME/tools/orgo/bin/orgo" --version
5562 python3 --version
5663
5764 # build.py deploys with rsync when DEPLOY=true and ENV=prod. This build
@@ -74,7 +81,7 @@ jobs:
7481 exit 1
7582 EOF
7683 chmod +x .ci-bin/rsync
77 PATH="$PWD/.ci-bin:$HOME/.cargo/bin:$PATH" ENV=prod BUILD=true python3 build.py
84 PATH="$PWD/.ci-bin:$HOME/tools/orgo/bin:$PATH" ENV=prod BUILD=true python3 build.py
7885
7986 - |
8087 set -eu
@@ -99,10 +106,11 @@ jobs:
99106 | sh -s -- -y --no-modify-path --profile minimal
100107 fi
101108 v=0.22.0
102 if ! "$HOME/.cargo/bin/orgo" --version 2>/dev/null | grep -qx "orgo $v"; then
103 "$HOME/.cargo/bin/cargo" install orgo --version "$v" --locked
109 if ! "$HOME/tools/orgo/bin/orgo" --version 2>/dev/null | grep -qx "orgo $v"; then
110 "$HOME/.cargo/bin/cargo" install orgo --version "$v" --locked \
111 --root "$HOME/tools/orgo"
104112 fi
105 "$HOME/.cargo/bin/orgo" --version
113 "$HOME/tools/orgo/bin/orgo" --version
106114
107115 # Prebuilt release binary, checksum-verified, kept in $HOME between runs.
108116 - |
@@ -125,7 +133,7 @@ jobs:
125133 # URLs to /img/, which is served from a different docroot and would look
126134 # like 198 broken links here. The development output keeps them absolute,
127135 # so the image host gets checked for real.
128 - $HOME/.cargo/bin/orgo build content -o .build-dev --strict
136 - $HOME/tools/orgo/bin/orgo build content -o .build-dev --strict
129137
130138 # Internal links are the half worth gating on: they are entirely within
131139 # this repo, so a failure is always a real defect and always fixable here.
README.md +3 −2
@@ -66,8 +66,9 @@ For users employing Doom Emacs, open any repository Org file using
6666
6767## Building and Publishing the Site
6868
69The `build.py` script wraps the build: it runs orgo, and then either
70deploys the result or serves it locally.
69The `build.py` script wraps the build: it runs orgo, rewrites the footer's
70orgo version to match the orgo that ran, and then either deploys the result
71or serves it locally.
7172
7273Environment variables control what it does, and all default to off:
7374
build.py +59
@@ -4,6 +4,7 @@ This script automates the process of building and deploying the website.
44It handles tasks such as:
55
66- Running orgo to generate site content.
7- Rewriting the footer's orgo version to match the orgo that built the site.
78- Rewriting image URLs for the onion service (production only).
89- Optionally deploying the built site to a remote server.
910- Starting a local development server for previewing changes.
@@ -77,6 +78,63 @@ def rewrite_img_urls(build_dir=".build"):
7778 print(f"Rewrote {count} img.cleberg.net references to /img/")
7879
7980
81# The footer's "Powered by orgo <version>" note. Matching the whole phrase rewrites a
82# page carrying an old version as readily as one still carrying the template's
83# placeholder, and leaves prose that ends a paragraph with a link to orgo alone.
84ORGO_NOTE = re.compile(
85 r'(Powered by <a href="https://gitbay\.org/krz/orgo">orgo</a> )[^<]*(</p>)'
86)
87
88
89def orgo_version():
90 """The version reported by the orgo on PATH, e.g. "0.22.0"."""
91 result = subprocess.run(
92 ["orgo", "--version"], capture_output=True, text=True, check=False
93 )
94 if result.returncode != 0:
95 print("Could not read the orgo version:", file=sys.stderr)
96 print(result.stderr, file=sys.stderr)
97 sys.exit(1)
98 # `orgo --version` prints "orgo X.Y.Z". Anything else means the output format
99 # changed, and guessing at it would ship a wrong version to every page.
100 parts = result.stdout.split()
101 if len(parts) != 2 or parts[0] != "orgo":
102 print(f"Unexpected `orgo --version` output: {result.stdout!r}", file=sys.stderr)
103 sys.exit(1)
104 return parts[1]
105
106
107def rewrite_orgo_version(build_dir):
108 """Point the footer's orgo version at the orgo that just built the site.
109
110 Runs over every page rather than only the re-rendered ones, because the build cache
111 keys on a cache *format* version and not on orgo's release version: after a version
112 bump that leaves the format alone, the pages carried over from the previous build
113 would otherwise keep printing the old one.
114
115 The whole point of this is that the note cannot go stale, so a footer that no longer
116 matches is a failure and not a no-op — matching nothing anywhere means the note was
117 removed or its markup changed, and a silent pass would ship the template's
118 placeholder to every page.
119 """
120 version = orgo_version()
121 count = 0
122 for html in Path(build_dir).rglob("*.html"):
123 text = html.read_text(encoding="utf-8")
124 new_text, n = ORGO_NOTE.subn(rf"\g<1>{version}\g<2>", text)
125 if n and new_text != text:
126 html.write_text(new_text, encoding="utf-8")
127 count += n
128 if count == 0:
129 print(
130 "No 'Powered by orgo' note found in the build — the footer in "
131 "content/templates/base.html no longer matches ORGO_NOTE",
132 file=sys.stderr,
133 )
134 sys.exit(1)
135 print(f"Set the footer orgo version to {version} on {count} pages")
136
137
80138def run_orgo_build(build_dir):
81139 """
82140 Build the site with orgo.
@@ -164,6 +222,7 @@ def main():
164222
165223 if os.environ.get("BUILD", "").casefold() == "true":
166224 run_orgo_build(build_dir)
225 rewrite_orgo_version(build_dir)
167226 # The onion needs same-origin images; dev previews keep the absolute URLs.
168227 # Runs over every page, not just the re-rendered ones, so a page carried over
169228 # from an earlier build is rewritten too.
content/templates/base.html +1
@@ -93,6 +93,7 @@
9393<a href="{{ root }}tips/index.html">Tips</a> &middot;
9494<a href="https://iheartrss.com/">I &hearts; RSS</a>
9595<p>[ <a href="https://krz.sh">krz</a> &middot; <a href="https://audit-labs.dev">audit labs</a> ]</p>
96<p>Powered by <a href="https://gitbay.org/krz/orgo">orgo</a> ORGO_VERSION</p>
9697</footer>
9798</body>
9899</html>