name: gitea-merge-queue

# External serialized merge queue for Gitea 1.22.6.
#
# Gitea's `pull_auto_merge` table is not a real merge queue: it does not
# serialize green PRs against a freshly-tested latest main. This workflow runs
# the user-space queue bot, one PR per tick, using the non-bypass merge actor.
#
# Queue contract:
#   - add label `merge-queue` to an open same-repo PR
#   - bot updates stale PR heads with current main, then waits for CI
#   - bot merges only when current main is green and required PR contexts pass
#   - add `merge-queue-hold` to pause a queued PR without removing it

on:
  # Schedule moved to operator-config:
  #   /etc/cron.d/molecule-core-merge-queue ->
  #   /usr/local/bin/molecule-core-cron-bot.sh merge-queue
  #
  # The queue bot still processes one PR per tick, but no longer occupies
  # one of the shared Actions runners just to poll.
  workflow_dispatch:

permissions:
  contents: read

concurrency:
  group: gitea-merge-queue-${{ github.repository }}
  cancel-in-progress: false

jobs:
  queue:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - name: Check out queue script from main
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          ref: ${{ github.event.repository.default_branch }}

      - name: Process one queued PR
        env:
          # AUTO_SYNC_TOKEN is the devops-engineer persona PAT. It is the
          # non-bypass merge actor allowed by branch protection.
          GITEA_TOKEN: ${{ secrets.AUTO_SYNC_TOKEN }}
          GITEA_HOST: git.moleculesai.app
          REPO: ${{ github.repository }}
          WATCH_BRANCH: ${{ github.event.repository.default_branch }}
          QUEUE_LABEL: merge-queue
          HOLD_LABEL: merge-queue-hold
          UPDATE_STYLE: merge
          REQUIRED_CONTEXTS: >-
            CI / all-required (pull_request),
            sop-checklist / all-items-acked (pull_request)
          # Push-side required contexts. Checking CI / all-required (push)
          # explicitly instead of the combined state avoids false-pause when
          # non-blocking jobs (continue-on-error: true) have failed — those
          # failures pollute combined state but do not gate merges.
          # NOTE: the event-suffixed context name is intentional — branch protection
          # MUST require `CI / all-required (pull_request)` (with suffix), NOT the
          # bare `CI / all-required`. Gitea treats absent contexts as pending, not
          # skipped; requiring the bare name silently blocks all merges (issue #1473).
          PUSH_REQUIRED_CONTEXTS: CI / all-required (push)
        run: python3 .gitea/scripts/gitea-merge-queue.py
