# Build the app + bot container images, publish them to Gitea's container
# registry, then roll the production stack onto the fresh images — all on every
# merge to main. Production only ever pulls prebuilt images; it never builds.
#
# Two jobs run in sequence:
#   build   — builds & pushes website-app / website-bot images (on ubuntu-latest)
#   deploy  — `needs: build`, so it starts only after a clean build+push, and
#             pulls + recreates the stack on the production host (on uom-deploy-runner)
#
# Prerequisites (one-time):
#   • An always-on Gitea runner with label `ubuntu-latest` whose jobs have the
#     host Docker socket mounted (/var/run/docker.sock), so `docker build` talks
#     to the host daemon. This also gives free layer caching between runs.
#   • A second self-hosted runner labelled `uom-deploy-runner` ON the production host,
#     with access to the Docker daemon and to /home/perry/website (the directory
#     holding the production docker-compose.yml + .env). This is what actually
#     rolls the stack; it must be able to `docker compose pull` from the registry
#     (log in once on the host, or ensure the images are public-read).
#   • Two repo secrets (Settings → Actions → Secrets):
#       REGISTRY_USER  — the Gitea username that owns the token below
#       REGISTRY_TOKEN — a Gitea access token with `write:package` (+ read:package)
#     See the PR description / README for step-by-step token creation.
#
# Produces, in gitea.whitlocktech.com/<owner>/ :
#   website-app:latest  + website-app:sha-<7>
#   website-bot:latest  + website-bot:sha-<7>
# then deploys the `:latest` images (docker-compose.yml defaults IMAGE_TAG=latest).

name: Build container images

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

concurrency:
  group: images-${{ github.ref }}
  cancel-in-progress: true

env:
  REGISTRY: gitea.whitlocktech.com

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Check out the merged commit
        uses: actions/checkout@v4

      - name: Derive image refs (registry owner must be lowercase for Docker)
        run: |
          set -euo pipefail
          OWNER="$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')"
          SHORT_SHA="${GITHUB_SHA:0:7}"
          echo "APP_IMAGE=${REGISTRY}/${OWNER}/website-app" >> "$GITHUB_ENV"
          echo "BOT_IMAGE=${REGISTRY}/${OWNER}/website-bot" >> "$GITHUB_ENV"
          echo "TAG=sha-${SHORT_SHA}" >> "$GITHUB_ENV"

      - name: Verify the Docker daemon is reachable
        # Fails fast with a clear message if the host socket isn't mounted into
        # the job container (the one hard runner prerequisite).
        run: |
          set -euo pipefail
          if ! docker info >/dev/null 2>&1; then
            echo "::error::Docker daemon not reachable. Mount /var/run/docker.sock into the runner's job containers."
            exit 1
          fi
          echo "Docker daemon OK"

      - name: Log in to the Gitea container registry
        run: |
          set -euo pipefail
          echo "${{ secrets.REGISTRY_TOKEN }}" \
            | docker login "${REGISTRY}" -u "${{ secrets.REGISTRY_USER }}" --password-stdin

      - name: Build & push the app image (server + client)
        run: |
          set -euo pipefail
          docker build -f Dockerfile \
            -t "${APP_IMAGE}:latest" \
            -t "${APP_IMAGE}:${TAG}" \
            .
          docker push "${APP_IMAGE}:latest"
          docker push "${APP_IMAGE}:${TAG}"

      - name: Build & push the bot image
        run: |
          set -euo pipefail
          docker build -f bot/Dockerfile \
            -t "${BOT_IMAGE}:latest" \
            -t "${BOT_IMAGE}:${TAG}" \
            .
          docker push "${BOT_IMAGE}:latest"
          docker push "${BOT_IMAGE}:${TAG}"

      - name: Log out (clear cached credentials from the runner)
        if: always()
        run: docker logout "${REGISTRY}" || true

  deploy:
    # Roll production onto the images `build` just pushed. `needs: build` makes
    # this wait for a clean build+push — if the build fails, deploy never fires,
    # so the running stack is left untouched rather than torn down for nothing.
    needs: build
    runs-on: uom-deploy-runner
    # Guard against a workflow_dispatch fired from a non-main branch: only ever
    # deploy the main line to production.
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Pull the fresh images and recreate the stack
        # `pull` grabs the new :latest images the build job published; `down`
        # then `up -d` recreates the containers on them. Compose only recreates
        # services whose image digest changed, so the DB stays put.
        run: |
          set -euo pipefail
          cd /home/perry/website
          docker compose pull
          docker compose down
          docker compose up -d
          docker compose ps
