Spaces:
Runtime error
Runtime error
| 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 ===" | |