#!/bin/bash # Auto-deploy script for LXC 151 (matrix homeserver) # Handles: hookshot transformation functions, livekit service file (graceful), # voice-limit-guard (livekit policy sidecar), matrixbot # Triggered by: Gitea webhook on push to main set -euo pipefail REPO_DIR="/opt/matrix-config" LOG="/var/log/matrix-deploy.log" CLONE_URL="https://code.lotusguild.org/LotusGuild/matrix.git" ENV_FILE="/etc/matrix-deploy.env" exec >> "$LOG" 2>&1 echo "=== $(date) === LXC151 deploy triggered ===" # Load env (MATRIX_TOKEN, MATRIX_SERVER, MATRIX_ROOM) if [ -f "$ENV_FILE" ]; then # shellcheck source=/dev/null source "$ENV_FILE" fi # Clone or pull if [ ! -d "$REPO_DIR/.git" ]; then git clone "$CLONE_URL" "$REPO_DIR" else cd "$REPO_DIR" git fetch --all PREV=$(git rev-parse HEAD) git reset --hard origin/main NEW=$(git rev-parse HEAD) CHANGED=$(git diff --name-only "$PREV" "$NEW") echo "Changed files: $CHANGED" # Hookshot transforms if echo "$CHANGED" | grep -q '^hookshot/'; then echo "Deploying hookshot transforms..." export MATRIX_TOKEN MATRIX_SERVER MATRIX_ROOM bash "$REPO_DIR/hookshot/deploy.sh" fi # Livekit service file — graceful restart (never kill active calls) if echo "$CHANGED" | grep -q '^systemd/livekit-server.service'; then echo "livekit-server.service changed — staging graceful restart..." cp "$REPO_DIR/systemd/livekit-server.service" /etc/systemd/system/livekit-server.service systemctl daemon-reload touch /run/livekit-restart-pending echo "Restart pending — will apply when no active calls." fi # Voice-limit / quality guard (fronts lk-jwt-service on :8070) if echo "$CHANGED" | grep -qE '^livekit/voice-limit-guard\.py|^systemd/voice-limit-guard\.service'; then echo "Deploying voice-limit-guard..." # Validate syntax BEFORE swapping so a broken edit can never wedge token # issuance (a syntax error would crash the guard and block all joins). if python3 -m py_compile "$REPO_DIR/livekit/voice-limit-guard.py"; then GUARD_DST="/opt/voice-limit-guard/voice-limit-guard.py" GUARD_BAK="/opt/voice-limit-guard/voice-limit-guard.py.bak" # Back up the last-known-good guard so a runtime-broken (but # syntactically valid) deploy can self-heal — a dead guard breaks # ALL new joins, so we must never leave it down. [ -f "$GUARD_DST" ] && cp "$GUARD_DST" "$GUARD_BAK" install -D -m644 "$REPO_DIR/livekit/voice-limit-guard.py" "$GUARD_DST" if echo "$CHANGED" | grep -q '^systemd/voice-limit-guard\.service'; then install -m644 "$REPO_DIR/systemd/voice-limit-guard.service" /etc/systemd/system/voice-limit-guard.service systemctl daemon-reload fi # Restarting the guard only affects joins in a ~1s window (established # calls talk directly to livekit-server); it does not drop calls. # `|| true` so a non-zero restart can't abort the deploy under set -e. systemctl restart voice-limit-guard || true sleep 1 if systemctl is-active --quiet voice-limit-guard; then echo "voice-limit-guard restarted successfully." elif [ -f "$GUARD_BAK" ]; then echo "ERROR: new voice-limit-guard failed to start — rolling back to last-known-good." cp "$GUARD_BAK" "$GUARD_DST" systemctl restart voice-limit-guard || true sleep 1 if systemctl is-active --quiet voice-limit-guard; then echo "voice-limit-guard rolled back and running." else echo "CRITICAL: voice-limit-guard down after rollback — manual intervention needed." fi else echo "CRITICAL: voice-limit-guard failed to start and no backup to roll back to." fi else echo "ERROR: py_compile failed on voice-limit-guard.py — skipping deploy." fi fi # Matrixbot source files if echo "$CHANGED" | grep -q '^matrixbot/'; then echo "Deploying matrixbot changes..." BOT_DIR="/opt/matrixbot" BOT_FILES="bot.py callbacks.py commands.py config.py utils.py welcome.py wordle.py wordlist_answers.py wordlist_valid.py requirements.txt" for f in $BOT_FILES; do if [ -f "$REPO_DIR/matrixbot/$f" ]; then cp "$REPO_DIR/matrixbot/$f" "$BOT_DIR/$f" fi done systemctl restart matrixbot sleep 2 if systemctl is-active --quiet matrixbot; then echo "matrixbot restarted successfully." else echo "ERROR: matrixbot failed to restart." fi fi fi echo "=== $(date) === LXC151 deploy complete ==="