# .gitea/workflows/ci.yml — Wisp CI/CD (Gitea Actions, GitHub-Actions syntax)
# ---------------------------------------------------------------------------
# Runner model: a single self-hosted act_runner ON the briggen.dev server, with
# the host Docker socket mounted into job containers (see /root/act_runner/
# config.yaml). Because the runner is co-located with the host Docker daemon +
# the Traefik `proxy` network, deploy needs NO ssh keys and NO image registry —
# `docker compose` simply builds and (re)creates the `wisp` container on the host.
#
# Triggers:
#   push to master -> test, then deploy-web
#   pull_request   -> test only
#
# Required repo secrets (Settings -> Actions -> Secrets) — used by the APK job
# once it is enabled (see TODO at the bottom): ANDROID_KEYSTORE_BASE64,
# ANDROID_KEYSTORE_PASSWORD, ANDROID_KEY_ALIAS, ANDROID_KEY_PASSWORD.
# ---------------------------------------------------------------------------

name: CI

on:
  push:
    branches: [master]
    tags: ['v*']
  pull_request:

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

jobs:
  # ---- test: typecheck + unit tests ----------------------------------------
  # Runs on the default runner image (has Node, which actions/checkout needs);
  # Bun is installed in a step rather than via a Node-less bun container.
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Bun
        run: |
          curl -fsSL https://bun.sh/install | bash
          echo "$HOME/.bun/bin" >> "$GITHUB_PATH"
      - run: bun install --frozen-lockfile
      - run: bun run typecheck
      - run: bun run test

  # ---- deploy-web: build + (re)start the nginx web container on the host ----
  # Runs on the server runner; the mounted host Docker socket means `docker
  # compose` builds the image and recreates the `wisp` service in place, joined
  # to the existing Traefik `proxy` network. No SSH, no registry.
  deploy-web:
    needs: test
    if: github.event_name == 'push' && github.ref == 'refs/heads/master'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build + (re)start wisp on the host
        run: docker compose -f deploy/wisp.compose.yml --project-directory . up -d --build --remove-orphans
      - name: Health check
        run: |
          for i in $(seq 1 10); do
            if docker exec wisp wget -qO- http://localhost:8080/healthz | grep -q ok; then
              echo "healthy"; exit 0
            fi
            sleep 2
          done
          echo "wisp container did not become healthy" >&2; exit 1

  # ---- build-apk: signed release APK, served at /wisp.apk ------------------
  # Socket-safe: the whole Android build runs inside `docker buildx build`
  # (context streamed to the host BuildKit — no host bind-mounts), signed via
  # BuildKit --secret from the ANDROID_* repo secrets. We then extract the APK
  # with docker create/cp and copy it into the running `wisp` web container so
  # it is downloadable at https://wisp.briggen.dev/wisp.apk.
  build-apk:
    # ONLY on version tags (v*). The native build is heavy (NDK/whisper.cpp) and
    # slow on the shared prod runner; building it on every push would peg the box
    # (and degrade the live web apps). Cut a release with: git tag v1.2.3 && git push --tags
    needs: [test]
    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest
    timeout-minutes: 120
    steps:
      - uses: actions/checkout@v4
      - name: Stage signing secrets as files
        run: |
          umask 077
          mkdir -p "$RUNNER_TEMP/sec"
          printf '%s' "${{ secrets.ANDROID_KEYSTORE_BASE64 }}"   > "$RUNNER_TEMP/sec/ks_b64"
          printf '%s' "${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" > "$RUNNER_TEMP/sec/ks_pass"
          printf '%s' "${{ secrets.ANDROID_KEY_ALIAS }}"         > "$RUNNER_TEMP/sec/ks_alias"
          printf '%s' "${{ secrets.ANDROID_KEY_PASSWORD }}"      > "$RUNNER_TEMP/sec/ks_keypass"
      - name: Build signed APK image
        run: |
          docker buildx build \
            --secret id=ks_b64,src="$RUNNER_TEMP/sec/ks_b64" \
            --secret id=ks_pass,src="$RUNNER_TEMP/sec/ks_pass" \
            --secret id=ks_alias,src="$RUNNER_TEMP/sec/ks_alias" \
            --secret id=ks_keypass,src="$RUNNER_TEMP/sec/ks_keypass" \
            -f docker/android.Dockerfile -t wisp-android:ci --load .
      - name: Extract APK + publish to the host (served at /wisp.apk via the compose volume)
        run: |
          cid=$(docker create wisp-android:ci)
          docker cp "$cid:/workspace/android/app/build/outputs/apk/release/app-release.apk" ./app-release.apk
          docker rm "$cid"
          ls -lh app-release.apk
          # Stream the APK into a host-mounted dir so it survives web container
          # recreation (the wisp container mounts /srv/wisp/wisp.apk read-only).
          docker run --rm -i -v /srv/wisp:/out alpine:3 sh -c 'mkdir -p /out && cat > /out/wisp.apk' < ./app-release.apk
      - name: Cleanup staged secrets
        if: always()
        run: rm -rf "$RUNNER_TEMP/sec"
      - name: Upload APK artifact
        uses: actions/upload-artifact@v3
        continue-on-error: true
        with:
          name: wisp-release-apk
          path: app-release.apk
