vps/ directory — all versioned, installed on VPS with one command: - vps/deploy.sh: pull from Hermes Gitea → pnpm build → deploy bundle → health check → auto-rollback on failure - vps/webhook.js: Node.js webhook receiver (port 9000, HMAC-SHA256) validates Gitea signature, runs deploy.sh, skips non-main branches - vps/timmy-deploy-hook.service: systemd unit for webhook receiver - vps/timmy-health.service + .timer: health watchdog every 5 min, auto-restarts timmy-tower if /api/health returns non-200 - vps/install.sh: one-time VPS setup — installs scripts, sets WEBHOOK_SECRET in .env, adds nginx /webhook/deploy block, enables services Gitea webhook configured on admin/timmy-tower (id: 1): - URL: http://143.198.27.163/webhook/deploy - HMAC secret stored in .local/deploy-webhook-secret (gitignored) One-time install command: WEBHOOK_SECRET=$(cat .local/deploy-webhook-secret) \ ssh root@143.198.27.163 'bash -s' < vps/install.sh replit.md: removed stale bore-tunnel push instructions; documented sovereign deploy workflow, monitoring commands, and rollback procedure
127 lines
5.8 KiB
Bash
127 lines
5.8 KiB
Bash
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# vps/install.sh — One-time setup of push-to-deploy pipeline on Hermes VPS
|
|
#
|
|
# Run once from the VPS (or from a machine with SSH access):
|
|
# bash vps/install.sh
|
|
# OR remotely:
|
|
# ssh root@143.198.27.163 'bash -s' < vps/install.sh
|
|
#
|
|
# What it does:
|
|
# 1. Ensures Node 24 + pnpm are available
|
|
# 2. Installs deploy/webhook/health scripts to /opt/timmy-tower/
|
|
# 3. Generates a WEBHOOK_SECRET and adds to .env
|
|
# 4. Adds nginx location block for /webhook/deploy
|
|
# 5. Enables + starts systemd services: timmy-deploy-hook, timmy-health.timer
|
|
# 6. Prints the webhook secret so you can configure the Gitea webhook
|
|
# =============================================================================
|
|
set -euo pipefail
|
|
|
|
DEPLOY_DIR="/opt/timmy-tower"
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
NGINX_CONF="/etc/nginx/sites-enabled/default"
|
|
WEBHOOK_PORT=9000
|
|
|
|
log() { echo "[install] $*"; }
|
|
ok() { echo "[install] ✓ $*"; }
|
|
err() { echo "[install] ✗ $*" >&2; exit 1; }
|
|
|
|
# ── 1. Ensure Node 24 ─────────────────────────────────────────────────────────
|
|
log "Checking Node.js..."
|
|
NODE_VER=$(node --version 2>/dev/null || echo "none")
|
|
if [[ "$NODE_VER" == none ]]; then
|
|
err "Node.js is not installed. Install Node 24 first: https://nodejs.org"
|
|
fi
|
|
ok "Node.js $NODE_VER"
|
|
|
|
# ── 2. Ensure pnpm ────────────────────────────────────────────────────────────
|
|
log "Checking pnpm..."
|
|
if ! command -v pnpm &>/dev/null; then
|
|
log "Installing pnpm via corepack..."
|
|
corepack enable
|
|
corepack prepare pnpm@latest --activate
|
|
fi
|
|
ok "pnpm $(pnpm --version)"
|
|
|
|
# ── 3. Copy scripts to deploy dir ─────────────────────────────────────────────
|
|
log "Copying scripts to $DEPLOY_DIR..."
|
|
cp "$SCRIPT_DIR/deploy.sh" "$DEPLOY_DIR/deploy.sh"
|
|
cp "$SCRIPT_DIR/webhook.js" "$DEPLOY_DIR/webhook.js"
|
|
cp "$SCRIPT_DIR/health-check.sh" "$DEPLOY_DIR/health-check.sh"
|
|
chmod +x "$DEPLOY_DIR/deploy.sh" "$DEPLOY_DIR/health-check.sh"
|
|
ok "Scripts installed."
|
|
|
|
# ── 4. Set WEBHOOK_SECRET in .env ────────────────────────────────────────────
|
|
# Priority: $WEBHOOK_SECRET env var → existing .env entry → generate new one
|
|
ENV_FILE="$DEPLOY_DIR/.env"
|
|
if [ -n "${WEBHOOK_SECRET:-}" ]; then
|
|
# Caller provided secret — write or replace in .env
|
|
if grep -q "^WEBHOOK_SECRET=" "$ENV_FILE" 2>/dev/null; then
|
|
sed -i "s|^WEBHOOK_SECRET=.*|WEBHOOK_SECRET=$WEBHOOK_SECRET|" "$ENV_FILE"
|
|
else
|
|
echo "WEBHOOK_SECRET=$WEBHOOK_SECRET" >> "$ENV_FILE"
|
|
fi
|
|
ok "WEBHOOK_SECRET set from environment."
|
|
elif grep -q "^WEBHOOK_SECRET=" "$ENV_FILE" 2>/dev/null; then
|
|
WEBHOOK_SECRET=$(grep "^WEBHOOK_SECRET=" "$ENV_FILE" | cut -d= -f2-)
|
|
log "WEBHOOK_SECRET already in .env — keeping existing value."
|
|
else
|
|
WEBHOOK_SECRET=$(openssl rand -hex 32)
|
|
echo "WEBHOOK_SECRET=$WEBHOOK_SECRET" >> "$ENV_FILE"
|
|
ok "WEBHOOK_SECRET generated and saved to .env"
|
|
log "NOTE: Update the Gitea webhook secret to match: $WEBHOOK_SECRET"
|
|
fi
|
|
|
|
# ── 5. Install systemd services ───────────────────────────────────────────────
|
|
log "Installing systemd units..."
|
|
cp "$SCRIPT_DIR/timmy-deploy-hook.service" /etc/systemd/system/
|
|
cp "$SCRIPT_DIR/timmy-health.service" /etc/systemd/system/
|
|
cp "$SCRIPT_DIR/timmy-health.timer" /etc/systemd/system/
|
|
|
|
systemctl daemon-reload
|
|
systemctl enable --now timmy-deploy-hook
|
|
systemctl enable --now timmy-health.timer
|
|
ok "Services enabled: timmy-deploy-hook, timmy-health.timer"
|
|
|
|
# ── 6. Nginx — add /webhook/deploy proxy block ───────────────────────────────
|
|
if grep -q "webhook/deploy" "$NGINX_CONF" 2>/dev/null; then
|
|
log "nginx already has /webhook/deploy block — skipping"
|
|
else
|
|
log "Adding nginx proxy for /webhook/deploy..."
|
|
BLOCK="
|
|
# Timmy deploy webhook (managed by install.sh)
|
|
location /webhook/deploy {
|
|
proxy_pass http://127.0.0.1:${WEBHOOK_PORT}/deploy;
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_read_timeout 10s;
|
|
}"
|
|
|
|
# Insert before the closing } of the server block
|
|
sed -i "s|^}|${BLOCK}\n}|" "$NGINX_CONF"
|
|
nginx -t && systemctl reload nginx
|
|
ok "nginx updated and reloaded."
|
|
fi
|
|
|
|
# ── 7. Print summary ─────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "================================================================"
|
|
echo " Push-to-deploy pipeline installed on Hermes VPS"
|
|
echo "================================================================"
|
|
echo ""
|
|
echo " Webhook endpoint: http://143.198.27.163/webhook/deploy"
|
|
echo " WEBHOOK_SECRET: $WEBHOOK_SECRET"
|
|
echo ""
|
|
echo " Configure this Gitea webhook on admin/timmy-tower:"
|
|
echo " URL: http://143.198.27.163/webhook/deploy"
|
|
echo " Secret: $WEBHOOK_SECRET"
|
|
echo " Events: Push"
|
|
echo " Branch: main (filter in webhook.js)"
|
|
echo ""
|
|
echo " Useful commands:"
|
|
echo " tail -f $DEPLOY_DIR/deploy.log # watch deploy logs"
|
|
echo " tail -f $DEPLOY_DIR/health.log # watch health logs"
|
|
echo " systemctl status timmy-deploy-hook # webhook service status"
|
|
echo " bash $DEPLOY_DIR/deploy.sh # manual deploy"
|
|
echo "================================================================"
|