fix(integrations): stop treating any "already" failure as a successful issue/MR close or reopen (#28777)

GitHub issue close/reopen swallowed every gh failure mentioning "already"
(e.g. "API rate limit already exceeded"), and GitLab MR close/reopen did the
same for glab. gh exits 0 when the issue is already in the target state, so
every gh failure is now reported. For glab, only its fixed no-op refusals
("this merge request has been closed" / "this merge request is already open")
count as success; "has already been merged" and all other errors surface.

Fixes #28766

Co-authored-by: Neil <neil@stably.ai>
This commit is contained in:
Žan Mlakar
2026-10-27 17:42:24 -07:00
committed by GitHub
co-authored by Neil
parent 07ea055d4e
commit 7bcaee270d
4 changed files with 39 additions and 7 deletions
+2 -4
View File
@@ -56,11 +56,9 @@ export async function updateIssue(
await ghExecFileAsync(['issue', 'reopen', String(issueNumber), '--repo', repo], ghOptions)
}
} catch (err) {
// Why: gh exits 0 when the issue is already in the target state, so any failure is real.
const stderr = err instanceof Error ? err.message : String(err)
// Repeated state transitions are idempotent.
if (!stderr.toLowerCase().includes('already')) {
errors.push(classifyGhError(stderr).message)
}
errors.push(classifyGhError(stderr).message)
} finally {
release()
}
+12
View File
@@ -537,4 +537,16 @@ describe('issue source operations', () => {
{ cwd: '/repo-root', host: 'github.com' }
)
})
it('reports a failed close whose gh error mentions "already"', async () => {
getIssueOwnerRepoMock.mockResolvedValueOnce({ owner: 'stablyai', repo: 'orca' })
ghExecFileAsyncMock.mockRejectedValueOnce(
new Error('GraphQL: API rate limit already exceeded for user ID 1.')
)
await expect(updateIssue('/repo-root', 924, { state: 'closed' })).resolves.toEqual({
ok: false,
error: 'GitHub rate limit hit. Try again in a few minutes.'
})
})
})