# -----------------------------------------------------------------------------
# cameleer-website — Deploy to Hetzner Webhosting L
#
# MANUAL TRIGGER ONLY. Runs exclusively on workflow_dispatch from the Gitea UI
# (Actions → deploy → Run workflow). Does NOT auto-deploy on push to main —
# merges to main must be explicitly promoted to production.
#
# Build and deploy run in a single job; rsync uploads dist/ directly. No
# upload-artifact round-trip (v3 strips dotfiles, v4 isn't supported on Gitea).
#
# Security headers (HSTS, CSP, X-Frame-Options, etc.) are NOT set by this
# deploy. Hetzner Webhosting L runs Apache with AllowOverride None on the
# user docroot, so file-based .htaccess is silently ignored. All response
# headers are owned by Cloudflare Transform Rules — see OPERATOR-CHECKLIST.md
# §2 "Cloudflare". Apache config exposed via konsoleH UI is the only origin-
# side override path and is not managed from this repo.
#
# Runner: self-hosted arm64 Gitea runner. Adjust `runs-on` if your runner's
# labels differ. Deploy target is Hetzner amd64 — arch mismatch is a non-issue
# because the bundle is static HTML/CSS/JS.
#
# Required secrets (repo settings → Actions → Secrets):
#   SFTP_HOST, SFTP_USER, SFTP_PATH, SFTP_KEY, SFTP_KNOWN_HOSTS
#   PUBLIC_AUTH_SIGNIN_URL, PUBLIC_AUTH_SIGNUP_URL, PUBLIC_SALES_EMAIL
# -----------------------------------------------------------------------------

name: deploy

on:
  workflow_dispatch:

concurrency:
  group: deploy-production
  cancel-in-progress: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 25
    env:
      PUBLIC_AUTH_SIGNIN_URL: ${{ secrets.PUBLIC_AUTH_SIGNIN_URL }}
      PUBLIC_AUTH_SIGNUP_URL: ${{ secrets.PUBLIC_AUTH_SIGNUP_URL }}
      PUBLIC_SALES_EMAIL: ${{ secrets.PUBLIC_SALES_EMAIL }}

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run unit tests (sanity check)
        run: npm test

      - name: Build site
        run: npm run build

      - name: Guard — no TBD markers may ship in built HTML
        run: |
          if grep -rlE '(TBD):' dist 2>/dev/null | grep -E '\.(html|svg)$'; then
            echo "Built output contains unfilled <TBD:...>) markers."
            echo "Fill in imprint.astro and privacy.astro operator fields before merging to main."
            exit 1
          fi

      - name: Configure SSH
        env:
          SFTP_KEY: ${{ secrets.SFTP_KEY }}
          SFTP_KNOWN_HOSTS: ${{ secrets.SFTP_KNOWN_HOSTS }}
        run: |
          set -e
          : "${SFTP_KEY:?SFTP_KEY secret must be set}"
          : "${SFTP_KNOWN_HOSTS:?SFTP_KNOWN_HOSTS secret must be set}"
          mkdir -p ~/.ssh
          printf '%s\n' "$SFTP_KEY" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          printf '%s\n' "$SFTP_KNOWN_HOSTS" > ~/.ssh/known_hosts
          chmod 644 ~/.ssh/known_hosts
          # Ensure rsync + openssh are present even on a minimal runner image.
          if ! command -v rsync >/dev/null 2>&1 || ! command -v ssh >/dev/null 2>&1; then
            if command -v sudo >/dev/null 2>&1; then SUDO=sudo; else SUDO=; fi
            $SUDO apt-get update -qq
            $SUDO apt-get install -y --no-install-recommends rsync openssh-client
          fi

      - name: Deploy via rsync
        env:
          SFTP_USER: ${{ secrets.SFTP_USER }}
          SFTP_HOST: ${{ secrets.SFTP_HOST }}
          SFTP_PATH: ${{ secrets.SFTP_PATH }}
        run: |
          # Fail loudly if any secret is missing — otherwise rsync --delete
          # could be directed at the SSH user's home root.
          : "${SFTP_USER:?SFTP_USER secret must be set}"
          : "${SFTP_HOST:?SFTP_HOST secret must be set}"
          : "${SFTP_PATH:?SFTP_PATH secret must be set}"
          # Hetzner Webhosting splits SSH into two ports:
          #   port 22  — SFTP only, no remote command exec
          #   port 222 — full SSH with shell exec (rsync needs this)
          # `--rsync-path=/usr/bin/rsync` tells the local rsync where to find
          # the remote binary on Hetzner's locked-down PATH.
          # `BatchMode=yes` disables interactive prompts.
          rsync -avz --delete --rsync-path=/usr/bin/rsync \
            -e "ssh -p 222 -i $HOME/.ssh/id_ed25519 -o BatchMode=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=$HOME/.ssh/known_hosts" \
            dist/ "$SFTP_USER@$SFTP_HOST:$SFTP_PATH/"

      - name: Post-deploy smoke test
        run: |
          set -e
          echo "Checking security headers on www.cameleer.io..."
          HEADERS=$(curl -sI https://www.cameleer.io/ || echo "")
          echo "$HEADERS" | grep -i '^strict-transport-security:' || { echo "HSTS missing"; exit 1; }
          echo "$HEADERS" | grep -i '^content-security-policy:' || { echo "CSP missing"; exit 1; }
          echo "$HEADERS" | grep -i '^x-frame-options:' || { echo "XFO missing"; exit 1; }
          echo "All required headers present on the live origin."
