Unverified 提交 895487b0 authored 作者: wwwillchen-bot's avatar wwwillchen-bot 提交者: GitHub

chore: remove merge-pr workflow (#2930)

## Summary - Removes the `merge-pr.yml` GitHub Actions workflow that auto-merged PRs from allowed authors when CI passed ## Test plan - Verify no other workflows depend on `merge-pr.yml` - Confirm CI still runs normally without this workflow 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2930" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end --> Co-authored-by: 's avatarWill Chen <willchen90@gmail.com> Co-authored-by: 's avatarClaude <noreply@anthropic.com>
上级 51fc07e9
name: Merge PR when ready
on:
workflow_run:
workflows: ["CI"]
types:
- completed
env:
# Allowed authors for auto-merge (update in one place)
ALLOWED_MERGE_AUTHORS: wwwillchen,wwwillchen-bot,dyadbot
jobs:
merge:
# Only run if:
# 1. The CI workflow succeeded
# 2. The trigger was a pull request (not a push to main)
if: >
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
actions: read
steps:
- name: Check PR and merge if ready
uses: actions/github-script@v7
env:
ALLOWED_AUTHORS: ${{ env.ALLOWED_MERGE_AUTHORS }}
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const headSha = context.payload.workflow_run.head_sha;
const ciCheckSuiteId = context.payload.workflow_run.check_suite_id;
console.log(`Checking PR for head SHA: ${headSha}`);
// Use pull_requests from workflow_run event payload directly
// This avoids the 100 PR limit issue when listing all open PRs
const workflowRunPRs = context.payload.workflow_run.pull_requests;
let pr;
if (workflowRunPRs && workflowRunPRs.length > 0) {
if (workflowRunPRs.length > 1) {
console.log(`Multiple PRs (${workflowRunPRs.length}) associated with workflow run — refusing to merge to avoid ambiguity`);
return;
}
const prNumber = workflowRunPRs[0].number;
const { data: prData } = await github.rest.pulls.get({
owner,
repo,
pull_number: prNumber,
});
pr = prData.state === 'open' ? prData : undefined;
} else {
// Fallback: use listPullRequestsAssociatedWithCommit for targeted lookup
// Note: workflow_run.pull_requests is empty for fork PRs
const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner,
repo,
commit_sha: headSha,
});
const headBranch = context.payload.workflow_run.head_branch;
const openPrs = prs.filter(p => p.state === 'open' && p.head.ref === headBranch);
if (openPrs.length > 1) {
console.log(`Multiple open PRs found for SHA ${headSha} on branch ${headBranch} — refusing to merge to avoid ambiguity`);
return;
}
pr = openPrs[0];
}
if (!pr) {
console.log('No open PR found for this SHA');
return;
}
console.log(`Found PR #${pr.number}: ${pr.title}`);
console.log(`Author: ${pr.user.login}`);
// Check if author is allowed (read from environment variable)
const allowedAuthors = (process.env.ALLOWED_AUTHORS || '').split(',').map(a => a.trim()).filter(Boolean);
if (!allowedAuthors.includes(pr.user.login)) {
console.log(`Author ${pr.user.login} is not in allowed list: ${allowedAuthors.join(', ')}`);
return;
}
// Check if PR has the "merge-when-ready" label
const labels = pr.labels.map(l => l.name);
console.log(`PR labels: ${labels.join(', ') || '(none)'}`);
if (!labels.includes('merge-when-ready')) {
console.log('PR does not have "merge-when-ready" label');
return;
}
// Verify all check suites have passed
const allCheckSuites = await github.paginate(
github.rest.checks.listSuitesForRef,
{ owner, repo, ref: headSha, per_page: 100 },
(response) => response.data
);
const pendingOrFailed = allCheckSuites.filter(suite => {
// Skip neutral conclusions (e.g., skipped workflows)
if (suite.conclusion === 'neutral' || suite.conclusion === 'skipped' || suite.conclusion === 'cancelled') {
console.log(`Skipping suite with conclusion '${suite.conclusion}': ${suite.app?.name || 'Unknown'}`);
return false;
}
// Exclude the CI workflow's check suite (already completed successfully — it triggered this workflow)
if (suite.id === ciCheckSuiteId) {
console.log(`Skipping CI check suite (id=${suite.id})`);
return false;
}
// Skip check suites that are in-progress (conclusion is null)
// These are likely workflow_run-triggered workflows that started after CI
if (suite.status === 'in_progress' || suite.status === 'queued') {
// Check if this is a workflow_run triggered suite by comparing timestamps
const ciCompletedAt = new Date(context.payload.workflow_run.updated_at);
const suiteCreatedAt = new Date(suite.created_at);
if (suiteCreatedAt >= ciCompletedAt) {
console.log(`Skipping workflow_run-triggered suite: ${suite.app?.name || 'Unknown'} (created after CI completed)`);
return false;
}
}
// Check if still in progress or failed
return suite.status !== 'completed' || suite.conclusion !== 'success';
});
// Guard: require at least one completed successful check suite
const successfulSuites = allCheckSuites.filter(
suite => suite.status === 'completed' && suite.conclusion === 'success'
);
if (successfulSuites.length === 0) {
console.log('No completed successful check suites found — refusing to merge');
return;
}
if (pendingOrFailed.length > 0) {
console.log('Some check suites are still pending or have failed:');
pendingOrFailed.forEach(suite => {
console.log(` - ${suite.app?.name || 'Unknown'}: status=${suite.status}, conclusion=${suite.conclusion}`);
});
return;
}
console.log('All checks passed! Merging PR...');
// Merge the PR via squash, using sha parameter to prevent TOCTOU race condition
try {
await github.rest.pulls.merge({
owner,
repo,
pull_number: pr.number,
merge_method: 'squash',
sha: headSha,
});
console.log(`Successfully merged PR #${pr.number}`);
} catch (error) {
console.log(`Failed to merge PR: ${error.message}`);
throw error;
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论