From: Plom Heller Date: Wed, 23 Sep 2026 10:28:42 +0000 (+0200) Subject: On git server setup right-away self-host this very repo. X-Git-Url: https://plomlompom.com/repos/%7B%7B%20web_path%20%7D%7D/static/booking/%7B%7Bdb.prefix%7D%7D/edit?a=commitdiff_plain;h=00313dc191467f101aba86b25e8519243b2a3a83;p=confplom On git server setup right-away self-host this very repo. --- diff --git a/CLAUDE.md b/CLAUDE.md index 6e5e816..10d999d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -561,13 +561,14 @@ root's home; as the user it picks up entries added after provisioning. ## `setup_git_server.sh` Optional, run by hand on an already-set-up target to turn it into a git -host — infrastructure only, no specific repo. Motivation: a fresh system -with no SSH keys exchanged can't use `install_server.sh`; a git host lets it -instead anonymously `git clone git:///.git`. Repos themselves -are added afterwards, one at a time, by `adopt_repo.sh` (below). No -arguments; safe to rerun — every step here is idempotent, since the one -thing worth protecting against a rerun (an already-adopted repo) is state -`adopt_repo.sh` owns, not this script. +host. Motivation: a fresh system with no SSH keys exchanged can't use +`install_server.sh`; a git host lets it instead anonymously +`git clone git:///.git`. The one repo this script hosts itself +is this repo's own landed copy (`${PATH_REPO}`, e.g. `/opt/confplom`), +since it's always already there and is exactly what such a fresh system +most needs; every other repo is added afterwards, one at a time, by +`adopt_repo.sh` (below). No arguments; safe to rerun — every step is +idempotent or skipped if already done. 1. `apt-get -y update` + install `git` (a bare target may lack it). 2. `mkdir -p "${PATH_GIT_BASE}"` (`/srv/git`) and `chown` it (not `-R`) to @@ -575,10 +576,10 @@ thing worth protecting against a rerun (an already-adopted repo) is state it (over `ssh`, via `adopt_repo.sh`) without sudo — root SSH is already disabled by `start_root_server.sh`, and `git://` itself is read-only, so this is the only way a repo ever gets created or updated there. - Deliberately not recursive: repos under `${PATH_GIT_BASE}` are - `adopt_repo.sh`'s to own, not this script's to reassert on a rerun — e.g. - a future repo owned by some other account shouldn't get silently - reclaimed by a rerun of this one. + Deliberately not recursive: repos under `${PATH_GIT_BASE}` are owned + repo by repo (step 4 below, or `adopt_repo.sh`), not reasserted on a + rerun — e.g. a future repo owned by some other account shouldn't get + silently reclaimed by a rerun of this one. 3. Render `templates/git-daemon.service` (`User=${USERNAME}`; never run a network-facing daemon as root needlessly) running `git daemon --reuseaddr --base-path=/srv/git /srv/git` — one daemon, @@ -586,8 +587,36 @@ thing worth protecting against a rerun (an already-adopted repo) is state under `${PATH_GIT_BASE}`, so it's never touched again per repo; `systemctl daemon-reload` + `enable --now git-daemon` (systemd is already present; no inetd needed). -4. Print the clone/push URL patterns and a reminder that git daemon listens - on 9418/tcp in case a firewall is added later. +4. **Self-hosting**, the same-host counterpart of `adopt_repo.sh`'s steps + 2–3, skipped (narrated) if `${PATH_GIT_BASE}/.git` already + exists: `git clone --bare --no-hardlinks` from `${PATH_REPO}` (a bare + clone takes exactly branches and tags); `remote remove origin`, since a + bare clone still records its source as `origin`, meaningless there; the + same `git-daemon-export-ok` + `receive.deny*` hardening as `adopt_repo.sh` + (duplicated rather than shared: `adopt_repo.sh` sends it as an `ssh` + command string, which a `lib/` helper can't serve); then `chown -R` of + just this one new repo to `${USERNAME}`, the account every later push + arrives as. `--no-hardlinks` because a local clone otherwise hardlinks + object files, and that `chown -R` would then also re-own + `${PATH_REPO}`'s objects. The early sanity check that `${PATH_REPO}` has + a `.git` runs before anything else changes. Unlike `adopt_repo.sh` this + can't repoint the checkout actually committed in (that's on another + machine), so it prints the `git remote set-url` line to run there; until + then the server is merely seeded, not authoritative. The snapshot may be + older than that checkout, which is fine as long as nothing since was + rewritten — a rewrite would now be refused by `denyNonFastForwards`. +5. Point `${PATH_REPO}`'s own `origin` at `git://localhost/.git` + (`set-url`, or `add` if none), unconditionally, so the landed copy can + later be refreshed with `git pull` instead of another `install_server.sh` + tar. Via `git://` rather than the local path because git refuses to + operate in a repo owned by another user (`safe.directory`), root not + exempt: a local-path fetch runs `upload-pack` as root inside the + `${USERNAME}`-owned bare repo, whereas over `git://` that side is run by + `git-daemon` as `${USERNAME}` itself. Also read-only, fitting a copy + nobody should push from. +6. Print the workstation repoint line, the clone/push URL patterns and a + reminder that git daemon listens on 9418/tcp in case a firewall is added + later. ## `adopt_repo.sh` diff --git a/scripts/setup_git_server.sh b/scripts/setup_git_server.sh index 768c3e3..4890b3e 100755 --- a/scripts/setup_git_server.sh +++ b/scripts/setup_git_server.sh @@ -1,13 +1,23 @@ #!/bin/sh . "$(dirname "$0")/_lib.sh" +include DIRNAME_REPO include PATH_GIT_BASE +include PATH_REPO include USERNAME +include error include msg include render_template +include try_quiet # constants derived from changeables FNAME_GIT_DAEMON_UNIT=git-daemon.service PATH_GIT_DAEMON_UNIT="/etc/systemd/system/${FNAME_GIT_DAEMON_UNIT}" +PATH_SELF_HOSTED="${PATH_GIT_BASE}/${DIRNAME_REPO}.git" +URL_SELF_HOSTED_LOCAL="git://localhost/${DIRNAME_REPO}.git" + +# early sanity check, so we fail before changing anything +[ -d "${PATH_REPO}/.git" ]\ + || error "${PATH_REPO}: not a git checkout, so cannot host it" msg 'Ensuring installation of git …' apt-get -y update @@ -23,7 +33,31 @@ render_template "${FNAME_GIT_DAEMON_UNIT}" >| "${PATH_GIT_DAEMON_UNIT}" systemctl daemon-reload systemctl enable --now git-daemon -msg 'Ready. Adopt a repo onto this server from wherever it lives with:' +if [ -e "${PATH_SELF_HOSTED}" ]; then + msg '%s already exists, leaving it alone.' "${PATH_SELF_HOSTED}" +else + msg 'Hosting this very repo at %s, hardened against history loss …' \ + "${PATH_SELF_HOSTED}" + git clone --quiet --bare --no-hardlinks "${PATH_REPO}" \ + "${PATH_SELF_HOSTED}" + git -C "${PATH_SELF_HOSTED}" remote remove origin + touch "${PATH_SELF_HOSTED}/git-daemon-export-ok" + git -C "${PATH_SELF_HOSTED}" config receive.denyNonFastForwards true + git -C "${PATH_SELF_HOSTED}" config receive.denyDeletes true + chown -R "${USERNAME}:${USERNAME}" "${PATH_SELF_HOSTED}" +fi + +msg "Pointing %s's origin at %s …" "${PATH_REPO}" "${URL_SELF_HOSTED_LOCAL}" +if try_quiet git -C "${PATH_REPO}" remote get-url origin; then + git -C "${PATH_REPO}" remote set-url origin "${URL_SELF_HOSTED_LOCAL}" +else + git -C "${PATH_REPO}" remote add origin "${URL_SELF_HOSTED_LOCAL}" +fi + +msg 'Ready. To make the checkout you commit in push here, run there:' +msg ' git remote set-url origin ssh://%s@%s' \ + "${USERNAME}" "${PATH_SELF_HOSTED}" +msg 'Adopt any other repo onto this server from wherever it lives with:' msg ' sh scripts/adopt_repo.sh ' msg 'Anonymous clone URL pattern (read-only): git:///.git' msg 'Push URL pattern (read-write): ssh://%s@%s/.git' \