99 lines
4.6 KiB
YAML
99 lines
4.6 KiB
YAML
name: Deploy to VPS
|
|
|
|
on:
|
|
workflow_run:
|
|
workflows: ["Publish to Docker Hub"]
|
|
types: [completed]
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
deploy:
|
|
if: >-
|
|
(github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success')
|
|
&& vars.DEPLOY_ENABLED == 'true'
|
|
name: Deploy OmniRoute to VPS
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Check VPS SSH reachability from runner
|
|
id: reach
|
|
env:
|
|
# Pass the host via env (never interpolate a secret straight into the
|
|
# script body) so /dev/tcp gets a shell variable, not inlined text.
|
|
VPS_HOST: ${{ secrets.VPS_HOST }}
|
|
run: |
|
|
set -uo pipefail
|
|
# A GitHub-hosted runner can only deploy when it can actually open a TCP
|
|
# connection to the VPS SSH port. The Local VPS lives on a private LAN and
|
|
# the Akamai host firewalls :22 to known IPs, so the runner is routinely
|
|
# unable to reach it (`dial tcp ***:22: i/o timeout`). Treat "unreachable
|
|
# from the runner" as a SKIP — the real deploys are run manually from an
|
|
# allowed network via the deploy-vps-local / deploy-vps-akamai skills — so
|
|
# an unreachable host no longer red-fails every release/push pipeline.
|
|
# When the host IS reachable, the deploy step below still runs in full and
|
|
# its health gate surfaces any genuine deploy failure.
|
|
if timeout 15 bash -c 'exec 3<>"/dev/tcp/${VPS_HOST}/22"' 2>/dev/null; then
|
|
echo "reachable=true" >> "$GITHUB_OUTPUT"
|
|
echo "✅ VPS_HOST:22 reachable from the runner — proceeding with deploy."
|
|
else
|
|
echo "reachable=false" >> "$GITHUB_OUTPUT"
|
|
echo "::warning title=Auto-deploy skipped::VPS_HOST:22 is not reachable from this GitHub runner (private LAN / firewalled). Deploy manually with the deploy-vps-local or deploy-vps-akamai skill."
|
|
fi
|
|
|
|
- name: Deploy via SSH
|
|
if: steps.reach.outputs.reachable == 'true'
|
|
uses: appleboy/ssh-action@v1
|
|
with:
|
|
host: ${{ secrets.VPS_HOST }}
|
|
username: ${{ secrets.VPS_USER }}
|
|
key: ${{ secrets.VPS_SSH_KEY }}
|
|
port: 22
|
|
timeout: 60s
|
|
command_timeout: 15m
|
|
script: |
|
|
set -euo pipefail
|
|
|
|
echo "=== Updating OmniRoute ==="
|
|
npm install -g omniroute@latest
|
|
INSTALLED_VERSION=$(omniroute --version 2>/dev/null | tr -d '[:space:]' || echo "unknown")
|
|
echo "Installed CLI version: $INSTALLED_VERSION"
|
|
|
|
# Recreate the PM2 process instead of `pm2 restart`. A bare restart
|
|
# re-runs whatever script path was saved earlier; after the build-output
|
|
# reorg (app/ -> dist/, .next -> .build/next) a process pinned to the old
|
|
# app/server-ws.mjs path can no longer start, and the node process dies
|
|
# while PM2 still reports "online" — so the box never binds :20128.
|
|
# Always launch via the `omniroute` bin so .env is loaded and the dist/
|
|
# layout is resolved correctly.
|
|
echo "=== (Re)creating PM2 process via bin ==="
|
|
pm2 delete omniroute 2>/dev/null || true
|
|
pm2 start omniroute --name omniroute -- --port 20128
|
|
pm2 save
|
|
|
|
# Health gate: fail the deploy unless the box actually reports healthy.
|
|
# Poll /api/monitoring/health for "status":"healthy" (a deeper signal than
|
|
# a static page 200 — it confirms the app booted, not just that a port is
|
|
# bound). Boot can take a while after a native-module/build-layout change,
|
|
# so poll up to ~3min before giving up.
|
|
echo "=== Health Check (gates the deploy) ==="
|
|
ok=0
|
|
for i in $(seq 1 36); do
|
|
BODY=$(curl -sf -m 5 http://localhost:20128/api/monitoring/health 2>/dev/null || true)
|
|
if printf '%s' "$BODY" | grep -q '"status":"healthy"'; then
|
|
ok=1
|
|
echo "✅ /api/monitoring/health -> healthy (attempt $i) — version $INSTALLED_VERSION"
|
|
break
|
|
fi
|
|
echo "… not healthy yet (attempt $i/36), retrying in 5s"
|
|
sleep 5
|
|
done
|
|
if [ "$ok" != "1" ]; then
|
|
echo "❌ Health check failed — /api/monitoring/health never reported healthy after ~3min"
|
|
echo "--- recent PM2 logs ---"
|
|
pm2 logs omniroute --lines 40 --nostream || true
|
|
exit 1
|
|
fi
|
|
echo "=== Deploy complete ==="
|