diff --git a/src/main/github/client.ts b/src/main/github/client.ts index 6717cf5451f..a86d2e56121 100644 --- a/src/main/github/client.ts +++ b/src/main/github/client.ts @@ -1158,43 +1158,51 @@ export async function getPRChecks( ): Promise { const ownerRepo = headSha ? await getOwnerRepo(repoPath) : null if (ownerRepo && headSha) { - await assertRateLimitBudget('core') - await acquire() + let canUseRestChecks = true try { - // Why: --cache 60s saves rate-limit budget during polling, but when the - // user explicitly clicks refresh we must skip it so gh fetches fresh data. - const cacheArgs = options?.noCache ? [] : ['--cache', '60s'] - const { stdout } = await ghExecFileAsync( - [ - 'api', - ...cacheArgs, - `repos/${ownerRepo.owner}/${ownerRepo.repo}/commits/${encodeURIComponent(headSha)}/check-runs?per_page=100` - ], - { cwd: repoPath } - ) - noteRateLimitSpend('core') - const data = JSON.parse(stdout) as { - check_runs: { - name: string - status: string - conclusion: string | null - html_url: string - details_url: string | null - }[] - } - return data.check_runs.map((d) => ({ - name: d.name, - status: mapCheckRunRESTStatus(d.status), - conclusion: mapCheckRunRESTConclusion(d.status, d.conclusion), - url: d.details_url || d.html_url || null - })) + await assertRateLimitBudget('core') } catch (err) { - // Why: a PR can outlive the cached head SHA after force-pushes or remote - // rewrites. Falling back to `gh pr checks` keeps the panel populated - // instead of rendering a false "no checks" state from a stale commit. - console.warn('getPRChecks via head SHA failed, falling back to gh pr checks:', err) - } finally { - release() + canUseRestChecks = false + console.warn('getPRChecks skipped REST check-runs, falling back to gh pr checks:', err) + } + if (canUseRestChecks) { + await acquire() + try { + // Why: --cache 60s saves rate-limit budget during polling, but when the + // user explicitly clicks refresh we must skip it so gh fetches fresh data. + const cacheArgs = options?.noCache ? [] : ['--cache', '60s'] + const { stdout } = await ghExecFileAsync( + [ + 'api', + ...cacheArgs, + `repos/${ownerRepo.owner}/${ownerRepo.repo}/commits/${encodeURIComponent(headSha)}/check-runs?per_page=100` + ], + { cwd: repoPath } + ) + noteRateLimitSpend('core') + const data = JSON.parse(stdout) as { + check_runs: { + name: string + status: string + conclusion: string | null + html_url: string + details_url: string | null + }[] + } + return data.check_runs.map((d) => ({ + name: d.name, + status: mapCheckRunRESTStatus(d.status), + conclusion: mapCheckRunRESTConclusion(d.status, d.conclusion), + url: d.details_url || d.html_url || null + })) + } catch (err) { + // Why: a PR can outlive the cached head SHA after force-pushes or remote + // rewrites. Falling back to `gh pr checks` keeps the panel populated + // instead of rendering a false "no checks" state from a stale commit. + console.warn('getPRChecks via head SHA failed, falling back to gh pr checks:', err) + } finally { + release() + } } } diff --git a/src/renderer/src/components/right-sidebar/ChecksPanel.tsx b/src/renderer/src/components/right-sidebar/ChecksPanel.tsx index 91bdde3c32e..c9078de718f 100644 --- a/src/renderer/src/components/right-sidebar/ChecksPanel.tsx +++ b/src/renderer/src/components/right-sidebar/ChecksPanel.tsx @@ -544,6 +544,7 @@ export default function ChecksPanel(): React.JSX.Element { const isQueuedPRRefresh = prRefreshState?.status === 'queued' const isInFlightPRRefresh = prRefreshState?.status === 'in-flight' const isPausedPRRefresh = prRefreshState?.status === 'paused' + const isErroredPRRefresh = prRefreshState?.status === 'error' return (
@@ -552,24 +553,28 @@ export default function ChecksPanel(): React.JSX.Element { ? `${operationLabel} in progress` : isRemoteRepo ? 'Checks unavailable' - : isQueuedPRRefresh - ? 'Checking for pull request' - : isInFlightPRRefresh + : isErroredPRRefresh + ? 'Could not refresh pull request' + : isQueuedPRRefresh ? 'Checking for pull request' - : 'No pull request found'} + : isInFlightPRRefresh + ? 'Checking for pull request' + : 'No pull request found'}
{operationInProgress ? 'PR checks will be available after the operation completes' : isRemoteRepo ? 'PR refresh is not supported for remote worktrees yet' - : isQueuedPRRefresh - ? 'Waiting to refresh GitHub status for this branch' - : isInFlightPRRefresh - ? 'Refreshing GitHub status for this branch' - : isPausedPRRefresh - ? 'GitHub refresh is paused by the current rate-limit budget' - : 'Push your branch and open a PR to see checks here'} + : isErroredPRRefresh + ? 'GitHub status could not be refreshed. Existing cached data was preserved.' + : isQueuedPRRefresh + ? 'Waiting to refresh GitHub status for this branch' + : isInFlightPRRefresh + ? 'Refreshing GitHub status for this branch' + : isPausedPRRefresh + ? 'GitHub refresh is paused by the current rate-limit budget' + : 'Push your branch and open a PR to see checks here'}
{!operationInProgress && !isRemoteRepo && (