Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 6 additions & 44 deletions .github/workflows/auto-merge.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
name: Auto-Merge on Codex + Human LGTM
name: Auto-Merge on Human LGTM

# Triggers when a PR review is submitted.
# Merges if ALL of the following are true:
# 1. PR has the auto-fix label (from OpenClaw)
# 2. commit status codex/lgtm = success (set by codex-gate.yml)
# 3. At least one human collaborator (not FuugaMo/Codex) has APPROVED
# 2. At least one human collaborator has APPROVED

on:
pull_request_review:
types: [submitted]
status:
# Re-check when the codex/lgtm status changes
# (handles the case where human approved before Codex finished)

permissions:
contents: write
pull-requests: write
statuses: read

jobs:
merge-check:
Expand All @@ -26,60 +21,27 @@ jobs:
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const CODEX_USER = 'FuugaMo';

// ── Find target PR ─────────────────────────────────────────────────
let pr;
if (context.eventName === 'pull_request_review') {
pr = context.payload.pull_request;
} else {
// status event — find open auto-fix PRs for this commit
const sha = context.payload.sha;
const { data: prs } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
});
pr = prs.find(p =>
p.head.sha === sha &&
p.labels.some(l => l.name === 'auto-fix')
);
if (!pr) return;
}
const pr = context.payload.pull_request;

const labels = (pr.labels || []).map(l => l.name);
if (!labels.includes('auto-fix')) return;
if (pr.draft) return;

// ── Check codex/lgtm commit status ────────────────────────────────
const { data: statuses } = await github.rest.repos.listCommitStatusesForRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: pr.head.sha,
});

const codexStatus = statuses.find(s => s.context === 'codex/lgtm');
if (!codexStatus || codexStatus.state !== 'success') {
console.log('codex/lgtm not yet success — skipping merge');
return;
}

// ── Check human approval ───────────────────────────────────────────
const { data: reviews } = await github.rest.pulls.listReviews({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
});

// Get the latest review state per reviewer
const latest = new Map();
for (const r of reviews) {
if (r.state === 'COMMENTED') continue;
latest.set(r.user.login, r.state);
}

const humanApproved = [...latest.entries()].some(
([user, state]) => user !== CODEX_USER && state === 'APPROVED'
([, state]) => state === 'APPROVED'
Comment on lines 43 to +44
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require non-automated approval before auto-merge

This predicate now treats any latest APPROVED review as "human approval", so an automated reviewer can satisfy the gate and merge an auto-fix PR with no human reviewer involved. In this repo, .github/workflows/codex-gate.yml explicitly handles automated Codex pull_request_review events, so this path is realistic: once that bot submits APPROVED, this workflow will merge immediately. Please restore an explicit reviewer-identity filter (or bot check) so only real human approvals count.

Useful? React with 👍 / 👎.

);

if (!humanApproved) {
Expand All @@ -88,7 +50,7 @@ jobs:
}

// ── All conditions met — merge ─────────────────────────────────────
console.log(`Merging PR #${pr.number}: Codex LGTM ✓ + human approval ✓`);
console.log(`Merging PR #${pr.number}: human approval ✓`);

await github.rest.pulls.merge({
owner: context.repo.owner,
Expand All @@ -99,6 +61,6 @@ jobs:
commit_message: [
pr.body || '',
'',
`Auto-merged: Codex LGTM + human approval`,
`Auto-merged: human approval`,
].join('\n').trim(),
});
Loading