molecule-ci/.gitea/actions/audit-force-merge/action.yml
claude-ceo-assistant (Claude Opus 4.7 on Hongming's MacBook) 120b71c564 feat(actions): add audit-force-merge composite action
§SOP-6 force-merge detector, hosted as a Gitea Actions composite
action so it can be vendored into every org repo via a single
`uses:` line instead of copy-pasting the bash. Source of truth
for the audit script logic.

Why composite vs reusable workflow: Gitea 1.22.6 doesn't support
cross-repo `uses: org/repo/.gitea/workflows/X.yml@ref`. Cross-repo
reusable workflows landed in go-gitea/gitea#32562 (1.26.0, Oct 2025)
and have not been backported. Composite actions resolve via the
actions-fetch path which works cross-repo against a public callee.
Re-evaluate when operator host runs Gitea ≥ 1.26.

Consumer workflow shape:

    on:
      pull_request_target:
        types: [closed]
    jobs:
      audit:
        if: github.event.pull_request.merged == true
        runs-on: ubuntu-latest
        steps:
          - uses: molecule-ai/molecule-ci/.gitea/actions/audit-force-merge@main
            with:
              gitea-token: ${{ secrets.SOP_TIER_CHECK_TOKEN }}
              repo: ${{ github.repository }}
              pr-number: ${{ github.event.pull_request.number }}
              required-checks: |
                sop-tier-check / tier-check (pull_request)

No actions/checkout step needed in the consumer — the audit script
does pure API calls, never reads working tree. Removing checkout is
also a small security win (PR head code never loaded).

Verified end-to-end on internal#123 + molecule-core#150 with the
inline copies (which this PR will replace via consumer-side stub
PRs once merged). Tier: low.
2026-05-08 20:29:40 -07:00

56 lines
2.1 KiB
YAML

name: 'Audit force-merge'
description: >-
§SOP-6 force-merge audit. Detects PRs merged with required-status-checks
not green at HEAD SHA and emits incident.force_merge JSON to runner
stdout. Vector docker_logs source ships the line to Loki on
molecule-canonical-obs (per reference_obs_stack_phase1).
# Why a composite action and not a reusable workflow:
# Gitea 1.22.6 does NOT support cross-repo `uses: org/repo/.gitea/
# workflows/X.yml@ref`. Cross-repo reusable workflows landed in
# go-gitea/gitea PR #32562 in Gitea 1.26.0 (Oct 2025). On 1.22.x the
# clone fails because act_runner mints a caller-scoped GITEA_TOKEN.
# Composite actions resolve via the actions-fetch path which works
# cross-repo on 1.22 against a public callee — that's us. Re-evaluate
# this choice when the operator host upgrades to Gitea ≥ 1.26.
inputs:
gitea-token:
description: >-
PAT for sop-tier-bot (or equivalent read-only audit identity).
Needs read:user,read:repository,read:issue scopes — admin scope
is intentionally NOT required.
required: true
gitea-host:
description: 'Gitea host'
required: false
default: 'git.moleculesai.app'
repo:
description: 'owner/name; typically ${{ github.repository }}'
required: true
pr-number:
description: 'PR number; typically ${{ github.event.pull_request.number }}'
required: true
required-checks:
description: >-
Newline-separated required-status-check context names. Mirror
of branch protection's status_check_contexts. Declared at the
caller because /branch_protections requires admin scope which
this audit identity intentionally does not hold (least-privilege).
When the required-check set changes, update both branch
protection AND this input.
required: true
runs:
using: composite
steps:
- name: Detect force-merge + emit audit event
shell: bash
env:
GITEA_TOKEN: ${{ inputs.gitea-token }}
GITEA_HOST: ${{ inputs.gitea-host }}
REPO: ${{ inputs.repo }}
PR_NUMBER: ${{ inputs.pr-number }}
REQUIRED_CHECKS: ${{ inputs.required-checks }}
run: bash "$GITHUB_ACTION_PATH/audit.sh"