Repository navigation
build(site): move generators out of the web root, add a pull-based deploy - #1538
Conversation
…ploy site/ held two build scripts, make-og-image.sh and make-banner.py, alongside the files it serves. Any deploy that copied the directory had to remember to exclude them, and the one written for #1523 forgot the second. Move both to tools/site/. make-og-image.sh now cds into ../../site, so its inputs and outputs are unchanged; make-banner.py takes every path as an argument and only its usage example needed the new location. Add tools/site/deploy.sh. It runs on the web server, pulls the public repository (no credential), keeps one rolling backup, mirrors site/ into the web root with rsync --delete, and records the deployed commit in .deployed-sha. The trigger is manual, documented in tools/site/README.md. Closes #1523. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019cEymaSpmZ1arjngxx1VNM
|
Claude finished @frankbria's task in 5m 58s —— View job Third-party bug-hunt reviewMethod: read the full diff against The earlier review already covered the unguarded
Everything else checked out clean: the Model: k3[1m] |
ReviewMoving the generators out of 1. [ -f "$SRC/site/index.html" ] || { echo "refusing: $SRC/site/index.html missing" >&2; exit 1; }2. No locking and a non-atomic publish (minor). 3. Stale doc references (minor).
4. Deploy script details (nits).
Tests: there's no automated coverage, which is reasonable for a shell script on the server. None of this is blocking, but I'd add the guard in #1 before merging, since |
|
Claude finished @frankbria's task in 3m 8s —— View job Third-party bug-hunt review (synchronize)
One new finding survived verification:
Everything else checked out clean: the shallow Model: k3[1m] |
Follow-up reviewThe diff hasn't changed since the two earlier reviews. The findings below are still open, and I'm not adding new ones. Still worth fixing before merge
Nits
The rest looks fine. 🤖 Generated with Claude Code |
Two defects found in review on #1538, both reproduced and fixed: - First deploy on a fresh server aborted: the backup tar ran before anything created the web root, and `set -e` stopped the run with an error that did not name the cause. Reproduced in a clean debian container (`tar: /var/www: Cannot open`). The backup is now taken only when the web root exists, and the root is created before the sync. - A re-run at the same commit replaced the last-good backup with the tree it was about to re-sync, which right after a bad deploy is the broken one. The backup is now skipped when the web root already holds this commit. The sync still runs, so a re-run continues to repair a hand-edited web root. Paths are overridable by environment for testing. Exercised in a clean container: first deploy with /var/www absent, no-op re-run, drift repair, new-commit backup, .well-known preserved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019cEymaSpmZ1arjngxx1VNM
|
Claude finished @frankbria's task in 3m 8s —— View job Third-party bug-hunt review (synchronize, 191baed)
One new finding survived verification:
No critical or major defects. The earlier reviews' open items (empty-source rsync guard, Model: k3[1m] |
Follow-up reviewThe rolling-backup guard and the first-deploy case are now handled in
Nit: I read the diff and did not run 🤖 Generated with Claude Code |
|
Claude finished @frankbria's task in 2m 57s —— View job Third-party bug-hunt review (synchronize, 2d89720)
One new finding survived verification:
No critical or major defects. The backup skip logic, fail-closed sha ordering, and rsync excludes re-checked clean against the current code. Model: k3[1m] |
Follow-up reviewThe diff matches what the earlier reviews covered, so I'm not adding new findings. The backup-skip and first-deploy fixes in Still open:
I didn't run 🤖 Generated with Claude Code |
Third-party bug-hunt review (synchronize, 2d89720)
One new finding survived verification:
No critical or major defects. The backup skip logic, fail-closed sha ordering, and rsync excludes all re-checked clean against the current code. Model: k3[1m] |
Found in review on #1538. A run killed mid-rsync leaves the web root a torn mix of two commits while .deployed-sha still names the old one, so the next run saw a different commit and backed the torn tree up over the good one. A marker now exists only while a sync is under way. If it survives, the previous run did not finish and the backup is skipped. The sync still runs, so the re-run repairs the torn root and then clears the marker. The marker lives beside the backup, not in the web root, so it is never served. Exercised in a clean container alongside the earlier cases: a re-run after a simulated interruption leaves the last-good backup byte-identical, repairs the root, and clears the marker. 15/15 checks pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019cEymaSpmZ1arjngxx1VNM
|
Follow-up reviewThe Still open from my earlier reviews, with no change in
Nit: I read the diff and did not run 🤖 Generated with Claude Code |
Found in review on #1538. tar wrote the backup in place, so a tar that failed part-way (disk full, read error) aborted the deploy but left a truncated archive where the last-good backup had been: the safety mechanism destroyed what it protected. It now writes to a temporary file and renames on success. Tested with a tar shim that writes a truncated archive and exits 2: the deploy aborts before any sync, the previous backup is byte-identical, and the next normal run succeeds with no stale temp file. The earlier 15 deploy checks still pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019cEymaSpmZ1arjngxx1VNM
Follow-up reviewThe temp-file-and-rename for the tar backup is in, and so is the
Nits: I read the diff and did not run 🤖 Generated with Claude Code |
|
Claude finished @frankbria's task in 2m 26s —— View job Third-party bug-hunt review (synchronize, 72a168d)
One new finding:
Full summary posted as a separate review comment with the inline suggestion on the diff. Model: k3[1m] · · Branch: |
Third-party bug-hunt review (synchronize, 72a168d)
One new finding survived verification:
No critical or major defects. The backup-skip logic, temp-file tar, and fail-closed sha ordering all re-checked clean against the current code. Model: k3[1m] |
|
Claude finished @frankbria's task in 4m 26s —— View job Third-party bug-hunt review (synchronize, 4d6b91b) — done
One new finding posted inline (minor): No critical or major defects. Full summary posted as a separate comment. Model: k3[1m] · Branch: |
Follow-up review
Still open
Nit: add a CHANGELOG note under Unreleased saying the generators moved to The 🤖 Generated with Claude Code |
Third-party bug-hunt review (synchronize, 4d6b91b)
One new finding survived verification:
No critical or major defects. The tar temp-file fix, the interrupted-run marker, and the fail-closed sha ordering all re-checked clean against the current code. Model: k3[1m] |
Found in review on #1538. .deployed-sha was written before chown, so a chown failure (say, no www-data user on a fresh server) recorded a successful deploy and stranded the in-progress marker, which then silently suppressed every later backup. Two changes. The script now refuses to start, before any backup or sync, if www-data does not exist, and says so. And success is recorded only after chown, so any later failure leaves the previous sha and the marker for the next run to repair. The README's one-time setup now names the prerequisites. Tested in a clean container: with www-data removed the script exits 1 with the reason and leaves no web root and no marker; with chown failing after the sync it records no success and keeps the marker, and the next normal run records the sha and clears it. Earlier suites still pass (15/15, 6/6). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019cEymaSpmZ1arjngxx1VNM
|
Claude finished @frankbria's task in 4m 35s —— View job I'll analyze this and get back to you. |
Follow-up reviewThe chown-before-sha ordering is now fixed: Two items from earlier reviews are still open:
Everything else looks good to merge once item 1 is addressed. 🤖 Generated with Claude Code |
Third-party bug-hunt review (synchronize, 48ec7f8)
One new finding survived verification:
No critical or major defects. The `www-data` refusal, the success-recording reorder, and the README's new prerequisite list all re-checked clean against the current code. Model: k3[1m] |
Found in review on #1538. mkdir ran before the in-progress marker, so a run killed between the two left an empty web root with no marker, which the next run took for a complete deploy and archived over the last-good backup. The marker now goes down before the first change to the web root and comes up after the last. That is the invariant the earlier fixes rely on; this was the one window it did not cover. Tested in a clean container: web root removed, marker set as a killed run would leave it, then a re-run keeps the last-good backup byte-identical, rebuilds the root and clears the marker. All earlier deploy suites still pass (15, 6, 10). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019cEymaSpmZ1arjngxx1VNM
|
Claude finished @frankbria's task in 2m 34s —— View job Third-party bug-hunt review (synchronize, 8d473e2)
✅ No defects found in Full summary posted as a separate comment. Model: k3[1m] · Branch: |
Follow-up reviewI read the current One item from earlier reviews is still open, and it is the one I'd fix before merge. Empty-source guard. If [ -f "$SRC/site/index.html" ] || { echo "refusing: $SRC/site/index.html missing" >&2; exit 1; }Locking (minor). Two overlapping runs share the 🤖 Generated with Claude Code |
Third-party bug-hunt review (synchronize, 8d473e2)
✅ Third Party Review: no defects found in One status note, not a new finding: the reply "Fixed in 8d473e2 … the marker is now set before Model: k3[1m] |
Found in review on #1538. A first clone killed part-way (OOM, reboot, dropped session) left a .git with no commit; every later run skipped the clone and failed at fetch, with nothing naming the recovery. The script now removes the checkout and clones again when $SRC/.git has no commit. Narrower than the suggested fix on purpose: SRC is overridable, so the removal only touches a git checkout with no commit. A checkout that has a commit is kept even when fetch fails, and a directory that is not a git checkout is never deleted. Tested in a clean container: a wedged checkout is re-cloned and deployed; a healthy checkout whose fetch fails aborts the run and survives; SRC pointed at an unrelated directory refuses and leaves it intact. All earlier deploy suites still pass (15, 6, 10, 5). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019cEymaSpmZ1arjngxx1VNM
|
Claude finished @frankbria's task in 2m 33s —— View job Third-party bug-hunt review (synchronize, cc4e4f3)
✅ Third Party Review: no defects found in This closes the last kill-window in the script's state machine that didn't self-heal — the partial-clone thread from the previous round is now fixed as suggested (with a sound narrowing). Status note resolved: the fix reply that had landed on the wrong thread is now backed by this commit on the correct one. |
Follow-up reviewI read the current One item from my earlier reviews is still open, and I'd fix it before merge. Empty-source guard (should fix). If [ -f "$SRC/site/index.html" ] || { echo "refusing: $SRC/site/index.html missing" >&2; exit 1; }Locking (minor). Two overlapping runs share the Tests: the PR reports 🤖 Generated with Claude Code |
Third-party bug-hunt review (synchronize, cc4e4f3)
✅ Third Party Review: no defects found in Model: k3[1m] |

Closes #1523.
What changes
site/make-og-image.shandsite/make-banner.pymove totools/site/. Everything undersite/is now servable, so a deploy copies the directory as-is — no exclude list to remember, which is the thing the first draft of Automate codeframe.sh site deploy (pull-based, no tailnet credential in CI) #1523 got wrong.make-og-image.shnowcds into../../site, so its inputs and outputs are unchanged.tools/site/deploy.shruns on the server: pulls the public repo (no credential), keeps one rolling backup, mirrorssite/into the web root withrsync --delete, writes the deployed commit to.deployed-sha.tailscale ssh root@frank-prod-server deploy-codeframe-site. Setup and usage intools/site/README.md.Against #1523's acceptance criteria
/opt/codeframe-sitetools/site/deploy.sh→/usr/local/bin/deploy-codeframe-sitesite/atorigin/mainrsync -a --delete; nothing to exclude any more/var/www/codeframe.sh/.deployed-sha--deletemake-og-image.shnot reachable over HTTPsite/at alltools/site/README.md.well-known/in the web root is excluded from--deletein case certificate renewal ever writes there.Verification
bash -npasses ondeploy.shandmake-og-image.shpy_compilepasses onmake-banner.pysite/contains no.shor.pytools/site/make-og-image.shfrom its new location: exit 0, wrotesite/og-image.pngat 1200x630; regenerated binaries discarded, not committeduv run pytest tests/ci/ -q→81 passed in 1.76s🤖 Generated with Claude Code
https://claude.ai/code/session_019cEymaSpmZ1arjngxx1VNM