Lint / JS (eslint) (push) Successful in 12s
Lint / Shell (shellcheck) (push) Successful in 13s
Lint / No secrets in webhook configs (push) Successful in 6s
Lint / Landing page is rendered (matrix (push) Successful in 5s
Lint / Python (ruff) (push) Successful in 6s
Lint / Secret scan (gitleaks) (push) Successful in 8s
Lint / Shell (shellcheck) (pull_request) Successful in 10s
Lint / JS (eslint) (pull_request) Successful in 8s
Lint / No secrets in webhook configs (pull_request) Successful in 5s
Lint / Python deps (pip-audit) (push) Successful in 41s
Lint / Landing page is rendered (matrix (pull_request) Successful in 4s
Lint / Python (ruff) (pull_request) Successful in 5s
Lint / Secret scan (gitleaks) (pull_request) Successful in 11s
Lint / Python deps (pip-audit) (pull_request) Successful in 56s
livekit-graceful-restart.sh decided "no active calls" by counting established TCP connections on :7881 with `ss ... state established | grep -c ESTAB`. Two bugs: - With a `state` filter ss omits the State column, so `grep -c ESTAB` is always 0: the check never saw anything. - :7881 is only LiveKit's ICE-TCP media fallback. Signalling is on :7880 and media is normally UDP. Reproduced in a two-person call on the local stack: the old check counted 0 and would have restarted. Now it waits while LiveKit's own livekit_participant_total is above 0 or any signalling WebSocket on :7880 is open, and also waits if the metrics can't be read. The deploy hook now installs the script when it changes (it never did, so edits to it never reached the server). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
120 lines
5.2 KiB
Bash
120 lines
5.2 KiB
Bash
#!/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
|
|
|
|
# The graceful-restart check itself (run by livekit-graceful-restart.timer)
|
|
if echo "$CHANGED" | grep -q '^deploy/livekit-graceful-restart\.sh'; then
|
|
echo "Deploying livekit-graceful-restart.sh..."
|
|
bash -n "$REPO_DIR/deploy/livekit-graceful-restart.sh" \
|
|
&& install -m755 "$REPO_DIR/deploy/livekit-graceful-restart.sh" /usr/local/bin/livekit-graceful-restart.sh
|
|
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 ==="
|