Add auto-deployment infrastructure for all 4 LXCs
- Per-LXC deploy scripts (lxc151-hookshot, lxc106-cinny, lxc139-landing, lxc110-draupnir) - Per-LXC webhook hook configs with unique HMAC-SHA256 secrets - Livekit graceful restart script + systemd timer (waits for zero active calls) - Fix hookshot/deploy.sh capitalization bug (Uptime-Kuma, Tinker-Tickets, etc.) Each LXC independently clones repo and runs its own deploy.sh via adnanh/webhook on port 9000. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
18
deploy/hooks-lxc106.json
Normal file
18
deploy/hooks-lxc106.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "matrix-deploy",
|
||||||
|
"execute-command": "/usr/local/bin/matrix-deploy.sh",
|
||||||
|
"command-working-directory": "/opt/matrix-config",
|
||||||
|
"response-message": "Deploying cinny config...",
|
||||||
|
"trigger-rule": {
|
||||||
|
"match": {
|
||||||
|
"type": "payload-hash-sha256",
|
||||||
|
"secret": "76dd5febd1cc3458545ce37537f4bfe26f241a9635b57a2cba183ebc9221230b",
|
||||||
|
"parameter": {
|
||||||
|
"source": "header",
|
||||||
|
"name": "X-Gitea-Signature"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
18
deploy/hooks-lxc110.json
Normal file
18
deploy/hooks-lxc110.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "matrix-deploy",
|
||||||
|
"execute-command": "/usr/local/bin/matrix-deploy.sh",
|
||||||
|
"command-working-directory": "/opt/matrix-config",
|
||||||
|
"response-message": "Deploying draupnir config...",
|
||||||
|
"trigger-rule": {
|
||||||
|
"match": {
|
||||||
|
"type": "payload-hash-sha256",
|
||||||
|
"secret": "0d23fab8743e9ee6b52cbd05a889b04c927ffa2b2b21fe50244f1a534d1a22d0",
|
||||||
|
"parameter": {
|
||||||
|
"source": "header",
|
||||||
|
"name": "X-Gitea-Signature"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
18
deploy/hooks-lxc139.json
Normal file
18
deploy/hooks-lxc139.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "matrix-deploy",
|
||||||
|
"execute-command": "/usr/local/bin/matrix-deploy.sh",
|
||||||
|
"command-working-directory": "/opt/matrix-config",
|
||||||
|
"response-message": "Deploying matrix landing page...",
|
||||||
|
"trigger-rule": {
|
||||||
|
"match": {
|
||||||
|
"type": "payload-hash-sha256",
|
||||||
|
"secret": "ddea576ef03bff35f0c9d138b626b273d9e9502434e0717899a87677cd5ac267",
|
||||||
|
"parameter": {
|
||||||
|
"source": "header",
|
||||||
|
"name": "X-Gitea-Signature"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
18
deploy/hooks-lxc151.json
Normal file
18
deploy/hooks-lxc151.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "matrix-deploy",
|
||||||
|
"execute-command": "/usr/local/bin/matrix-deploy.sh",
|
||||||
|
"command-working-directory": "/opt/matrix-config",
|
||||||
|
"response-message": "Deploying matrix hookshot transforms...",
|
||||||
|
"trigger-rule": {
|
||||||
|
"match": {
|
||||||
|
"type": "payload-hash-sha256",
|
||||||
|
"secret": "38ba0e66763da2096c47645cbf636ce3c2c51232e006b964e57d6bb94a32dcaa",
|
||||||
|
"parameter": {
|
||||||
|
"source": "header",
|
||||||
|
"name": "X-Gitea-Signature"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
22
deploy/livekit-graceful-restart.sh
Normal file
22
deploy/livekit-graceful-restart.sh
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Checks every 5 minutes if a livekit restart is pending.
|
||||||
|
# Only restarts when there are no active WebSocket connections on port 7881
|
||||||
|
# (established connections = active call participants).
|
||||||
|
# Run by: livekit-graceful-restart.timer (systemd)
|
||||||
|
|
||||||
|
if [ ! -f /run/livekit-restart-pending ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Count established WebSocket signaling connections on livekit port 7881
|
||||||
|
ACTIVE=$(ss -tn state established '( dport = :7881 or sport = :7881 )' | grep -c ESTAB || true)
|
||||||
|
|
||||||
|
if [ "$ACTIVE" -gt 0 ]; then
|
||||||
|
echo "$(date): Livekit restart pending but $ACTIVE active connection(s) — waiting."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$(date): No active calls — applying livekit-server restart."
|
||||||
|
systemctl restart livekit-server
|
||||||
|
rm -f /run/livekit-restart-pending
|
||||||
|
echo "$(date): livekit-server restarted successfully."
|
||||||
41
deploy/lxc106-cinny.sh
Normal file
41
deploy/lxc106-cinny.sh
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Auto-deploy script for LXC 106 (cinny)
|
||||||
|
# Handles: cinny/config.json, cinny/dev-update.sh
|
||||||
|
# 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"
|
||||||
|
|
||||||
|
exec >> "$LOG" 2>&1
|
||||||
|
echo "=== $(date) === LXC106 deploy triggered ==="
|
||||||
|
|
||||||
|
# Clone or pull
|
||||||
|
if [ ! -d "$REPO_DIR/.git" ]; then
|
||||||
|
git clone "$CLONE_URL" "$REPO_DIR"
|
||||||
|
CHANGED="cinny/config.json cinny/dev-update.sh"
|
||||||
|
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"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$CHANGED" | grep -q '^cinny/config.json'; then
|
||||||
|
echo "Deploying cinny config.json..."
|
||||||
|
cp "$REPO_DIR/cinny/config.json" /var/www/html/config.json
|
||||||
|
echo "✓ config.json deployed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$CHANGED" | grep -q '^cinny/dev-update.sh'; then
|
||||||
|
echo "Deploying cinny dev-update.sh..."
|
||||||
|
cp "$REPO_DIR/cinny/dev-update.sh" /usr/local/bin/cinny-dev-update.sh
|
||||||
|
chmod +x /usr/local/bin/cinny-dev-update.sh
|
||||||
|
echo "✓ dev-update.sh deployed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== $(date) === LXC106 deploy complete ==="
|
||||||
52
deploy/lxc110-draupnir.sh
Normal file
52
deploy/lxc110-draupnir.sh
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Auto-deploy script for LXC 110 (draupnir)
|
||||||
|
# Handles: draupnir/production.yaml — restarts draupnir after deploy
|
||||||
|
# NOTE: access token in production.yaml is redacted in git.
|
||||||
|
# The real token lives only at /opt/draupnir/config/production.yaml on this LXC.
|
||||||
|
# This script merges the git version (structure/settings) with the live token.
|
||||||
|
# Triggered by: Gitea webhook on push to main
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_DIR="/opt/matrix-config"
|
||||||
|
LIVE_CONFIG="/opt/draupnir/config/production.yaml"
|
||||||
|
LOG="/var/log/matrix-deploy.log"
|
||||||
|
CLONE_URL="https://code.lotusguild.org/LotusGuild/matrix.git"
|
||||||
|
|
||||||
|
exec >> "$LOG" 2>&1
|
||||||
|
echo "=== $(date) === LXC110 deploy triggered ==="
|
||||||
|
|
||||||
|
# Clone or pull
|
||||||
|
if [ ! -d "$REPO_DIR/.git" ]; then
|
||||||
|
git clone "$CLONE_URL" "$REPO_DIR"
|
||||||
|
CHANGED="draupnir/production.yaml"
|
||||||
|
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"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$CHANGED" | grep -q '^draupnir/production.yaml'; then
|
||||||
|
echo "Deploying draupnir config..."
|
||||||
|
|
||||||
|
# Extract live access token (never stored in git)
|
||||||
|
LIVE_TOKEN=$(grep '^accessToken:' "$LIVE_CONFIG" | awk '{print $2}' | tr -d '"')
|
||||||
|
if [ -z "$LIVE_TOKEN" ]; then
|
||||||
|
echo "ERROR: Could not extract live accessToken from $LIVE_CONFIG — aborting." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy repo version and restore the live token
|
||||||
|
cp "$REPO_DIR/draupnir/production.yaml" "$LIVE_CONFIG"
|
||||||
|
sed -i "s|accessToken: \"REDACTED\"|accessToken: \"$LIVE_TOKEN\"|" "$LIVE_CONFIG"
|
||||||
|
|
||||||
|
echo "Restarting draupnir..."
|
||||||
|
systemctl restart draupnir
|
||||||
|
sleep 3
|
||||||
|
systemctl is-active draupnir && echo "✓ draupnir restarted successfully" || echo "✗ draupnir failed to start"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== $(date) === LXC110 deploy complete ==="
|
||||||
36
deploy/lxc139-landing.sh
Normal file
36
deploy/lxc139-landing.sh
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Auto-deploy script for LXC 139 (nginx proxy manager)
|
||||||
|
# Handles: landing/index.html
|
||||||
|
# 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"
|
||||||
|
|
||||||
|
exec >> "$LOG" 2>&1
|
||||||
|
echo "=== $(date) === LXC139 deploy triggered ==="
|
||||||
|
|
||||||
|
# Clone or pull
|
||||||
|
if [ ! -d "$REPO_DIR/.git" ]; then
|
||||||
|
git clone "$CLONE_URL" "$REPO_DIR"
|
||||||
|
CHANGED="landing/index.html"
|
||||||
|
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"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$CHANGED" | grep -q '^landing/index.html'; then
|
||||||
|
echo "Deploying landing page..."
|
||||||
|
mkdir -p /var/www/matrix-landing
|
||||||
|
cp "$REPO_DIR/landing/index.html" /var/www/matrix-landing/index.html
|
||||||
|
nginx -s reload
|
||||||
|
echo "✓ landing page deployed and nginx reloaded"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== $(date) === LXC139 deploy complete ==="
|
||||||
49
deploy/lxc151-hookshot.sh
Normal file
49
deploy/lxc151-hookshot.sh
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Auto-deploy script for LXC 151 (matrix homeserver)
|
||||||
|
# Handles: hookshot transformation functions, livekit service file (graceful)
|
||||||
|
# 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
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== $(date) === LXC151 deploy complete ==="
|
||||||
@@ -24,8 +24,9 @@ DIR="$(cd "$(dirname "$0")" && pwd)"
|
|||||||
deploy_hook() {
|
deploy_hook() {
|
||||||
local file="$1"
|
local file="$1"
|
||||||
local filename="$(basename "$file" .js)"
|
local filename="$(basename "$file" .js)"
|
||||||
# Capitalize first letter to match state_key (e.g. proxmox -> Proxmox)
|
# Capitalize first letter of each hyphen-separated word, preserving hyphens
|
||||||
local state_key="$(echo "$filename" | sed 's/\b\(.\)/\u\1/g' | sed 's/-\(.\)/\u\1/g')"
|
# e.g. proxmox -> Proxmox, uptime-kuma -> Uptime-Kuma, tinker-tickets -> Tinker-Tickets
|
||||||
|
local state_key="$(python3 -c "import sys; s='$filename'; print('-'.join(w.capitalize() for w in s.split('-')))")"
|
||||||
local fn="$(cat "$file")"
|
local fn="$(cat "$file")"
|
||||||
local encoded_room="$(python3 -c "import urllib.parse; print(urllib.parse.quote('$MATRIX_ROOM'))")"
|
local encoded_room="$(python3 -c "import urllib.parse; print(urllib.parse.quote('$MATRIX_ROOM'))")"
|
||||||
local encoded_key="$(python3 -c "import urllib.parse; print(urllib.parse.quote('$state_key'))")"
|
local encoded_key="$(python3 -c "import urllib.parse; print(urllib.parse.quote('$state_key'))")"
|
||||||
|
|||||||
9
systemd/livekit-graceful-restart.service
Normal file
9
systemd/livekit-graceful-restart.service
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=LiveKit graceful restart when no active calls
|
||||||
|
After=livekit-server.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/bin/livekit-graceful-restart.sh
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
9
systemd/livekit-graceful-restart.timer
Normal file
9
systemd/livekit-graceful-restart.timer
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Check every 5 minutes if a pending livekit restart can be applied
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnBootSec=5min
|
||||||
|
OnUnitActiveSec=5min
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
Reference in New Issue
Block a user