Skip GitHub CLI preflight checks if no GitHub projects exist

This commit is contained in:
Jinjing 2026-06-18 08:51:08 -07:00 committed by GitHub
parent fd536d3e48
commit 4ca248383b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 208 additions and 66 deletions

View File

@ -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<PreflightIssue[]>([])
@ -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 {
</div>
</div>
<div className="absolute bottom-6 left-0 right-0 flex justify-center">
<GitHubStarButton hasRepos={repos.length > 0} />
</div>
{showGitHubSupportFooter && (
<div className="absolute bottom-6 left-0 right-0 flex justify-center">
<GitHubStarButton hasRepos={repos.length > 0} />
</div>
)}
</div>
)
}

View File

@ -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<Repo> & Pick<Repo, 'id' | 'path' | 'displayName'>): 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)
})
})

View File

@ -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
}