refactor(infra): converge on setup+bootstrap pattern, fix systemd copy bug

- setup_server.sh: full rewrite to match materia/template pattern — adds Docker
  install, git/curl/ca-certificates apt install, age + sops install (arch-aware),
  uv install as service user, age keypair generation, SSH config write (root+chown);
  removes systemd unit copy (was buggy: copied before repo was cloned)
- NEW bootstrap_supervisor.sh: ~45 lines — age key check, clone/fetch, tag checkout,
  sops decrypt, uv sync, copy landing-backup + supervisor systemd units, enable + start
- deploy.sh: replace 53-line self-install preamble (sops/age install + keypair
  generation + exit-1 flow) with simple sops check + decrypt; Docker blue/green
  logic unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Deeman
2026-02-27 06:57:00 +01:00
parent 8b74d6a76b
commit 5218717e8d
3 changed files with 224 additions and 120 deletions

View File

@@ -1,60 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail
# ── Ensure sops + age are installed ───────────────────────
APP_DIR="$(cd "$(dirname "$0")" && pwd)"
BIN_DIR="$APP_DIR/bin"
mkdir -p "$BIN_DIR"
export PATH="$BIN_DIR:$PATH"
ARCH=$(uname -m)
case "$ARCH" in
x86_64) ARCH_SOPS="amd64"; ARCH_AGE="amd64" ;;
aarch64) ARCH_SOPS="arm64"; ARCH_AGE="arm64" ;;
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
esac
if ! command -v age &>/dev/null; then
echo "==> Installing age to $BIN_DIR..."
AGE_VERSION="v1.3.1"
curl -fsSL "https://dl.filippo.io/age/${AGE_VERSION}?for=linux/${ARCH_AGE}" -o /tmp/age.tar.gz
tar -xzf /tmp/age.tar.gz -C "$BIN_DIR" --strip-components=1 age/age age/age-keygen
chmod +x "$BIN_DIR/age" "$BIN_DIR/age-keygen"
rm /tmp/age.tar.gz
fi
# ── Decrypt secrets ───────────────────────────────────────
if ! command -v sops &>/dev/null; then
echo "==> Installing sops to $BIN_DIR..."
SOPS_VERSION="v3.12.1"
curl -fsSL "https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux.${ARCH_SOPS}" -o "$BIN_DIR/sops"
chmod +x "$BIN_DIR/sops"
fi
# ── Ensure age keypair exists ─────────────────────────────
AGE_KEY_FILE="${SOPS_AGE_KEY_FILE:-$APP_DIR/age-key.txt}"
export SOPS_AGE_KEY_FILE="$AGE_KEY_FILE"
if [ ! -f "$AGE_KEY_FILE" ]; then
echo "==> Generating age keypair..."
age-keygen -o "$AGE_KEY_FILE" 2>&1
chmod 600 "$AGE_KEY_FILE"
AGE_PUB=$(grep "public key:" "$AGE_KEY_FILE" | awk '{print $NF}')
echo ""
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "!! NEW SERVER — add this public key to .sops.yaml: !!"
echo "!! !!"
echo "!! $AGE_PUB !!"
echo "!! !!"
echo "!! Then run: sops updatekeys .env.prod.sops !!"
echo "!! Commit, push, and re-deploy. !!"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo ""
echo "ERROR: sops not found — run infra/setup_server.sh first"
exit 1
fi
# ── Decrypt secrets ───────────────────────────────────────
sops --input-type dotenv --output-type dotenv -d .env.prod.sops > .env
chmod 600 .env
sops --input-type dotenv --output-type dotenv -d "${APP_DIR}/.env.prod.sops" > "${APP_DIR}/.env"
chmod 600 "${APP_DIR}/.env"
COMPOSE="docker compose -f docker-compose.prod.yml"
LIVE_FILE=".live-slot"