mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-11-14 06:46:56 +00:00
fix(ci): skip unsupported stacked PRs in nightly merge (#10063)
Signed-off-by: WenyXu <wenymedia@gmail.com>
This commit is contained in:
@@ -13,6 +13,10 @@ GitHub may delay scheduled runs.
|
||||
The label alone controls nightly opt-in; the enqueue job does not check the PR
|
||||
body. PRs must be approved and clean, and have all
|
||||
review threads resolved. Drafts are skipped.
|
||||
If GitHub rejects enqueueing because a PR belongs to a stack, the workflow skips
|
||||
it and continues without failing the run or counting it toward the five-PR limit.
|
||||
Other API errors still fail the run. Dry runs do not call the enqueue API, so
|
||||
they cannot detect this stack rejection.
|
||||
Already queued PRs and manually enabled auto-merge requests are left alone;
|
||||
Maintainers can use **Merge when ready** to proceed earlier. Unready PRs wait for a later
|
||||
scheduled run; this workflow never enables auto-merge on their behalf.
|
||||
|
||||
@@ -26,7 +26,7 @@ const checked = '- [x] Merge during the nightly window <!-- nightly-merge -->';
|
||||
|
||||
async function run({ sync = false, body = checked, labels = ['merge nightly'],
|
||||
detail = {}, current = {}, threads = [[true]], count = 1, dryRun = false,
|
||||
failFirst = false } = {}) {
|
||||
failFirst = false, enqueueError = Error('head changed') } = {}) {
|
||||
const calls = [];
|
||||
let page = 0;
|
||||
const github = {
|
||||
@@ -43,7 +43,7 @@ async function run({ sync = false, body = checked, labels = ['merge nightly'],
|
||||
graphql: async (query, args) => {
|
||||
if (query.includes('mutation')) {
|
||||
calls.push(['enqueue', args]);
|
||||
if (failFirst && calls.filter(call => call[0] === 'enqueue').length === 1) throw Error('head changed');
|
||||
if (failFirst && calls.filter(call => call[0] === 'enqueue').length === 1) throw enqueueError;
|
||||
return {};
|
||||
}
|
||||
if (query.includes('reviewThreads')) {
|
||||
@@ -123,3 +123,31 @@ test('label-only opt-in survives PRs without a template and does not require a c
|
||||
// The label is authoritative for admission, even before checkbox synchronization.
|
||||
assert.equal((await run({ body: checked.replace('[x]', '[ ]') })).some(call => call[0] === 'enqueue'), true);
|
||||
});
|
||||
|
||||
const stackMessage = 'This pull request is part of a stack and must be enqueued using the asynchronous merge REST API. For more information, see https://docs.github.com/rest/pulls/pulls#merge-a-pull-request-asynchronously';
|
||||
|
||||
test('stacked PR rejection is skipped without consuming the successful enqueue cap', async () => {
|
||||
const calls = await run({ count: 7, failFirst: true,
|
||||
enqueueError: Object.assign(Error('GraphQL request failed'), { errors: [{ message: stackMessage }] }) });
|
||||
assert.equal(calls.some(c => c[0] === 'failed'), false);
|
||||
assert.deepEqual(calls.find(c => c[0] === 'summary')[1], [
|
||||
['PR', 'Result'], ['1', 'Skipped: stacked PR'],
|
||||
...[2, 3, 4, 5, 6].map(number => [String(number), 'Enqueued'])
|
||||
]);
|
||||
assert.deepEqual(calls.filter(c => c[0] === 'enqueue').map(c => c[1].id),
|
||||
['PR_1', 'PR_2', 'PR_3', 'PR_4', 'PR_5', 'PR_6']);
|
||||
});
|
||||
|
||||
test('mixed or unstructured errors are not mistaken for a stacked PR rejection', async () => {
|
||||
for (const enqueueError of [
|
||||
Error(stackMessage),
|
||||
Object.assign(Error('GraphQL request failed'), { errors: [] }),
|
||||
Object.assign(Error('GraphQL request failed'), { errors: [{ message: stackMessage }, { message: 'Forbidden' }] })
|
||||
]) {
|
||||
const calls = await run({ count: 2, failFirst: true, enqueueError });
|
||||
assert.equal(calls.filter(c => c[0] === 'failed').length, 1);
|
||||
assert.deepEqual(calls.find(c => c[0] === 'summary')[1], [
|
||||
['PR', 'Result'], ['1', 'Failed: see workflow logs'], ['2', 'Enqueued']
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -122,12 +122,23 @@ jobs:
|
||||
}
|
||||
if (!dryRun) {
|
||||
// GitHub enforces branch rules; never bypass or enable auto-merge.
|
||||
await github.graphql(`
|
||||
try {
|
||||
await github.graphql(`
|
||||
mutation($id: ID!, $sha: GitObjectID!) {
|
||||
enqueuePullRequest(input: {pullRequestId: $id, expectedHeadOid: $sha}) {
|
||||
mergeQueueEntry { id }
|
||||
}
|
||||
}`, { id: pr.id, sha: pr.headRefOid });
|
||||
} catch (error) {
|
||||
// Stacked PRs require a different API and are not handled by this workflow.
|
||||
const stackError = 'This pull request is part of a stack and must be enqueued using the asynchronous merge REST API.';
|
||||
if (error.errors?.length && error.errors.every(item => item.message?.startsWith(stackError))) {
|
||||
core.warning(`PR #${number}: skipped stacked PR`);
|
||||
rows.push([String(number), 'Skipped: stacked PR']);
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
enqueued++;
|
||||
rows.push([String(number), dryRun ? 'Would enqueue' : 'Enqueued']);
|
||||
|
||||
Reference in New Issue
Block a user