From 9837442334d3124dcc314a022ef29d4e76385e62 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Mon, 23 Mar 2026 23:36:37 -0700 Subject: [PATCH] feat: display CI build status on worktree cards Show CI check status icons (success/failure/pending) next to the branch name on worktree cards. Return 'neutral' instead of 'pending' when no CI checks exist to avoid showing a misleading spinner. Fix lint errors in client.ts (curly braces, array-type). Co-Authored-By: Claude Opus 4.6 --- src/main/github/client.ts | 34 +++++++++++++------ .../src/components/sidebar/WorktreeCard.tsx | 22 ++++++++++-- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/main/github/client.ts b/src/main/github/client.ts index c204fe4005d..f696ba8ec18 100644 --- a/src/main/github/client.ts +++ b/src/main/github/client.ts @@ -7,7 +7,7 @@ const execFileAsync = promisify(execFile) // Concurrency limiter - max 4 parallel gh processes const MAX_CONCURRENT = 4 let running = 0 -const queue: Array<() => void> = [] +const queue: (() => void)[] = [] function acquire(): Promise { if (running < MAX_CONCURRENT) { @@ -25,7 +25,9 @@ function acquire(): Promise { function release(): void { running-- const next = queue.shift() - if (next) next() + if (next) { + next() + } } /** @@ -104,13 +106,13 @@ export async function listIssues(repoPath: string, limit = 20): Promise - }> + labels: { name: string }[] + }[] return data.map((d) => ({ number: d.number, title: d.title, @@ -127,19 +129,25 @@ export async function listIssues(repoPath: string, limit = 20): Promise) { + for (const check of rollup as { status?: string; conclusion?: string; state?: string }[]) { const conclusion = check.conclusion?.toUpperCase() const status = check.status?.toUpperCase() const state = check.state?.toUpperCase() @@ -162,7 +170,11 @@ function deriveCheckStatus(rollup: unknown[] | null | undefined): CheckStatus { } } - if (hasFailure) return 'failure' - if (hasPending) return 'pending' + if (hasFailure) { + return 'failure' + } + if (hasPending) { + return 'pending' + } return 'success' } diff --git a/src/renderer/src/components/sidebar/WorktreeCard.tsx b/src/renderer/src/components/sidebar/WorktreeCard.tsx index 06aceba32e8..cc13a7f595d 100644 --- a/src/renderer/src/components/sidebar/WorktreeCard.tsx +++ b/src/renderer/src/components/sidebar/WorktreeCard.tsx @@ -3,7 +3,7 @@ import { useAppStore } from '@/store' import { Badge } from '@/components/ui/badge' import { HoverCard, HoverCardTrigger, HoverCardContent } from '@/components/ui/hover-card' import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip' -import { Bell, LoaderCircle, CircleDot } from 'lucide-react' +import { Bell, LoaderCircle, CircleDot, CircleCheck, CircleX } from 'lucide-react' import RepoDotLabel from '@/components/repo/RepoDotLabel' import StatusIndicator from './StatusIndicator' import WorktreeContextMenu from './WorktreeContextMenu' @@ -218,7 +218,7 @@ const WorktreeCard = React.memo(function WorktreeCard({ {worktree.displayName} - {/* Line 2: Repo badge + branch + primary badge */} + {/* Line 2: Repo badge + branch + primary badge + CI status */}
{repo && ( )} + {pr && pr.checksStatus !== 'neutral' && ( + + + + {pr.checksStatus === 'success' && ( + + )} + {pr.checksStatus === 'failure' && } + {pr.checksStatus === 'pending' && ( + + )} + + + + CI checks {checksLabel(pr.checksStatus).toLowerCase()} + + + )}
{/* Meta section: Issue, Comment, PR */}