clone

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

Keep repos private through backup and restore

cat committed acb4b5c
A backup stages git-daemon-export-ok and forge/readers with the rest of
a repo's state, and a restore neither re-creates the flag nor serves a
private repo's sites again, so a restored forge shows what the old one
did and nothing more.
commit
acb4b5ccdbf384f89e15b62da3f0061f9a2b4a6f
parent
cc80fa3fa9b28afe1bacda9a7f2d7b7b9245f091
author
cat <admin@dev.site>
kind
fix risk score 1.4 of 3, Jev's estimate
2 files changed, +41 -11
Mbin/forge17+++++---------
Atest/scenarios/private-backup.sh35++++++++++++++++++++++++++++++
M bin/forge +6 -11
@@ -2017,7 +2017,8 @@ repack_repo() {
20172017 # even when it is taken mid-push, so it isn't read from the live files. Each20182018 # run first stages a copy, per repo in this order:20192019 # 1. everything but refs and objects: review logs, comments, forge/jev,2020-# config, HEAD;2020+# config, HEAD, and who may read it (git-daemon-export-ok, whose absence2021+# makes a repo private, and forge/readers);20212022 # 2. the refs, read once by git into packed-refs;20222023 # 3. the objects, as hard links, which cost nothing because object files20232024 # never change once written. Loose objects go first, then packs, so an
@@ -2394,7 +2395,8 @@ cmd_backups() {
23942395 23952396 # What a backup leaves out, rebuilt for a forge whose files were just23962397 # restored: what server init and keys sync write, hooks pointing at this2397-# host's forge, pinned review revisions, extracted sites, every page.2398+# host's forge, pinned review revisions, extracted sites, every page. Which2399+# repos are private came back with their files, and nothing here changes it.23982400 restore_finish() {23992401 load_config24002402 cmd_server_init
@@ -2408,15 +2410,8 @@ restore_finish() {
24082410 cat "$GIT_DIR"/forge/reviews/* 2>/dev/null | awk '$1 == "rev" { print $3 }' | sort -u |24092411 git cat-file --batch-check='%(objectname) %(objecttype)' |24102412 awk '$2 == "commit" { print "update refs/forge/keep/" $1 " " $1 }' | git update-ref --stdin2411- for ref in $(git for-each-ref --format='%(refname)' refs/sites/ refs/site-previews/); do2412- case $ref in2413- refs/sites/*) deploy_site "sites/$OWNER/$NAME/${ref#refs/sites/}" "$ref" ;;2414- *)2415- rest=${ref#refs/site-previews/}2416- deploy_site "previews/$OWNER/$NAME/${rest%%/*}/$(enc_ref "${rest#*/}")" "$ref"2417- ;;2418- esac2419- done2413+ # A private repo's sites stay unserved (sites_public), as before the backup.2414+ if sites_public "$GIT_DIR"; then redeploy_sites; fi24202415 git update-server-info24212416 unset GIT_DIR24222417 done
A test/scenarios/private-backup.sh +35 -0
@@ -0,0 +1,35 @@
1+# A backup keeps which repos are private and who reads them: restoring one2+# (forge restore --from, as .forge/ci does) brings back a private repo still3+# private, with its readers, and builds no pages or sites for it.4+. "$(dirname "$0")/../lib.sh"5+user dami6+user ana7+repo --private dami/secret8+repo dami/open9+for r in secret open; do10+ clone dami "dami/$r" "$T/w/$r"11+ at "$T/w/$r" commit "first"12+ ok push_as dami "$T/w/$r" origin main "$(site_commit "$T/w/$r" docs):refs/sites/docs"13+done14+ok ssh_as dami readers dami/secret add ana15+16+ok forge backup --dry-run17+st=$FORGE_ROOT/.forge/backup/stage18+missing "$st/dami/secret.git/git-daemon-export-ok"19+exists "$st/dami/open.git/git-daemon-export-ok"20+eq "$(cat "$st/dami/secret.git/forge/readers")" ana21+22+r=$T/restored23+ok env FORGE_ROOT="$r" FORGE_WWW="$r/.forge/www" FORGE_CACHE="$T/cache2" "$FORGE" restore --from "$st"24+missing "$r/dami/secret.git/git-daemon-export-ok"25+exists "$r/dami/open.git/git-daemon-export-ok"26+eq "$(cat "$r/dami/secret.git/forge/readers")" ana27+exists "$r/dami/secret.git/hooks/pre-receive"28+missing "$r/.forge/www/dami/secret"29+lacks "$r/.forge/www/repos.json" 'dami/secret'30+missing "$r/.forge/www/sites/dami/secret/docs"31+exists "$r/.forge/www/dami/open/index.html"32+exists "$r/.forge/www/sites/dami/open/docs/index.html"33+# And it is still private there: init again (every boot) changes nothing.34+ok env FORGE_ROOT="$r" FORGE_WWW="$r/.forge/www" "$FORGE" server init35+missing "$r/dami/secret.git/git-daemon-export-ok"