ci: add main-push-audit tripwire
This commit is contained in:
62
.github/workflows/main-push-audit.yml
vendored
Normal file
62
.github/workflows/main-push-audit.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
name: Audit — Direct push to main
|
||||||
|
|
||||||
|
# Tripwire: fires on every push to main and flags pushes that did not come
|
||||||
|
# from a merged PR. Branch protection is enforced at the repo level, but this
|
||||||
|
# provides a soft audit trail for any direct pushes (e.g. emergency hotfixes).
|
||||||
|
#
|
||||||
|
# Behavior:
|
||||||
|
# - If the pushed commit is associated with a merged PR, exit quietly.
|
||||||
|
# - If not, open a GitHub issue tagging the pusher so it's visible.
|
||||||
|
#
|
||||||
|
# This never blocks the push. It's an audit, not a gate.
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: write
|
||||||
|
pull-requests: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
audit:
|
||||||
|
name: Check commit came from PR
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check for associated PR
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const sha = context.sha;
|
||||||
|
const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
commit_sha: sha,
|
||||||
|
});
|
||||||
|
const merged = prs.find(p => p.merged_at);
|
||||||
|
if (merged) {
|
||||||
|
core.info(`OK: commit ${sha.substring(0,7)} came from PR #${merged.number}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const pusher = context.payload.pusher?.name || context.actor;
|
||||||
|
const msg = context.payload.head_commit?.message?.split('\n')[0] || '(no message)';
|
||||||
|
const body = [
|
||||||
|
`**Direct push to \`main\` detected** — commit was not associated with a merged PR.`,
|
||||||
|
``,
|
||||||
|
`- Commit: \`${sha.substring(0,7)}\` — ${msg}`,
|
||||||
|
`- Pusher: @${pusher}`,
|
||||||
|
`- Workflow run: ${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
|
||||||
|
``,
|
||||||
|
`The project policy is that all changes to \`main\` go through a pull request so they can be reviewed before deploying to production. If this push was intentional (hotfix, infra change) close this issue with a comment explaining why.`,
|
||||||
|
``,
|
||||||
|
`_This issue was opened automatically by \`.github/workflows/main-push-audit.yml\`._`,
|
||||||
|
].join('\n');
|
||||||
|
await github.rest.issues.create({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
title: `Direct push to main: ${sha.substring(0,7)}`,
|
||||||
|
body,
|
||||||
|
labels: ['policy-violation', 'audit'],
|
||||||
|
});
|
||||||
|
core.warning(`Opened audit issue for direct push ${sha.substring(0,7)}`);
|
||||||
Reference in New Issue
Block a user