name: erp-kit Test Workspace

# Per PR: deploy a test workspace for each changed app, then run its
# integration and E2E suites in parallel against it.
# Delete the workspaces when the PR closes.
#
# Workspaces are named <prefix>-<app>-test-pr<N>, and are for automated tests
# only.
#
# Repo variables:
#   ERP_KIT_TEST_RUNNER       runner for the integration job          (default: ubuntu-slim)
#   ERP_KIT_BUILD_RUNNER      full VM runner: deploy + E2E container   (default: ubuntu-latest)
#   ERP_KIT_WORKSPACE_PREFIX  workspace name prefix      (default: repo name)
#   ERP_KIT_WORKSPACE_REGION  region for new workspaces  (default: asia-northeast)
#
# Changing ERP_KIT_WORKSPACE_REGION only affects workspaces created after the
# change: an existing <prefix>-<app>-test-pr<N> is reused wherever it already is.

on:
  pull_request:
    types: [opened, reopened, synchronize, closed]
    branches: [main]

jobs:
  # Detect which changed apps need a test workspace.
  # A docs/config-only PR resolves to apps='[]' and skips the matrix, but still
  # succeeds so a required check is satisfied.
  # On any doubt (diff error, empty list), test every app.
  changes:
    if: github.event.action != 'closed'
    runs-on: ubuntu-slim
    timeout-minutes: 5
    permissions:
      contents: read
    outputs:
      apps: ${{ steps.detect.outputs.apps }}
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
          fetch-depth: 0

      - name: Detect changed apps (fail-open)
        id: detect
        run: |
          set -euo pipefail
          base="${{ github.event.pull_request.base.sha }}"

          # Apps that have both a backend and a frontend.
          all_deployable_apps() {
            for dir in apps/*/; do
              app=$(basename "$dir")
              if [ -f "apps/$app/backend/package.json" ] && [ -f "apps/$app/frontend/package.json" ]; then
                printf '%s\n' "$app"
              fi
            done
          }
          to_json() { jq -Rsc 'split("\n") | map(select(. != "")) | unique'; }

          # If the diff can't be computed, test every app.
          if ! changed=$(git diff --name-only "$base" HEAD); then
            echo "::warning::git diff failed; testing all apps (fail-open)"
            apps=$(all_deployable_apps | to_json)
            echo "apps=$apps" >> "$GITHUB_OUTPUT"
            echo "Apps to test (all): $apps"
            exit 0
          fi

          # Drop docs/config paths; if nothing remains it is a docs-only PR.
          remaining=$(printf '%s\n' "$changed" \
            | grep -vE '(^|/)[^/]+\.md$|^\.claude/|^\.gitignore$|^LICENSE$' || true)
          if [ -z "$remaining" ]; then
            echo "Docs/config-only PR: skipping test workspace."
            echo "apps=[]" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          if printf '%s\n' "$remaining" | grep -qvE '^apps/'; then
            # A change outside apps/ (root, packages/**, lockfile) can affect every app.
            echo "Shared/root change: testing all deployable apps."
            apps=$(all_deployable_apps | to_json)
          else
            # Only apps/ changed: test the changed apps that have both sides.
            apps="[]"
            changed_apps=$(printf '%s\n' "$remaining" | awk -F/ 'NF>=2 {print $2}' | sort -u)
            for app in $changed_apps; do
              if [ -f "apps/$app/backend/package.json" ] && [ -f "apps/$app/frontend/package.json" ]; then
                apps=$(printf '%s' "$apps" | jq -c --arg a "$app" '. + [$a] | unique')
              fi
            done
          fi

          echo "apps=$apps" >> "$GITHUB_OUTPUT"
          echo "Apps to test: $apps"

  test-workspace:
    needs: changes
    if: github.event.action != 'closed' && needs.changes.outputs.apps != '[]'
    # The called workflow only reads the repo (no PR comment).
    permissions:
      contents: read
    strategy:
      fail-fast: false
      matrix:
        app: ${{ fromJson(needs.changes.outputs.apps) }}
    uses: ./.github/workflows/erp-kit-test-workspace-app.yml
    with:
      app: ${{ matrix.app }}
    # Pass only the secrets the called workflow declares.
    secrets:
      TAILOR_PFMU_CLIENT_ID: ${{ secrets.TAILOR_PFMU_CLIENT_ID }}
      TAILOR_PFMU_CLIENT_SECRET: ${{ secrets.TAILOR_PFMU_CLIENT_SECRET }}
      TAILOR_PLATFORM_TOKEN: ${{ secrets.TAILOR_PLATFORM_TOKEN }}

  # On PR close, delete this PR's test workspaces.
  destroy:
    if: github.event.action == 'closed'
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    timeout-minutes: 10
    steps:
      # Check out the base branch: a closed PR's merge ref may be gone.
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
          ref: ${{ github.event.pull_request.base.ref }}

      - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10

      - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
        with:
          cache: pnpm
          cache-dependency-path: ./pnpm-lock.yaml
          node-version-file: package.json

      - run: pnpm install

      - name: Fetch Tailor token
        id: token
        uses: ./.github/actions/erp-kit-fetch-tailor-token
        with:
          client_id: ${{ secrets.TAILOR_PFMU_CLIENT_ID }}
          client_secret: ${{ secrets.TAILOR_PFMU_CLIENT_SECRET }}
          token: ${{ secrets.TAILOR_PLATFORM_TOKEN }}

      - name: Destroy per-app test workspaces
        env:
          TAILOR_PLATFORM_TOKEN: ${{ steps.token.outputs.token }}
          PREFIX: ${{ vars.ERP_KIT_WORKSPACE_PREFIX || github.event.repository.name }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
        run: |
          set -euo pipefail
          # Delete by name pattern (<prefix>-<app>-test-pr<N>), not by listing
          # apps/: an app added only on the PR is absent from the base branch.
          # The -test-pr<N> suffix limits deletion to this PR's test workspaces.
          # The CLI can run from any app backend.
          backend=""
          for dir in apps/*/backend; do
            if [ -f "$dir/package.json" ]; then backend="$dir"; break; fi
          done
          if [ -z "$backend" ]; then
            echo "::warning::No app backend in the base checkout; cannot reach tailor to delete test workspaces."
            exit 0
          fi

          name_prefix="${PREFIX}-"
          name_suffix="-test-pr${PR_NUMBER}"
          matches="$(pnpm -C "$backend" exec tailor workspace list --json \
            | jq -r --arg p "$name_prefix" --arg s "$name_suffix" \
                '.[] | select((.name | startswith($p)) and (.name | endswith($s))) | "\(.id)\t\(.name)"')"

          if [ -z "$matches" ]; then
            echo "No test workspaces to delete for PR #${PR_NUMBER}"
            exit 0
          fi

          while IFS="$(printf '\t')" read -r id name; do
            [ -n "$id" ] || continue
            echo "Deleting workspace $name ($id)"
            pnpm -C "$backend" exec tailor workspace delete --yes --workspace-id "$id"
          done <<< "$matches"

      - name: Comment teardown
        if: ${{ github.event.pull_request.number != '' }}
        env:
          GH_TOKEN: ${{ github.token }}
        run: gh pr comment "${{ github.event.pull_request.number }}" --body "Test workspaces destroyed."
