name: "CI: Integration"

defaults:
  run:
    shell: bash

on:
  workflow_dispatch:
    inputs:
      gh_ref:
        description: 'Name of the branch or ref'
        type: string
        required: true
        default: 'develop'

      shards:
        description: 'Shard layout (JSON array)'
        type: choice
        required: true
        default: '[1, 2, 3, 4]'
        options:
          - '[1, 2, 3, 4]'
          - '[1, 2, 3, 4, 5, 6]'
          - '[1, 2]'
          - '[1]'

      workers:
        description: 'Playwright workers per shard'
        type: string
        required: true
        default: '2'

  pull_request:
    paths:
      - 'frontend/**'
      - 'common/**'
      - 'render-wasm/**'
      - '.github/workflows/tests-integration.yml'

    types:
      - opened
      - synchronize
      - ready_for_review

  push:
    branches:
      - develop
      - staging

    paths:
      - 'frontend/**'
      - 'common/**'
      - 'render-wasm/**'
      - '.github/workflows/tests-integration.yml'

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || inputs.gh_ref || github.ref }}
  cancel-in-progress: true

jobs:
  build-integration:
    if: ${{ !github.event.pull_request.draft }}
    name: "Build Integration Bundle"
    runs-on: penpot-extended-runner
    timeout-minutes: 30
    container:
      image: penpotapp/devenv:latest
      volumes:
        - /var/cache/github-runner/m2:/root/.m2
        - /var/cache/github-runner/gitlib:/root/.gitlibs

    outputs:
      bundle_key: ${{ steps.vars.outputs.bundle_key }}

    steps:
      # An empty `ref` makes checkout fall back to its default (the PR merge
      # ref on pull_request, the pushed ref on push).
      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          ref: ${{ inputs.gh_ref }}

      # The cache key must come from the SHA actually checked out: on a manual
      # run `github.sha` points at the dispatching ref, not at `gh_ref`.
      - name: Extract cache key
        id: vars
        run: |
          echo "bundle_key=integration-bundle-$(git rev-parse HEAD)" >> $GITHUB_OUTPUT

      - name: Build Bundle
        working-directory: ./frontend
        run: |
          ./scripts/build

      - name: Store Bundle Cache
        uses: actions/cache@v5
        with:
          key: ${{ steps.vars.outputs.bundle_key }}
          path: frontend/resources/public

  test-integration:
    if: ${{ !github.event.pull_request.draft }}
    name: "Integration Tests (${{ matrix.shard }})"
    runs-on: penpot-extended-runner
    timeout-minutes: ${{ github.base_ref == 'staging' && 60 || 25 }}

    needs: build-integration

    # TEMPORARY (release stabilization): PRs targeting `staging` run on a
    # single serial shard, so new flakes cannot block the release work.
    # Remove the `github.base_ref` branch below to restore full parallelism.
    strategy:
      fail-fast: false
      matrix:
        shard: ${{ fromJSON(inputs.shards || (github.base_ref == 'staging' && '[1]' || '[1, 2, 3, 4]')) }}

    container:
      image: penpotapp/devenv:latest
      volumes:
        - /var/cache/github-runner/m2:/root/.m2
        - /var/cache/github-runner/gitlib:/root/.gitlibs
        - /var/cache/github-runner/ms-playwright:/ms-playwright
      env:
        PLAYWRIGHT_BROWSERS_PATH: /ms-playwright

    steps:
      - name: Checkout Repository
        uses: actions/checkout@v6
        with:
          ref: ${{ inputs.gh_ref }}

      - name: Restore Cache
        uses: actions/cache/restore@v5
        with:
          key: ${{ needs.build-integration.outputs.bundle_key }}
          path: frontend/resources/public

      - name: Install deps
        working-directory: ./frontend
        run: |
          corepack enable;
          corepack install;
          pnpm install --frozen-lockfile;

      # No-op once the shared volume is warm; keeps the first run working.
      - name: Install Playwright Chromium
        working-directory: ./frontend
        run: pnpm exec playwright install chromium

      # `strategy.job-total` is the matrix size, so the shard denominator
      # follows the `shards` input without being hardcoded.
      - name: Run Tests
        working-directory: ./frontend
        env:
          WORKERS: ${{ inputs.workers }}
          BASE_REF: ${{ github.base_ref }}
        run: |
          # TEMPORARY (release stabilization): see the note on the matrix above.
          if [ -z "$WORKERS" ]; then
            if [ "$BASE_REF" = "staging" ]; then WORKERS=1; else WORKERS=2; fi
          fi
          echo "Running shard ${{ matrix.shard }}/${{ strategy.job-total }} with $WORKERS workers"
          pnpm exec playwright test --project default \
            --workers="$WORKERS" \
            --shard=${{ matrix.shard }}/${{ strategy.job-total }} \
            --reporter=blob

      - name: Upload blob report
        uses: actions/upload-artifact@v7
        if: always()
        with:
          name: integration-blob-report-${{ matrix.shard }}
          path: frontend/blob-report/
          overwrite: true
          retention-days: 3

      - name: Upload test result
        uses: actions/upload-artifact@v7
        if: always()
        with:
          name: integration-tests-result-${{ matrix.shard }}
          path: frontend/test-results/
          overwrite: true
          if-no-files-found: ignore
          retention-days: 3

  merge-reports:
    if: ${{ always() && !github.event.pull_request.draft && needs.test-integration.result != 'skipped' }}
    name: "Merge Integration Reports"
    runs-on: penpot-extended-runner
    timeout-minutes: 15

    needs: test-integration

    container:
      image: penpotapp/devenv:latest
      volumes:
        - /var/cache/github-runner/m2:/root/.m2
        - /var/cache/github-runner/gitlib:/root/.gitlibs

    steps:
      - name: Checkout Repository
        uses: actions/checkout@v6
        with:
          ref: ${{ inputs.gh_ref }}

      - name: Install deps
        working-directory: ./frontend
        run: |
          corepack enable;
          corepack install;
          pnpm install --frozen-lockfile;

      - name: Download blob reports
        uses: actions/download-artifact@v7
        with:
          path: frontend/all-blob-reports
          pattern: integration-blob-report-*
          merge-multiple: true

      - name: Merge into HTML report
        working-directory: ./frontend
        env:
          PLAYWRIGHT_JSON_OUTPUT_NAME: report.json
        run: |
          pnpm exec playwright merge-reports \
            --reporter=html,json,list ./all-blob-reports

      - name: Test summary
        if: always()
        working-directory: ./frontend
        run: |
          if [ ! -f report.json ]; then
            echo "No report produced (all shards failed early)." >> "$GITHUB_STEP_SUMMARY"
            exit 0
          fi
          jq -r -f ../.github/scripts/playwright-summary.jq report.json >> "$GITHUB_STEP_SUMMARY"

      - name: Upload HTML report
        uses: actions/upload-artifact@v7
        with:
          name: integration-html-report
          path: frontend/playwright-report/
          overwrite: true
          retention-days: 7
