name: gate
on:
  pull_request:
  merge_group:
    types: [checks_requested]
  push:
    branches: {{GATE_PUSH_BRANCHES_YAML}}
    tags: ['v*']
concurrency:
  group: gate-${{ github.event.pull_request.number || (github.event_name == 'push' && github.sha) || github.ref }}
  cancel-in-progress: true
permissions:
  actions: read # #8206: exact-SHA development workflow verdict
  contents: read
  pull-requests: read # #8343: source commit declarations omitted by queue squashes
  packages: read
jobs:
  gate:
    runs-on: [self-hosted, linux, x64, mmi-live]
    timeout-minutes: 10
    steps:
      # #8206: keep a fresh successful tag gate; promote the exact development verdict, not a ref.
      - name: Reuse development gate for rc tag
        id: development-gate
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
        with:
          script: |
            core.setOutput('reuse', 'false');
            if (context.eventName !== 'push' || !/^refs\/tags\/v.+-rc\.\d+$/.test(context.ref) || !/^[0-9a-f]{40}$/.test(context.sha)) return;
            try {
              const { data: current } = await github.rest.actions.getWorkflowRun({ ...context.repo, run_id: context.runId });
              const { data } = await github.rest.actions.listWorkflowRuns({
                ...context.repo, workflow_id: current.workflow_id, branch: 'development',
                event: 'push', head_sha: context.sha, per_page: 100
              });
              // API returns newest first: a newer red/pending rerun must not reuse an older green.
              const run = data.workflow_runs[0];
              if (!run || run.head_sha !== context.sha || run.head_branch !== 'development' ||
                  run.event !== 'push' || run.status !== 'completed' || run.conclusion !== 'success') return;
              const jobs = await github.paginate(github.rest.actions.listJobsForWorkflowRun, {
                ...context.repo, run_id: run.id, filter: 'latest', per_page: 100
              });
              if (!jobs.some(job => job.name === 'gate' && job.status === 'completed' && job.conclusion === 'success')) return;
              core.setOutput('reuse', 'true');
              core.info('Reusing successful development gate run ' + run.id + ' for exact SHA ' + context.sha);
            } catch (error) {
              core.warning('Development gate proof unavailable; running normal gate: ' + error.message);
            }
      - if: ${{ steps.development-gate.outputs.reuse != 'true' }}
        uses: mutmutco/MMI-Hub/.github/actions/runner-checkout@{{GATE_RUNNER_SHA}}
        with:
          lockfile-path: package-lock.json
          fetch-depth: 0 # #8129: runbook freshness needs the PR merge-base history.
      - name: Runbook guard
        if: ${{ steps.development-gate.outputs.reuse != 'true' && github.event_name == 'pull_request' }}
        uses: mutmutco/MMI-Hub/.github/actions/runbook-guard@{{GATE_RUNBOOK_SHA}}
        with:
          base-ref: ${{ format('origin/{0}', github.base_ref) }}
      - name: Isolate npm cache
        if: ${{ steps.development-gate.outputs.reuse != 'true' }}
        run: echo "NPM_CONFIG_CACHE=$RUNNER_TEMP/npm-cache" >> "$GITHUB_ENV"
      - uses: mutmutco/MMI-Hub/.github/actions/runner-node-toolchain@{{GATE_RUNNER_SHA}}
        if: ${{ steps.development-gate.outputs.reuse != 'true' }}
        with:
          node-version: 24
          npm-version: '12'
      - run: npm ci --no-audit --no-fund
        if: ${{ steps.development-gate.outputs.reuse != 'true' }}
      - run: npm ci --prefix module --no-audit --no-fund
        if: ${{ steps.development-gate.outputs.reuse != 'true' }}
        env:
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - uses: mutmutco/MMI-Hub/.github/actions/run-with-budget@{{GATE_BUDGET_SHA}}
        if: ${{ steps.development-gate.outputs.reuse != 'true' }}
        with:
          command: npm run typecheck && npm run build && npm --prefix module run check
          max-seconds: {{GATE_MAX_SECONDS}}
          working-directory: .
