Skip to content

Commit b910620

Browse files
committed
bugfix-314-error-merging-release-branch-after-successful-deployment: Enhance MergeRepository to handle fallback to status checks when no PR-specific check runs are available after polling. Added test case to verify that the merge process correctly waits for pending status checks before proceeding. This improves the robustness of the merging logic and ensures proper handling of check statuses.
1 parent 3959fd1 commit b910620

2 files changed

Lines changed: 65 additions & 4 deletions

File tree

src/data/repository/__tests__/merge_repository.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,49 @@ describe('MergeRepository', () => {
293293
expect(mockPullsMerge).toHaveBeenCalled();
294294
});
295295

296+
it('when no check runs for this PR after max polls but status checks are pending, falls back to status checks and waits then merges', async () => {
297+
jest.useFakeTimers();
298+
mockPullsCreate.mockResolvedValue({ data: { number: 99 } });
299+
mockPullsListCommits.mockResolvedValue({ data: [] });
300+
mockPullsUpdate.mockResolvedValue({});
301+
mockPullsMerge.mockResolvedValue({});
302+
// Check runs on ref are for another PR only; this PR (99) has none.
303+
mockChecksListForRef.mockResolvedValue({
304+
data: {
305+
check_runs: [
306+
{ name: 'ci', status: 'completed', conclusion: 'success', pull_requests: [{ number: 1 }] },
307+
],
308+
},
309+
});
310+
// First 4 polls: pending status check; 5th: completed so we proceed to merge.
311+
mockReposGetCombinedStatusForRef
312+
.mockResolvedValueOnce({
313+
data: { state: 'pending', statuses: [{ context: 'ci', state: 'pending' }] },
314+
})
315+
.mockResolvedValueOnce({
316+
data: { state: 'pending', statuses: [{ context: 'ci', state: 'pending' }] },
317+
})
318+
.mockResolvedValueOnce({
319+
data: { state: 'pending', statuses: [{ context: 'ci', state: 'pending' }] },
320+
})
321+
.mockResolvedValueOnce({
322+
data: { state: 'pending', statuses: [{ context: 'ci', state: 'pending' }] },
323+
})
324+
.mockResolvedValue({
325+
data: { state: 'success', statuses: [{ context: 'ci', state: 'success' }] },
326+
});
327+
328+
const promise = repo.mergeBranch('o', 'r', 'release/1.0', 'develop', 60, 'token');
329+
await jest.runAllTimersAsync();
330+
const result = await promise;
331+
332+
jest.useRealTimers();
333+
expect(result).toHaveLength(1);
334+
expect(result[0].success).toBe(true);
335+
expect(mockReposGetCombinedStatusForRef).toHaveBeenCalled();
336+
expect(mockPullsMerge).toHaveBeenCalled();
337+
});
338+
296339
it('when timeout > 10 and checks never complete throws then direct merge succeeds', async () => {
297340
jest.useFakeTimers();
298341
mockPullsCreate.mockResolvedValue({ data: { number: 1 } });

src/data/repository/merge_repository.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,28 @@ This PR merges **${head}** into **${base}**.
133133
// haven't registered yet, or this PR/base has no required checks.
134134
waitForPrChecksAttempts++;
135135
if (waitForPrChecksAttempts >= maxWaitForPrChecksAttempts) {
136-
checksCompleted = true;
137-
logDebugInfo(
138-
`No check runs for this PR after ${maxWaitForPrChecksAttempts} polls; proceeding to merge (branch may have no required checks).`,
139-
);
136+
// Give up waiting for PR-specific check runs; fall back to status checks
137+
// before proceeding to merge (PR may have required status checks).
138+
const pendingChecksFallback = commitStatus.statuses.filter(status => {
139+
logDebugInfo(`Status check (fallback): ${status.context} (State: ${status.state})`);
140+
return status.state === 'pending';
141+
});
142+
143+
if (pendingChecksFallback.length === 0) {
144+
checksCompleted = true;
145+
logDebugInfo(
146+
`No check runs for this PR after ${maxWaitForPrChecksAttempts} polls; no pending status checks; proceeding to merge.`,
147+
);
148+
} else {
149+
logDebugInfo(
150+
`No check runs for this PR after ${maxWaitForPrChecksAttempts} polls; falling back to status checks. Waiting for ${pendingChecksFallback.length} status checks to complete.`,
151+
);
152+
pendingChecksFallback.forEach(check => {
153+
logDebugInfo(` - ${check.context} (State: ${check.state})`);
154+
});
155+
await new Promise(resolve => setTimeout(resolve, iteration * 1000));
156+
attempts++;
157+
}
140158
} else {
141159
logDebugInfo('Check runs exist on ref but none for this PR yet; waiting for workflows to register.');
142160
await new Promise(resolve => setTimeout(resolve, iteration * 1000));

0 commit comments

Comments
 (0)