From 4ca248383b3d92f6ceed3b481bcb25619de179b9 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Thu, 18 Jun 2026 08:51:08 -0700 Subject: [PATCH] Skip GitHub CLI preflight checks if no GitHub projects exist --- src/renderer/src/components/Landing.tsx | 86 ++++---------- .../landing-preflight-issues.test.ts | 106 ++++++++++++++++++ .../components/landing-preflight-issues.ts | 82 ++++++++++++++ 3 files changed, 208 insertions(+), 66 deletions(-) create mode 100644 src/renderer/src/components/landing-preflight-issues.test.ts create mode 100644 src/renderer/src/components/landing-preflight-issues.ts diff --git a/src/renderer/src/components/Landing.tsx b/src/renderer/src/components/Landing.tsx index dc5f6af8c..05d609f6d 100644 --- a/src/renderer/src/components/Landing.tsx +++ b/src/renderer/src/components/Landing.tsx @@ -14,6 +14,11 @@ import { useShortcutKeyDetails, type ShortcutKeyComboDetails } from '@/hooks/use import { useMountedRef } from '@/hooks/useMountedRef' import logo from '../../../../resources/logo.svg' import { translate } from '@/i18n/i18n' +import { + getLandingPreflightIssues, + hasGitHubBackedProject, + type PreflightIssue +} from './landing-preflight-issues' type ShortcutItem = { id: string @@ -21,65 +26,6 @@ type ShortcutItem = { action: string } -type PreflightIssue = { - id: string - title: string - description: string - fixLabel: string - fixUrl: string - /** Git is a hard global dependency and stays pinned; provider-specific CLI - * setup (gh) is a soft nudge the user can dismiss. */ - dismissible?: boolean -} - -function getPreflightIssues(status: { - git: { installed: boolean } - gh: { installed: boolean; authenticated: boolean } -}): PreflightIssue[] { - const issues: PreflightIssue[] = [] - - if (!status.git.installed) { - issues.push({ - id: 'git', - title: translate('auto.components.Landing.e5b7296d9d', 'Git is not installed'), - description: translate( - 'auto.components.Landing.b673e7cf1b', - 'Git is required for Git projects, source control, and workspace management.' - ), - fixLabel: 'Install Git', - fixUrl: 'https://git-scm.com/downloads' - }) - } - - if (!status.gh.installed) { - issues.push({ - id: 'gh', - title: translate('auto.components.Landing.5beaef5f9e', 'GitHub CLI is not installed'), - description: translate( - 'auto.components.Landing.73e1ad4282', - 'Orca uses the GitHub CLI (gh) to show pull requests, issues, and checks.' - ), - fixLabel: 'Install GitHub CLI', - fixUrl: 'https://cli.github.com', - dismissible: true - }) - } else if (!status.gh.authenticated) { - issues.push({ - id: 'gh-auth', - title: translate('auto.components.Landing.9f96d018b7', 'GitHub CLI is not authenticated'), - description: translate( - 'auto.components.Landing.00cee697c1', - 'Run "gh auth login" in a terminal to connect your GitHub account.' - ), - fixLabel: 'Learn more', - fixUrl: 'https://cli.github.com/manual/gh_auth_login', - dismissible: true - }) - } - - return issues -} - const ORCA_STARGAZERS_URL = 'https://github.com/stablyai/orca/stargazers' type StarState = 'loading' | 'starred' | 'not-starred' | 'web-fallback' | 'hidden' @@ -288,6 +234,8 @@ export default function Landing(): React.JSX.Element { const createTargetLabel = repos.length > 0 && repos.every((repo) => isGitRepoKind(repo)) ? 'Worktree' : 'Workspace' const canCreateWorktree = repos.length > 0 + const hasGitHubProject = useMemo(() => hasGitHubBackedProject(repos), [repos]) + const showGitHubSupportFooter = repos.length === 0 || hasGitHubProject const [preflightIssues, setPreflightIssues] = useState([]) @@ -298,7 +246,9 @@ export default function Landing(): React.JSX.Element { if (cancelled) { return } - setPreflightIssues(getPreflightIssues(status)) + setPreflightIssues( + getLandingPreflightIssues(status, { hasGitHubBackedProject: hasGitHubProject }) + ) }) } @@ -321,7 +271,7 @@ export default function Landing(): React.JSX.Element { document.removeEventListener('visibilitychange', handleWindowActive) window.removeEventListener('focus', handleWindowActive) } - }, []) + }, [hasGitHubProject]) useEffect(() => { if (preflightIssues.length === 0) { @@ -336,7 +286,9 @@ export default function Landing(): React.JSX.Element { if (cancelled) { return } - setPreflightIssues(getPreflightIssues(status)) + setPreflightIssues( + getLandingPreflightIssues(status, { hasGitHubBackedProject: hasGitHubProject }) + ) }) }, 30000) @@ -344,7 +296,7 @@ export default function Landing(): React.JSX.Element { cancelled = true window.clearInterval(intervalId) } - }, [preflightIssues.length]) + }, [hasGitHubProject, preflightIssues.length]) const createWorktreeShortcut = useShortcutKeyDetails('workspace.create') const previousWorktreeShortcut = useShortcutKeyDetails('worktree.navigateUp') @@ -430,9 +382,11 @@ export default function Landing(): React.JSX.Element { -
- 0} /> -
+ {showGitHubSupportFooter && ( +
+ 0} /> +
+ )} ) } diff --git a/src/renderer/src/components/landing-preflight-issues.test.ts b/src/renderer/src/components/landing-preflight-issues.test.ts new file mode 100644 index 000000000..99c315088 --- /dev/null +++ b/src/renderer/src/components/landing-preflight-issues.test.ts @@ -0,0 +1,106 @@ +import { describe, expect, it } from 'vitest' +import { + getLandingPreflightIssues, + hasGitHubBackedProject, + type LandingPreflightStatus +} from './landing-preflight-issues' +import type { Repo } from '../../../shared/types' + +function repo(overrides: Partial & Pick): Repo { + return { + badgeColor: '#737373', + addedAt: 100, + kind: 'git', + ...overrides + } +} + +const missingGhStatus: LandingPreflightStatus = { + git: { installed: true }, + gh: { installed: false, authenticated: false } +} + +describe('landing preflight issues', () => { + it('keeps Git issues even when no GitHub-backed project is registered', () => { + const issues = getLandingPreflightIssues( + { + git: { installed: false }, + gh: { installed: false, authenticated: false } + }, + { hasGitHubBackedProject: false } + ) + + expect(issues.map((issue) => issue.id)).toEqual(['git']) + }) + + it('does not report GitHub CLI issues when no GitHub-backed project is registered', () => { + const issues = getLandingPreflightIssues(missingGhStatus, { + hasGitHubBackedProject: false + }) + + expect(issues.some((issue) => issue.id.startsWith('gh'))).toBe(false) + }) + + it('keeps GitHub CLI issues when a GitHub-backed project is registered', () => { + const issues = getLandingPreflightIssues(missingGhStatus, { + hasGitHubBackedProject: true + }) + + expect(issues.map((issue) => issue.id)).toContain('gh') + }) + + it('reports GitHub auth issue when gh is installed but unauthenticated', () => { + const issues = getLandingPreflightIssues( + { + git: { installed: true }, + gh: { installed: true, authenticated: false } + }, + { hasGitHubBackedProject: true } + ) + + expect(issues.map((issue) => issue.id)).toContain('gh-auth') + }) + + it('treats GitLab-only registered projects as not GitHub-backed', () => { + expect( + hasGitHubBackedProject([ + repo({ + id: 'gitlab-repo', + path: '/Users/alice/gitlab', + displayName: 'gitlab' + }) + ]) + ).toBe(false) + }) + + it('detects GitHub-backed projects from generated avatar metadata', () => { + expect( + hasGitHubBackedProject([ + repo({ + id: 'github-repo', + path: '/Users/alice/orca', + displayName: 'orca', + repoIcon: { + type: 'image', + src: 'https://github.com/stablyai.png?size=64', + source: 'github', + label: 'stablyai/orca' + } + }) + ]) + ).toBe(true) + }) + + it('detects GitHub-backed projects from existing provider metadata', () => { + expect( + hasGitHubBackedProject([ + repo({ + id: 'github-repo', + path: '/Users/alice/orca', + displayName: 'orca', + upstream: { owner: 'stablyai', repo: 'orca' } + }) + ]) + ).toBe(true) + }) +}) diff --git a/src/renderer/src/components/landing-preflight-issues.ts b/src/renderer/src/components/landing-preflight-issues.ts new file mode 100644 index 000000000..73324510a --- /dev/null +++ b/src/renderer/src/components/landing-preflight-issues.ts @@ -0,0 +1,82 @@ +import { translate } from '@/i18n/i18n' +import { projectHostSetupProjectionFromRepos } from '../../../shared/project-host-setup-projection' +import type { Repo } from '../../../shared/types' + +export type PreflightIssue = { + id: string + title: string + description: string + fixLabel: string + fixUrl: string + /** Git is a hard global dependency and stays pinned; provider-specific CLI + * setup is a soft nudge the user can dismiss. */ + dismissible?: boolean +} + +export type LandingPreflightStatus = { + git: { installed: boolean } + gh: { installed: boolean; authenticated: boolean } +} + +export type LandingPreflightIssueOptions = { + hasGitHubBackedProject: boolean +} + +export function hasGitHubBackedProject(repos: readonly Repo[]): boolean { + const projection = projectHostSetupProjectionFromRepos(repos) + return projection.projects.some((project) => project.providerIdentity?.provider === 'github') +} + +export function getLandingPreflightIssues( + status: LandingPreflightStatus, + options: LandingPreflightIssueOptions +): PreflightIssue[] { + const issues: PreflightIssue[] = [] + + if (!status.git.installed) { + issues.push({ + id: 'git', + title: translate('auto.components.Landing.e5b7296d9d', 'Git is not installed'), + description: translate( + 'auto.components.Landing.b673e7cf1b', + 'Git is required for Git projects, source control, and workspace management.' + ), + fixLabel: 'Install Git', + fixUrl: 'https://git-scm.com/downloads' + }) + } + + // Why: gh only powers GitHub PRs/issues/checks; GitLab-only projects should + // not see GitHub setup pressure on the landing screen. + if (!options.hasGitHubBackedProject) { + return issues + } + + if (!status.gh.installed) { + issues.push({ + id: 'gh', + title: translate('auto.components.Landing.5beaef5f9e', 'GitHub CLI is not installed'), + description: translate( + 'auto.components.Landing.73e1ad4282', + 'Orca uses the GitHub CLI (gh) to show pull requests, issues, and checks.' + ), + fixLabel: 'Install GitHub CLI', + fixUrl: 'https://cli.github.com', + dismissible: true + }) + } else if (!status.gh.authenticated) { + issues.push({ + id: 'gh-auth', + title: translate('auto.components.Landing.9f96d018b7', 'GitHub CLI is not authenticated'), + description: translate( + 'auto.components.Landing.00cee697c1', + 'Run "gh auth login" in a terminal to connect your GitHub account.' + ), + fixLabel: 'Learn more', + fixUrl: 'https://cli.github.com/manual/gh_auth_login', + dismissible: true + }) + } + + return issues +}