84 lines
3.8 KiB
YAML
84 lines
3.8 KiB
YAML
# audit-force-merge — emit `incident.force_merge` to runner stdout when
|
|
# a PR is merged with required-status-checks not green. ALSO emits
|
|
# `incident.reserved_self_merge` when a PR that touched a CTO-reserved path
|
|
# (.gitea/reserved-paths.txt) was merged by its own author (author==merger) —
|
|
# the DETECTIVE backstop for the reserved-path-review preventive gate, since a
|
|
# force-merge bypasses any pre-merge gate (cp#673 precedent). Vector picks the
|
|
# JSON line off docker_logs and ships to Loki on molecule-canonical-obs (per
|
|
# `reference_obs_stack_phase1`); query as:
|
|
#
|
|
# {host="operator"} |= "event_type" |= "incident.force_merge" | json
|
|
# {host="operator"} |= "event_type" |= "incident.reserved_self_merge" | json
|
|
#
|
|
# Closes the §SOP-6 audit gap (the doc says force-merges write to
|
|
# `structure_events`, but that table lives in the platform DB, not
|
|
# Gitea-side; Loki is the practical equivalent for Gitea Actions
|
|
# events). When the credential / observability stack converges later,
|
|
# this can sync into structure_events from Loki via a backfill job —
|
|
# the structured JSON shape is forward-compatible.
|
|
#
|
|
# Logic in `.gitea/scripts/audit-force-merge.sh` per the same script-
|
|
# extract pattern as sop-checklist.
|
|
|
|
name: audit-force-merge
|
|
|
|
# pull_request_target loads from the base branch — same security model
|
|
# as sop-checklist. Without this, an attacker could rewrite the
|
|
# workflow on a PR and skip the audit emission for their own
|
|
# force-merge. See `.gitea/workflows/sop-checklist.yml` for the full
|
|
# rationale.
|
|
concurrency:
|
|
# Auto-cancel a superseded run: a newer commit/event supersedes the old
|
|
# one instead of leaving it queued forever (root fix for waiting-run pileup).
|
|
# PR events group by PR number; push events group by ref.
|
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
on:
|
|
pull_request_target:
|
|
types: [closed]
|
|
|
|
jobs:
|
|
audit:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
# Skip when PR is closed without merge — saves a runner.
|
|
if: github.event.pull_request.merged == true
|
|
steps:
|
|
- name: Check out base branch (for the script)
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
ref: ${{ github.event.pull_request.base.sha }}
|
|
- name: Detect force-merge + emit audit event
|
|
env:
|
|
# Same org-level secret the sop-checklist workflow uses.
|
|
GITEA_TOKEN: ${{ secrets.SOP_CHECKLIST_GATE_TOKEN }}
|
|
GITEA_HOST: git.moleculesai.app
|
|
REPO: ${{ github.repository }}
|
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
# Required-status-check contexts to evaluate at merge time.
|
|
# Branch-aware JSON dict: keys are protected branch names,
|
|
# values are arrays of context names that branch protection
|
|
# requires for that branch. Mirror this against branch
|
|
# protection (settings → branches → protected branch →
|
|
# required checks) for each branch listed here.
|
|
#
|
|
# Declared here rather than fetched from /branch_protections
|
|
# because that endpoint requires admin write — sop-checklist-bot is
|
|
# read-only by design (least-privilege).
|
|
REQUIRED_CHECKS_JSON: |
|
|
{
|
|
"main": [
|
|
"CI / all-required (pull_request)",
|
|
"E2E API Smoke Test / E2E API Smoke Test (pull_request)",
|
|
"Handlers Postgres Integration / Handlers Postgres Integration (pull_request)",
|
|
"Secret scan / Scan diff for credential-shaped strings (pull_request)"
|
|
],
|
|
"staging": [
|
|
"CI / all-required (pull_request)",
|
|
"sop-checklist / all-items-acked (pull_request)"
|
|
]
|
|
}
|
|
run: bash .gitea/scripts/audit-force-merge.sh
|