clone

a personal forge on plain git: static pages, stacked-diff reviews, signed local ci, repo sites and releases

Build private pages in a scratch directory under .forge/private

cat committed d6847cf
A private repo's build wrote its pages, and hard-linked its release
files, in .forge/build.<pid> before swapping them into the private root,
so while it ran they sat outside the directory only the forge can read.
It now builds in .forge/private/build.<pid>, beside the private root:
nothing of the repo is outside .forge/private even mid-build, and the
swaps stay renames on one filesystem (the assets go to www/assets, under
.forge too). A scenario records where each build writes through a
stand-in node, which 11ty runs through env, and checks the private one
is under .forge/private while the public one is not.
commit
d6847cf986c1e0201935d18ac3579379418e2a3a
parent
42f0525d2830eb35c14937ab5160e120de0a2f25
author
cat <admin@dev.site>
ci
✓ passed in 153s, signed by cat
kind
fix risk score 1.6 of 3, Jev's estimate
review
#4 Keep private repos' release trees in a store only the forge reads
ci attestation
forge-attestation 1
type ci
commit d6847cf986c1e0201935d18ac3579379418e2a3a
tree 15de173105194a2e2f6cbaf932de21bdd9b00e43
script .forge/ci e6f038ae810b87f90a4be115a762a9c91dd76a01
result pass
exit 0
started 2026-10-18T22:41:58Z
duration 153s
runner (not shown here; refs/notes/ci has it)
system Darwin 25.5.0 arm64
env host
log sha256:dd14cb7ebf2b0db4281d99cf5c1c7db4817f8e528c604ac1286f1d061e5dec91 239 lines
|remote: review #4: /dami/greet/reviews/4/
|pushed wip/i18n
|80f6c72 running .forge/ci: greet.sh: say what it is
|  | sh: /bin/sh
|  | ok   greet
|80f6c72 pass in 0s, signed
|pushed main
|remote: review #4: /dami/greet/reviews/4/
|pushed wip/i18n
|91a04ba running .forge/ci: wave at the world
|  | sh: /bin/sh
|  | FAIL greet : got 'hello, world o/', want 'hello, world'
|91a04ba fail in 1s, signed
|forge: 1 commit(s) did not pass
|remote: review #6: /dami/greet/reviews/6/
|pushed ana/wave
|fbfe1e4 running .forge/ci: update the test for the wave
|  | sh: /bin/sh
|  | ok   greet
|fbfe1e4 pass in 0s, signed
|remote: review #6: /dami/greet/reviews/6/
|pushed ana/wave
|warning: You appear to have cloned an empty repository.
|site home: deployed 0d38233 -> /sites/ana/dotfiles/home/
|created ana/notes
|remote: review #1: /ana/notes/reviews/1/
|pushed ana/todo
|landing #1 A todo list: ana/todo -> main
|fcc06fb running .forge/ci: web, server and Python versions of greet
|  | sh: /bin/sh
|  | ok   greet
|fcc06fb pass in 0s, signed
|pushed main
|4ee2865 running .forge/ci: messages: notes for the later regions
|  | sh: /bin/sh
|  | ok   greet
|4ee2865 pass in 0s, signed
|remote: review #7: /dami/greet/reviews/7/
|pushed dami/regions
|created dami/forge
|warning: You appear to have cloned an empty repository.
|site guide: deployed ba26723 -> /sites/dami/forge/guide/
|(node:29014) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built ana/dotfiles in 2s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.GCuIR6/wt/demo/root/.forge/www
|(node:31958) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built ana/notes in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.GCuIR6/wt/demo/root/.forge/www
|(node:32983) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built dami/forge in 1s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.GCuIR6/wt/demo/root/.forge/www
|(node:33938) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built dami/greet in 2s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.GCuIR6/wt/demo/root/.forge/www
|(node:35409) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time
|(Use `node --trace-warnings ...` to show where the warning was created)
|built - in 0s -> /var/folders/b4/mv8w4c8j0zj0_mldppxdbdw00000gn/T/forge-ci.GCuIR6/wt/demo/root/.forge/www
|226 of 226 pages render the same without 11ty
|guide: 4 files, links ok
|ok

Signature: good, cat

3 files changed, +56 -3
MREADME.md5+++-
Mbin/forge8++++-
Atest/scenarios/private-build-scratch.sh46++++++++++++++++++++++++++++++
M README.md +4 -1
M bin/forge +6 -2
A test/scenarios/private-build-scratch.sh +46 -0
@@ -0,0 +1,46 @@
1+# A private repo's build writes its pages, and links its release files, in2+# a scratch directory under .forge/private, which only the forge can read,3+# never in .forge/build.*, so nothing of the repo is outside it even4+# mid-build; the swap into the private root keeps the links hard links.5+# A stand-in node first on PATH (11ty runs through env node) notes where6+# each build writes, and how its parent is kept, while it runs.7+. "$(dirname "$0")/../lib.sh"8+user dami9+repo --private dami/secret10+repo dami/open11+clone dami dami/secret "$T/w/d"12+at "$T/w/d" commit "first"13+at "$T/w/d" git tag v114+ok push_as dami "$T/w/d" origin main15+mkdir "$T/f"16+printf 'secret tool\n' >"$T/f/tool"17+FORGE_USER=dami ok at "$T/w/d" "$FORGE" release publish v1 "$T/f/tool"18+19+mkdir "$T/bin"20+cat >"$T/bin/node" <<EOF21+#!/bin/sh22+case \${1:-} in *eleventy*) printf '%s %s %s\n' "\${FORGE_PRIVATE:-0}" "\$FORGE_WWW" "\$(ls -ld "\$(dirname "\$FORGE_WWW")" | cut -c1-10)" >>"$T/builds" ;; esac23+exec "$(command -v node)" "\$@"24+EOF25+chmod +x "$T/bin/node"26+PATH=$T/bin:$PATH build dami/secret27+PATH=$T/bin:$PATH build dami/open28+29+eq "$(grep -c . "$T/builds")" 230+set -- $(sed -n 1p "$T/builds")31+eq "$1" 132+case $2 in "$FORGE_ROOT/.forge/private/build."[0-9]*) ;; *) fail "the private build wrote in $2" ;; esac33+eq "$3" drwx------34+missing "$2"35+set -- $(sed -n 2p "$T/builds")36+eq "$1" 037+case $2 in "$FORGE_ROOT/.forge/build."[0-9]*) ;; *) fail "the public build wrote in $2" ;; esac38+if find "$FORGE_ROOT/.forge" -maxdepth 2 -name 'build.[0-9]*' | grep -q .; then fail "a build left its scratch directory"; fi39+40+p=$FORGE_ROOT/.forge/private/www/dami/secret41+exists "$p/index.html"42+tree=$(git --git-dir="$FORGE_ROOT/dami/secret.git" rev-parse 'refs/releases/v1^{tree}')43+inode() { ls -i "$1" | awk '{ print $1 }'; }44+eq "$(inode "$p/releases/download/v1/tool")" "$(inode "$FORGE_ROOT/.forge/private/store/$tree/tool")"45+missing "$FORGE_WWW/dami/secret"46+exists "$FORGE_WWW/assets"