#!/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: # WEBHOOK_SECRET=$(cat .local/deploy-webhook-secret) \ # ssh root@143.198.27.163 'bash -s' < vps/install.sh # # What it does: # 1. Ensures Node 24 + pnpm are available # 2. Verifies /root/.gitea-replit-token exists (needed by deploy.sh) # 3. Installs deploy/webhook/health scripts to /opt/timmy-tower/ # 4. Generates or applies a WEBHOOK_SECRET and adds to .env # 5. Adds nginx location block for /webhook/deploy (idempotent marker) # 6. Enables + starts systemd services: timmy-deploy-hook, timmy-health.timer # 7. Prints the webhook secret so you can configure the Gitea webhook # # Security note: # The webhook receiver listens on localhost:9000 only. Nginx proxies # /webhook/deploy from the public interface. HMAC-SHA256 verifies request # authenticity. For full transport confidentiality, place Hermes behind a # TLS-terminating proxy or configure nginx with Let's Encrypt. # ============================================================================= 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 NGINX_MARKER_START="# BEGIN timmy-deploy-webhook" NGINX_MARKER_END="# END timmy-deploy-webhook" log() { echo "[install] $*"; } ok() { echo "[install] ✓ $*"; } warn(){ echo "[install] ! $*"; } err() { echo "[install] ✗ $*" >&2; exit 1; } # ── 1. Ensure Node.js ───────────────────────────────────────────────────────── 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. Verify Gitea pull credentials ────────────────────────────────────────── GITEA_TOKEN_FILE="/root/.gitea-replit-token" if [ ! -f "$GITEA_TOKEN_FILE" ]; then warn "Gitea pull token not found at $GITEA_TOKEN_FILE" warn "deploy.sh will fail on first run without it." warn "Create it with the Gitea admin token:" warn " echo '' > $GITEA_TOKEN_FILE && chmod 600 $GITEA_TOKEN_FILE" warn "Continuing install — add the token before triggering a deploy." else ok "Gitea pull token present at $GITEA_TOKEN_FILE" fi # ── 4. Copy scripts to deploy dir ───────────────────────────────────────────── log "Copying scripts to $DEPLOY_DIR..." mkdir -p "$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." # ── 5. Set WEBHOOK_SECRET in .env ──────────────────────────────────────────── # Priority: $WEBHOOK_SECRET env var → existing .env entry → generate new one ENV_FILE="$DEPLOY_DIR/.env" touch "$ENV_FILE" chmod 600 "$ENV_FILE" if [ -n "${WEBHOOK_SECRET:-}" ]; then 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" warn "Update the Gitea webhook secret to match: $WEBHOOK_SECRET" fi # ── 6. 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" # ── 7. Nginx — idempotent marker-based /webhook/deploy proxy block ───────────── if grep -qF "$NGINX_MARKER_START" "$NGINX_CONF" 2>/dev/null; then log "nginx /webhook/deploy block already present — skipping" else log "Adding nginx proxy for /webhook/deploy..." BLOCK=" $NGINX_MARKER_START 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; } $NGINX_MARKER_END" # Use a temp file so we can validate before touching the live config TMP_CONF=$(mktemp) # Insert the block before the last closing brace of the server block awk -v block="$BLOCK" ' /^}$/ && !inserted { print block; inserted=1 } { print } ' "$NGINX_CONF" > "$TMP_CONF" if nginx -t -c "$TMP_CONF" 2>/dev/null; then cp "$TMP_CONF" "$NGINX_CONF" nginx -t && systemctl reload nginx ok "nginx updated and reloaded." else warn "nginx config test failed on temp file — skipping auto-patch." warn "Add this block manually to the server {} block in $NGINX_CONF:" echo "$BLOCK" fi rm -f "$TMP_CONF" fi # ── 8. 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 " NOTE: The webhook runs over plain HTTP. HMAC-SHA256 ensures" echo " authenticity. For full transport security, configure nginx" echo " with TLS (Let's Encrypt) on the Hermes public domain." echo "" echo " Configure this Gitea webhook on replit/timmy-tower:" echo " URL: http://143.198.27.163/webhook/deploy" echo " Secret: $WEBHOOK_SECRET" echo " Events: Push" echo " Branch: main (filtered by webhook.js)" echo "" echo " Gitea pull token: $GITEA_TOKEN_FILE" if [ ! -f "$GITEA_TOKEN_FILE" ]; then echo " *** NOT FOUND — add before first deploy ***" fi 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 "================================================================"