## `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://<server>/<repo>.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://<server>/<repo>.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
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,
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}/<repo-dirname>.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/<repo>.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`
#!/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
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@<this-server>%s' \
+ "${USERNAME}" "${PATH_SELF_HOSTED}"
+msg 'Adopt any other repo onto this server from wherever it lives with:'
msg ' sh scripts/adopt_repo.sh <path-repo> <this-server>'
msg 'Anonymous clone URL pattern (read-only): git://<this-server>/<repo>.git'
msg 'Push URL pattern (read-write): ssh://%s@<this-server>%s/<repo>.git' \