name: Deploy to racknerd1

# Deploys ArchNest (frontend + backend + guacd) to racknerd1 via Docker Compose.
#
# Triggers:
#   - push to main (automatic)
#   - manual run from the Actions tab (workflow_dispatch)
#
# Required GitHub Actions repo secrets (Settings -> Secrets and variables -> Actions):
#   RACKNERD_HOST    - racknerd1 hostname or IP the runner can SSH to
#   RACKNERD_USER    - deploy SSH user (must be in the docker group)
#   RACKNERD_SSH_KEY - private SSH key (PEM) for that user
#   RACKNERD_PORT    - SSH port (optional, defaults to 22)
#
# One-time host setup (NOT done by this workflow, see README Deployment section):
#   - Docker + Docker Compose installed, deploy user in the docker group
#   - mkdir -p /opt/archnest
#   - Create /opt/archnest/.env from .env.example with real generated secrets
#     (ARCHNEST_JWT_SECRET, ARCHNEST_SECRET_KEY, ARCHNEST_GUAC_CRYPT_KEY, ...).
#     This workflow refuses to deploy if that file is missing, and never
#     overwrites it, so live secrets/data are safe across deploys.

on:
  push:
    branches: [main]
  workflow_dispatch: {}

# Prevent overlapping deploys clobbering each other.
concurrency:
  group: deploy-racknerd1
  cancel-in-progress: false

env:
  DEPLOY_PATH: /opt/archnest

jobs:
  # Fail fast on build/type errors before touching the server.
  validate:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: 22

      - name: Install + type-check + build frontend
        run: |
          npm ci
          npx tsc --noEmit
          npm run build

      - name: Install + type-check + build backend
        working-directory: backend
        run: |
          npm ci
          npx tsc --noEmit
          npm run build

  deploy:
    needs: validate
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Pre-flight - confirm host .env exists (don't deploy without secrets)
        uses: appleboy/ssh-action@v1.2.0
        with:
          host: ${{ secrets.RACKNERD_HOST }}
          username: ${{ secrets.RACKNERD_USER }}
          key: ${{ secrets.RACKNERD_SSH_KEY }}
          port: ${{ secrets.RACKNERD_PORT || 22 }}
          script: |
            set -e
            mkdir -p ${{ env.DEPLOY_PATH }}
            if [ ! -f ${{ env.DEPLOY_PATH }}/.env ]; then
              echo "::error::Missing ${{ env.DEPLOY_PATH }}/.env on the host."
              echo "Create it from .env.example with real secrets before deploying."
              echo "It is intentionally never created/overwritten by this workflow."
              exit 1
            fi
            echo ".env present - proceeding."

      - name: Copy repo to racknerd1
        uses: appleboy/scp-action@v0.1.7
        with:
          host: ${{ secrets.RACKNERD_HOST }}
          username: ${{ secrets.RACKNERD_USER }}
          key: ${{ secrets.RACKNERD_SSH_KEY }}
          port: ${{ secrets.RACKNERD_PORT || 22 }}
          source: "."
          target: ${{ env.DEPLOY_PATH }}
          # Keep the host-only .env (and any other untracked host state) intact.
          rm: false
          overwrite: true

      - name: Build, restart, and clean up
        uses: appleboy/ssh-action@v1.2.0
        with:
          host: ${{ secrets.RACKNERD_HOST }}
          username: ${{ secrets.RACKNERD_USER }}
          key: ${{ secrets.RACKNERD_SSH_KEY }}
          port: ${{ secrets.RACKNERD_PORT || 22 }}
          command_timeout: 20m
          script: |
            set -e
            cd ${{ env.DEPLOY_PATH }}
            docker compose up -d --build --remove-orphans
            docker image prune -f

      - name: Health check (backend /api/health)
        uses: appleboy/ssh-action@v1.2.0
        with:
          host: ${{ secrets.RACKNERD_HOST }}
          username: ${{ secrets.RACKNERD_USER }}
          key: ${{ secrets.RACKNERD_SSH_KEY }}
          port: ${{ secrets.RACKNERD_PORT || 22 }}
          script: |
            set -e
            echo "Waiting for backend to become healthy..."
            for i in $(seq 1 30); do
              if curl -fsS http://127.0.0.1:4000/api/health >/dev/null 2>&1; then
                echo "Backend healthy."
                # Confirm the frontend container is serving too.
                if curl -fsS http://127.0.0.1:8080/ >/dev/null 2>&1; then
                  echo "Frontend healthy. Deploy succeeded."
                  exit 0
                fi
                echo "Frontend not ready yet..."
              fi
              sleep 5
            done
            echo "::error::Health check failed after ~150s. Dumping container status + logs."
            cd ${{ env.DEPLOY_PATH }}
            docker compose ps || true
            docker compose logs --tail=80 || true
            exit 1
