refactor(infra): consolidate tool installs in setup, strip bootstrap to essentials
- setup_server.sh: add git/curl/ca-certificates apt install, add uv install as service user, fix SSH config write (root + chown vs sudo heredoc), remove noise log lines after set -e makes them redundant - bootstrap_supervisor.sh: remove all tool installs (apt, uv, sops, age) — setup_server.sh is now the single source of truth; strip to ~45 lines: age-key check, clone/fetch, tag checkout, decrypt, uv sync, systemd enable - readme.md: update step 1 and step 3 descriptions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
#!/bin/bash
|
||||
# Bootstrap script for Materia supervisor instance.
|
||||
# Run once on a fresh server after setup_server.sh.
|
||||
# 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@<server_ip> 'bash -s' < infra/bootstrap_supervisor.sh
|
||||
#
|
||||
# Prerequisites:
|
||||
# - setup_server.sh already run (beanflows_service user, SSH deploy key, age keypair)
|
||||
# - 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
|
||||
|
||||
@@ -17,117 +18,47 @@ REPO_DIR="/opt/materia"
|
||||
GITLAB_PROJECT="deemanone/materia"
|
||||
UV="/home/${SERVICE_USER}/.local/bin/uv"
|
||||
|
||||
echo "=== Materia Supervisor Bootstrap ==="
|
||||
echo ""
|
||||
|
||||
[ "$(id -u)" = "0" ] || { echo "ERROR: Run as root"; exit 1; }
|
||||
|
||||
# ── System dependencies ────────────────────────────────────────────────────────
|
||||
# ── Check age keypair ─────────────────────────────────────────────────────────
|
||||
|
||||
echo "--- Installing system dependencies ---"
|
||||
apt-get update -q
|
||||
apt-get install -y -q git curl ca-certificates
|
||||
|
||||
# ── uv (installed as service user) ────────────────────────────────────────────
|
||||
|
||||
echo "--- Installing uv ---"
|
||||
if [ ! -f "${UV}" ]; then
|
||||
sudo -u "${SERVICE_USER}" bash -c \
|
||||
'curl -LsSf https://astral.sh/uv/install.sh | sh'
|
||||
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
|
||||
|
||||
# ── sops + age (as root, idempotent — setup_server.sh may have done this) ─────
|
||||
# ── Clone or update repository ────────────────────────────────────────────────
|
||||
|
||||
echo "--- Installing sops + age ---"
|
||||
ARCH=$(uname -m)
|
||||
case "${ARCH}" in
|
||||
x86_64) ARCH_TAG="amd64" ;;
|
||||
aarch64) ARCH_TAG="arm64" ;;
|
||||
*) echo "Unsupported architecture: ${ARCH}"; exit 1 ;;
|
||||
esac
|
||||
|
||||
if ! command -v age >/dev/null 2>&1; then
|
||||
AGE_VERSION="v1.3.1"
|
||||
curl -fsSL "https://dl.filippo.io/age/${AGE_VERSION}?for=linux/${ARCH_TAG}" -o /tmp/age.tar.gz
|
||||
tar -xzf /tmp/age.tar.gz -C /usr/local/bin --strip-components=1 age/age age/age-keygen
|
||||
chmod +x /usr/local/bin/age /usr/local/bin/age-keygen
|
||||
rm /tmp/age.tar.gz
|
||||
fi
|
||||
|
||||
if ! command -v sops >/dev/null 2>&1; then
|
||||
SOPS_VERSION="v3.12.1"
|
||||
curl -fsSL \
|
||||
"https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux.${ARCH_TAG}" \
|
||||
-o /usr/local/bin/sops
|
||||
chmod +x /usr/local/bin/sops
|
||||
fi
|
||||
|
||||
# ── Clone repository via SSH as service user ──────────────────────────────────
|
||||
|
||||
echo "--- Cloning repository ---"
|
||||
if [ -d "${REPO_DIR}/.git" ]; then
|
||||
echo "Repository already exists — fetching latest tags..."
|
||||
sudo -u "${SERVICE_USER}" git -C "${REPO_DIR}" fetch --tags --prune-tags origin
|
||||
else
|
||||
sudo -u "${SERVICE_USER}" git clone \
|
||||
"git@gitlab.com:${GITLAB_PROJECT}.git" "${REPO_DIR}"
|
||||
fi
|
||||
|
||||
# Checkout latest release tag (same logic as supervisor)
|
||||
LATEST_TAG=$(sudo -u "${SERVICE_USER}" \
|
||||
git -C "${REPO_DIR}" tag --list --sort=-version:refname "v*" | head -1)
|
||||
if [ -n "${LATEST_TAG}" ]; then
|
||||
echo "Checking out ${LATEST_TAG}..."
|
||||
sudo -u "${SERVICE_USER}" git -C "${REPO_DIR}" checkout --detach "${LATEST_TAG}"
|
||||
else
|
||||
echo "No release tags found — staying on current HEAD"
|
||||
fi
|
||||
|
||||
# ── Check age keypair ─────────────────────────────────────────────────────────
|
||||
# ── Decrypt secrets ───────────────────────────────────────────────────────────
|
||||
|
||||
echo "--- Checking age keypair ---"
|
||||
AGE_KEY_FILE="/home/${SERVICE_USER}/.config/sops/age/keys.txt"
|
||||
if [ ! -f "${AGE_KEY_FILE}" ]; then
|
||||
echo ""
|
||||
echo "ERROR: Age keypair not found at ${AGE_KEY_FILE}"
|
||||
echo ""
|
||||
echo "Run infra/setup_server.sh first, then:"
|
||||
echo " 1. Add the SSH deploy key to GitLab (Settings → Repository → Deploy Keys)"
|
||||
echo " 2. Add the age public key to .sops.yaml on your workstation"
|
||||
echo " 3. Run: sops updatekeys .env.prod.sops && git push"
|
||||
echo " 4. Re-run this bootstrap"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Decrypt secrets (as service user — SOPS auto-discovers age key from XDG) ──
|
||||
|
||||
echo "--- Decrypting secrets from .env.prod.sops ---"
|
||||
sudo -u "${SERVICE_USER}" bash -c \
|
||||
"sops --input-type dotenv --output-type dotenv -d ${REPO_DIR}/.env.prod.sops > ${REPO_DIR}/.env"
|
||||
chmod 600 "${REPO_DIR}/.env"
|
||||
echo "Secrets written to ${REPO_DIR}/.env"
|
||||
|
||||
# ── Data directories ───────────────────────────────────────────────────────────
|
||||
# ── Python dependencies ───────────────────────────────────────────────────────
|
||||
|
||||
echo "--- Creating data directories ---"
|
||||
mkdir -p /data/materia/landing
|
||||
chown -R "${SERVICE_USER}:${SERVICE_USER}" /data/materia
|
||||
|
||||
# ── Python dependencies (as service user) ─────────────────────────────────────
|
||||
|
||||
echo "--- Installing Python dependencies ---"
|
||||
sudo -u "${SERVICE_USER}" bash -c "cd ${REPO_DIR} && ${UV} sync --all-packages"
|
||||
|
||||
# ── Systemd service (as root) ──────────────────────────────────────────────────
|
||||
# ── Systemd service ───────────────────────────────────────────────────────────
|
||||
|
||||
echo "--- Setting up systemd service ---"
|
||||
cp "${REPO_DIR}/infra/supervisor/materia-supervisor.service" \
|
||||
/etc/systemd/system/materia-supervisor.service
|
||||
|
||||
echo "--- Enabling and starting service ---"
|
||||
cp "${REPO_DIR}/infra/supervisor/materia-supervisor.service" /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
systemctl enable materia-supervisor
|
||||
systemctl restart materia-supervisor
|
||||
systemctl enable --now materia-supervisor
|
||||
|
||||
echo ""
|
||||
echo "=== Bootstrap complete! ==="
|
||||
@@ -135,6 +66,4 @@ 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 ""
|
||||
echo "Repo: ${REPO_DIR}"
|
||||
echo "Tag: $(sudo -u "${SERVICE_USER}" git -C "${REPO_DIR}" describe --tags --always)"
|
||||
echo "Tag: $(sudo -u "${SERVICE_USER}" git -C "${REPO_DIR}" describe --tags --always)"
|
||||
|
||||
Reference in New Issue
Block a user