|
| 1 | +#!/usr/bin/env bash |
| 2 | +# /usr/libexec/mios/git-root-init.sh |
| 3 | +# |
| 4 | +# Initialize the deployed root `/` as a git working tree of the local |
| 5 | +# Forgejo `mios.git` mirror. Runs once via mios-git-root-init.service |
| 6 | +# after mios-forge-firstboot.service has created the admin user and |
| 7 | +# the operator's mios.git repo on the local Forgejo instance. |
| 8 | +# |
| 9 | +# Architecture invariant (mios_root_git memory): the deployed `/` IS a |
| 10 | +# git working tree. Operators edit files at FHS paths, `git commit`, |
| 11 | +# push to localhost:3000, the Forgejo Runner builds a new OCI image, |
| 12 | +# and `bootc switch` swaps to it on the next boot. Without /.git |
| 13 | +# present, the loop is broken -- the operator can't `git diff /` to |
| 14 | +# see what diverged from the deployed image. |
| 15 | +# |
| 16 | +# What this script does: |
| 17 | +# 1. Skip if /.git already present (idempotent; sentinel-free). |
| 18 | +# 2. Wait for the local Forgejo to respond on http://localhost:3000/ |
| 19 | +# (up to 60s). Forgejo bootstrap can take 30-45s on first boot. |
| 20 | +# 3. Probe whether the operator's mios.git repo exists at |
| 21 | +# http://localhost:3000/<user>/mios.git. If absent, skip and |
| 22 | +# log -- operator initializes via the Forgejo web UI or CLI. |
| 23 | +# 4. `git -C / init -b main` |
| 24 | +# 5. `git -C / remote add origin http://localhost:3000/<user>/mios.git` |
| 25 | +# 6. `git -C / fetch origin main` -- pull the upstream tree. |
| 26 | +# 7. `git -C / reset --soft FETCH_HEAD` -- adopt the FETCH_HEAD as |
| 27 | +# HEAD without touching the working tree (the deployed files |
| 28 | +# already match the image; `reset --soft` only updates the index |
| 29 | +# so `git status` shows no changes). |
| 30 | +# |
| 31 | +# After this runs, `git status` at / works, `git diff` shows operator |
| 32 | +# edits since deploy, and `git push` against localhost:3000 triggers |
| 33 | +# the Forgejo Runner build. |
| 34 | + |
| 35 | +set -euo pipefail |
| 36 | + |
| 37 | +# shellcheck source=/usr/lib/mios/paths.sh |
| 38 | +source /usr/lib/mios/paths.sh 2>/dev/null || true |
| 39 | +: "${MIOS_VAR_DIR:=/var/lib/mios}" |
| 40 | + |
| 41 | +_log() { |
| 42 | + logger -t mios-git-root-init "$*" 2>/dev/null || true |
| 43 | + echo "[git-root-init] $*" >&2 |
| 44 | +} |
| 45 | + |
| 46 | +# Idempotent: /.git is the sentinel. |
| 47 | +if [[ -d /.git ]]; then |
| 48 | + _log "/.git already present; nothing to do" |
| 49 | + exit 0 |
| 50 | +fi |
| 51 | + |
| 52 | +# Resolve the operator's username (drives the Forgejo repo path). |
| 53 | +MIOS_USER="${MIOS_USER:-mios}" |
| 54 | +if [[ -r /etc/mios/install.env ]]; then |
| 55 | + # shellcheck disable=SC1091 |
| 56 | + set -a; source /etc/mios/install.env 2>/dev/null || true; set +a |
| 57 | +fi |
| 58 | +MIOS_USER="${MIOS_USER:-mios}" |
| 59 | + |
| 60 | +FORGE_URL="${MIOS_FORGE_URL:-http://localhost:3000}" |
| 61 | +REPO_URL="${FORGE_URL}/${MIOS_USER}/mios.git" |
| 62 | + |
| 63 | +# Wait for Forgejo to be up. mios-forge.service can take 30-45s for |
| 64 | +# the first SQLite-DB write after a fresh container start. |
| 65 | +_log "waiting for Forgejo at ${FORGE_URL} ..." |
| 66 | +for _ in $(seq 1 60); do |
| 67 | + if curl -fsS --max-time 2 -o /dev/null "${FORGE_URL}/api/v1/version" 2>/dev/null; then |
| 68 | + _log "Forgejo reachable" |
| 69 | + break |
| 70 | + fi |
| 71 | + sleep 2 |
| 72 | +done |
| 73 | +if ! curl -fsS --max-time 2 -o /dev/null "${FORGE_URL}/api/v1/version" 2>/dev/null; then |
| 74 | + _log "Forgejo never came up; skipping git-root-init" |
| 75 | + exit 0 |
| 76 | +fi |
| 77 | + |
| 78 | +# Probe whether the mios.git repo exists. If not, the operator hasn't |
| 79 | +# created it yet -- skip; they'll init via web UI / git push. |
| 80 | +if ! curl -fsS --max-time 2 -o /dev/null "${REPO_URL}/info/refs?service=git-upload-pack" 2>/dev/null; then |
| 81 | + _log "${REPO_URL} not yet present; create it on Forgejo first, then re-run" |
| 82 | + exit 0 |
| 83 | +fi |
| 84 | + |
| 85 | +# Init / as a git tree without disturbing the deployed working tree. |
| 86 | +_log "git init / + remote add origin ${REPO_URL}" |
| 87 | +git -C / init -b main |
| 88 | +git -C / config core.fileMode false # prevent perm-noise on read-only composefs |
| 89 | +git -C / config core.autocrlf false |
| 90 | +git -C / config user.email "${MIOS_USER}@$(hostname).local" |
| 91 | +git -C / config user.name "${MIOS_USER}" |
| 92 | +git -C / remote add origin "${REPO_URL}" |
| 93 | + |
| 94 | +_log "fetching origin main ..." |
| 95 | +if git -C / fetch --depth=1 origin main 2>&1 | logger -t mios-git-root-init; then |
| 96 | + # Adopt FETCH_HEAD as HEAD without rewriting the working tree. |
| 97 | + # `reset --soft` only moves the branch pointer + index; the on-disk |
| 98 | + # files (which already match the image build) stay put. `git status` |
| 99 | + # then shows whatever the operator has edited since deploy. |
| 100 | + git -C / reset --soft FETCH_HEAD |
| 101 | + _log "/.git initialized; HEAD = $(git -C / rev-parse HEAD)" |
| 102 | +else |
| 103 | + _log "fetch failed; /.git left unset" |
| 104 | + exit 1 |
| 105 | +fi |
0 commit comments