Lint / JS (eslint) (pull_request) Successful in 10s
Lint / Shell (shellcheck) (pull_request) Successful in 11s
Lint / No Hookshot webhook IDs in README (pull_request) Successful in 6s
Lint / No secrets in webhook configs (pull_request) Successful in 6s
Lint / Landing page is rendered (matrix (pull_request) Successful in 6s
Lint / Python (ruff) (pull_request) Successful in 7s
Lint / Secret scan (gitleaks) (pull_request) Successful in 13s
Lint / Shell (shellcheck) (push) Successful in 26s
Lint / JS (eslint) (push) Successful in 18s
Lint / No Hookshot webhook IDs in README (push) Successful in 21s
Lint / No secrets in webhook configs (push) Successful in 12s
Lint / Python deps (pip-audit) (pull_request) Successful in 1m54s
Lint / Landing page is rendered (matrix (push) Successful in 17s
Lint / Python (ruff) (push) Successful in 16s
Lint / Secret scan (gitleaks) (push) Successful in 31s
Lint / Python deps (pip-audit) (push) Successful in 1m37s
mautrix-discord doesn't bridge presence. lotus_presence.py watches the Discord server (a second gateway session on the bridge's own bot, Guilds + Presence intents only) and sets presence and a status message on each @discord_* ghost: online / idle / DND / offline -> online / unavailable / msc3026 busy / offline; custom status and the first activity become the status message. Its own appservice registration is non-exclusive on the ghost namespace, so it can update ghosts but not create them. Synapse drops an unrefreshed user to offline after about a minute, so live states are re-sent every 20 s; if the service dies, ghosts go offline rather than stale. The installer generates the credentials on the server and doesn't restart Synapse. Statuses and activities are never logged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
133 lines
6.0 KiB
Bash
133 lines
6.0 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
|
|
|
|
# Discord presence service: only once installed (discord-bridge/install-presence.sh).
|
|
if [ -f /etc/lotus-presence/env ] && echo "$CHANGED" | grep -qE '^discord-bridge/lotus_presence\.py|^systemd/lotus-discord-presence\.service'; then
|
|
echo "Deploying lotus-discord-presence..."
|
|
if python3 -m py_compile "$REPO_DIR/discord-bridge/lotus_presence.py"; then
|
|
install -D -m644 "$REPO_DIR/discord-bridge/lotus_presence.py" /opt/lotus-presence/lotus_presence.py
|
|
install -m644 "$REPO_DIR/systemd/lotus-discord-presence.service" /etc/systemd/system/lotus-discord-presence.service
|
|
systemctl daemon-reload
|
|
systemctl restart lotus-discord-presence || true
|
|
else
|
|
echo "ERROR: lotus_presence.py has a syntax error, keeping the running version."
|
|
fi
|
|
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 ==="
|