mirror of
https://github.com/stablyai/orca.git
synced 2026-10-16 16:03:26 +00:00
Show host ownership for CLI provider accounts
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -219,6 +219,7 @@ Done means: reviewers and future agents can understand the vision, implementatio
|
||||
- [x] 2026-06-12: Added a narrow `.gitignore` rule for `notes/artifacts/` so verification screenshots stay local while the HTML report can reference them; verified with `git check-ignore`.
|
||||
- [x] 2026-06-12: Added `taskSourceContext` and `workspaceRunContext` to retryable pending worktree creation requests, seeded them from the Composer's selected task source and project-host setup, and verified the pending store preserves both identities with focused worktree tests plus web typecheck.
|
||||
- [x] 2026-06-12: Threaded GitHub Tasks row-level mutations through `TaskSourceContext` for issue state, assignees, reviewer requests/removals, PR merge/auto-merge, and new issue creation/follow-up reads; verified with focused GitHub/TaskPage tests, focused oxlint, `pnpm run typecheck`, and `git diff --check`.
|
||||
- [x] 2026-06-12: Added provider account-scope diagnostics to GitHub/GitLab CLI integration cards so code-host credential checks show whether they belong to the local desktop client or selected remote server, matching the existing Linear/Jira ownership copy; verified with focused integration-card scope tests and oxlint.
|
||||
- [x] 2026-06-12: Updated PR #5071 with the current verified scope, evidence docs, validation commands, explicit out-of-scope cloud/onboarding items, and remaining provider/version-skew risk.
|
||||
- [x] 2026-06-12: Added load-time backfill for legacy automations and automation runs that predate `runContext`/`sourceContext`, deriving explicit project host and provider source contexts from the saved repo id/path; verified with focused persistence migration coverage, automation service/precheck tests, daemon/settings merge-repair tests, and full `pnpm run typecheck`.
|
||||
- [x] 2026-06-12: Repaired the merge-repair lint gate without disabling max-lines by extracting automation run-target/usage modules and settings subcomponents, fixed stale source-context hook dependencies, and tightened GitHub PR checks cache ownership so `noCache` bypasses cache/in-flight requests while head-specific checks are reused during refresh events; verified targeted oxlint, focused automation/settings/GitHub cache tests (10 files / 157 tests), `github-checks-cache.test.ts`, `git diff --check`, and full `pnpm run typecheck`.
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { act } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
GitHubIntegrationCard,
|
||||
GitLabIntegrationCard
|
||||
} from './cli-source-control-integration-cards'
|
||||
|
||||
type StoreState = {
|
||||
settings: { activeRuntimeEnvironmentId: string | null }
|
||||
}
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
store: { current: null as StoreState | null },
|
||||
preflight: {
|
||||
statuses: {
|
||||
ghStatus: 'connected',
|
||||
glabStatus: 'connected',
|
||||
bitbucketStatus: 'not-configured',
|
||||
azureDevOpsStatus: 'not-configured',
|
||||
giteaStatus: 'not-configured',
|
||||
bitbucketAccount: null,
|
||||
azureDevOpsAccount: null,
|
||||
giteaAccount: null
|
||||
},
|
||||
unavailable: false,
|
||||
refresh: vi.fn()
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('@/store', () => ({
|
||||
useAppStore: (selector: (state: StoreState) => unknown) => {
|
||||
if (!mocks.store.current) {
|
||||
throw new Error('Store state was not installed')
|
||||
}
|
||||
return selector(mocks.store.current)
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('./source-control-preflight-card-status', () => ({
|
||||
usePreflightCardStatuses: () => mocks.preflight
|
||||
}))
|
||||
|
||||
let root: Root | null = null
|
||||
let container: HTMLDivElement | null = null
|
||||
|
||||
async function renderCard(card: React.ReactNode): Promise<HTMLDivElement> {
|
||||
container = document.createElement('div')
|
||||
document.body.appendChild(container)
|
||||
root = createRoot(container)
|
||||
await act(async () => {
|
||||
root?.render(card)
|
||||
})
|
||||
return container
|
||||
}
|
||||
|
||||
describe('CLI source-control integration card account scope', () => {
|
||||
afterEach(async () => {
|
||||
if (root) {
|
||||
await act(async () => {
|
||||
root?.unmount()
|
||||
})
|
||||
}
|
||||
root = null
|
||||
container?.remove()
|
||||
container = null
|
||||
mocks.store.current = null
|
||||
mocks.preflight.statuses.ghStatus = 'connected'
|
||||
mocks.preflight.statuses.glabStatus = 'connected'
|
||||
mocks.preflight.unavailable = false
|
||||
mocks.preflight.refresh.mockClear()
|
||||
})
|
||||
|
||||
it('shows local-client ownership for connected GitHub CLI credentials', async () => {
|
||||
mocks.store.current = { settings: { activeRuntimeEnvironmentId: null } }
|
||||
|
||||
const rendered = await renderCard(<GitHubIntegrationCard />)
|
||||
|
||||
expect(rendered.textContent).toContain('GitHub')
|
||||
expect(rendered.textContent).toContain('Connected')
|
||||
expect(rendered.textContent).toContain('Account scope: Local Mac')
|
||||
expect(rendered.textContent).toContain(
|
||||
'Credentials and account checks for this provider are owned by this desktop client.'
|
||||
)
|
||||
})
|
||||
|
||||
it('shows remote-server ownership for GitLab CLI credential checks', async () => {
|
||||
mocks.store.current = { settings: { activeRuntimeEnvironmentId: 'runtime-1' } }
|
||||
mocks.preflight.statuses.glabStatus = 'not-authenticated'
|
||||
|
||||
const rendered = await renderCard(<GitLabIntegrationCard />)
|
||||
|
||||
expect(rendered.textContent).toContain('GitLab')
|
||||
expect(rendered.textContent).toContain('Account scope: Remote server: runtime-1')
|
||||
expect(rendered.textContent).toContain(
|
||||
'Credentials and account checks for this provider are owned by this remote server.'
|
||||
)
|
||||
expect(rendered.textContent).toContain('glab auth login')
|
||||
})
|
||||
})
|
||||
@@ -1,9 +1,36 @@
|
||||
import { ExternalLink, Github, Gitlab, Terminal } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useAppStore } from '@/store'
|
||||
import { IntegrationCardDetails, IntegrationCardShell } from './integration-card-shell'
|
||||
import { getProviderAccountScope } from './provider-account-scope'
|
||||
import { usePreflightCardStatuses } from './source-control-preflight-card-status'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
function ProviderAccountScopeDetails({
|
||||
children
|
||||
}: {
|
||||
children?: React.ReactNode
|
||||
}): React.JSX.Element {
|
||||
const settings = useAppStore((s) => s.settings)
|
||||
const accountScope = getProviderAccountScope(settings)
|
||||
|
||||
return (
|
||||
<IntegrationCardDetails>
|
||||
<div className="text-xs">
|
||||
<span className="font-medium text-foreground">
|
||||
{translate(
|
||||
'auto.components.settings.cli.source.control.integration.cards.account_scope',
|
||||
'Account scope: {{value0}}',
|
||||
{ value0: accountScope.label }
|
||||
)}
|
||||
</span>
|
||||
<div className="mt-0.5 text-muted-foreground">{accountScope.description}</div>
|
||||
</div>
|
||||
{children}
|
||||
</IntegrationCardDetails>
|
||||
)
|
||||
}
|
||||
|
||||
export function GitHubIntegrationCard(): React.JSX.Element {
|
||||
const { statuses, unavailable, refresh } = usePreflightCardStatuses('gh')
|
||||
const status = unavailable ? 'unavailable' : statuses.ghStatus
|
||||
@@ -43,9 +70,9 @@ export function GitHubIntegrationCard(): React.JSX.Element {
|
||||
: 'Not authenticated'
|
||||
}
|
||||
>
|
||||
{status !== 'checking' && !connected ? (
|
||||
<IntegrationCardDetails>
|
||||
{status === 'unavailable' ? (
|
||||
<ProviderAccountScopeDetails>
|
||||
{status !== 'checking' && !connected ? (
|
||||
status === 'unavailable' ? (
|
||||
<>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{translate(
|
||||
@@ -125,9 +152,9 @@ export function GitHubIntegrationCard(): React.JSX.Element {
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</IntegrationCardDetails>
|
||||
) : null}
|
||||
)
|
||||
) : null}
|
||||
</ProviderAccountScopeDetails>
|
||||
</IntegrationCardShell>
|
||||
)
|
||||
}
|
||||
@@ -171,9 +198,9 @@ export function GitLabIntegrationCard(): React.JSX.Element {
|
||||
: 'Not authenticated'
|
||||
}
|
||||
>
|
||||
{status !== 'checking' && !connected ? (
|
||||
<IntegrationCardDetails>
|
||||
{status === 'unavailable' ? (
|
||||
<ProviderAccountScopeDetails>
|
||||
{status !== 'checking' && !connected ? (
|
||||
status === 'unavailable' ? (
|
||||
<>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{translate(
|
||||
@@ -257,9 +284,9 @@ export function GitLabIntegrationCard(): React.JSX.Element {
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</IntegrationCardDetails>
|
||||
) : null}
|
||||
)
|
||||
) : null}
|
||||
</ProviderAccountScopeDetails>
|
||||
</IntegrationCardShell>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user