§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.
56 lines
2.1 KiB
YAML
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"
|