#!/bin/bash # Bootstrap Materia supervisor after setup_server.sh + adding keys. # Run once on the server after SSH deploy key is added to GitLab # and the server age key is committed to .env.prod.sops. # # Usage: # ssh root@ 'bash -s' < infra/bootstrap_supervisor.sh # # Prerequisites: # - setup_server.sh already run (beanflows_service user, SSH deploy key, age keypair, uv) # - Deploy key added to GitLab (Settings → Repository → Deploy Keys) # - Server age public key added to .sops.yaml + .env.prod.sops committed + pushed set -euo pipefail SERVICE_USER="beanflows_service" REPO_DIR="/opt/materia" GITEA_REPO="ssh://git@git.padelnomics.io:2222/deemanone/materia.git" UV="/home/${SERVICE_USER}/.local/bin/uv" [ "$(id -u)" = "0" ] || { echo "ERROR: Run as root"; exit 1; } # ── Check age keypair ───────────────────────────────────────────────────────── AGE_KEY_FILE="/home/${SERVICE_USER}/.config/sops/age/keys.txt" if [ ! -f "${AGE_KEY_FILE}" ]; then echo "ERROR: Age keypair not found at ${AGE_KEY_FILE}" echo "Run infra/setup_server.sh first, then add the printed keys, then re-run." exit 1 fi # ── Clone or update repository ──────────────────────────────────────────────── if [ -d "${REPO_DIR}/.git" ]; then sudo -u "${SERVICE_USER}" git -C "${REPO_DIR}" fetch --tags --prune-tags origin else sudo -u "${SERVICE_USER}" git clone \ "${GITEA_REPO}" "${REPO_DIR}" fi LATEST_TAG=$(sudo -u "${SERVICE_USER}" \ git -C "${REPO_DIR}" tag --list --sort=-version:refname "v*" | head -1) if [ -n "${LATEST_TAG}" ]; then sudo -u "${SERVICE_USER}" git -C "${REPO_DIR}" checkout --detach "${LATEST_TAG}" fi # ── Decrypt secrets ─────────────────────────────────────────────────────────── sudo -u "${SERVICE_USER}" bash -c \ "SOPS_AGE_KEY_FILE=/home/${SERVICE_USER}/.config/sops/age/keys.txt \ sops --input-type dotenv --output-type dotenv -d ${REPO_DIR}/.env.prod.sops > ${REPO_DIR}/.env" chmod 600 "${REPO_DIR}/.env" # ── Python dependencies ─────────────────────────────────────────────────────── sudo -u "${SERVICE_USER}" bash -c "cd ${REPO_DIR} && ${UV} sync --all-packages" # ── Systemd supervisor service ──────────────────────────────────────────────── cp "${REPO_DIR}/infra/supervisor/materia-supervisor.service" /etc/systemd/system/ systemctl daemon-reload systemctl enable --now materia-supervisor # ── R2 backup timer (optional) ──────────────────────────────────────────────── # Enabled only when R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, and R2_ENDPOINT # are present in .env. Idempotent: rclone.conf is always regenerated from env # (so credential rotations take effect on re-run); systemd units are only copied # when they differ from what's already installed. _env_get() { grep -E "^${1}=" "${REPO_DIR}/.env" 2>/dev/null | head -1 | cut -d= -f2- | tr -d '"'"'" || true; } R2_KEY=$(_env_get R2_ACCESS_KEY_ID) R2_SECRET=$(_env_get R2_SECRET_ACCESS_KEY) R2_ENDPOINT=$(_env_get R2_ENDPOINT) if [ -n "${R2_KEY}" ] && [ -n "${R2_SECRET}" ] && [ -n "${R2_ENDPOINT}" ]; then echo "$(date '+%H:%M:%S') ==> Configuring R2 backup..." RCLONE_CONF_DIR="/home/${SERVICE_USER}/.config/rclone" RCLONE_CONF="${RCLONE_CONF_DIR}/rclone.conf" sudo -u "${SERVICE_USER}" mkdir -p "${RCLONE_CONF_DIR}" # Always write from env so credential rotations take effect automatically. cat > "${RCLONE_CONF}" </dev/null 2>&1; then cp "${REPO_DIR}/infra/backup/${unit}" /etc/systemd/system/ UNITS_CHANGED=1 fi done [ "${UNITS_CHANGED}" = "1" ] && systemctl daemon-reload systemctl enable --now materia-backup.timer echo "$(date '+%H:%M:%S') ==> R2 backup timer enabled." else echo "$(date '+%H:%M:%S') ==> R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY / R2_ENDPOINT not set — skipping backup timer." fi echo "" echo "=== Bootstrap complete! ===" echo "" echo "Check status: systemctl status materia-supervisor" echo "View logs: journalctl -u materia-supervisor -f" echo "Workflow status: sudo -u ${SERVICE_USER} ${UV} run -p ${REPO_DIR} python src/materia/supervisor.py status" echo "Backup timer: systemctl list-timers materia-backup.timer" echo "Tag: $(sudo -u "${SERVICE_USER}" git -C "${REPO_DIR}" describe --tags --always)"