mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 16:03:17 +00:00
Show pending project host setup status in composer
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -506,6 +506,8 @@ Already partially landed:
|
||||
delete, routed by setup-owning host
|
||||
- project settings action for creating pending setup/provisioning metadata on a
|
||||
known host before a checkout path exists
|
||||
- workspace creation host options distinguish pending/error/unsupported setup
|
||||
metadata from hosts that have no setup
|
||||
|
||||
Not complete yet:
|
||||
|
||||
|
||||
@@ -510,6 +510,8 @@ Already partially landed:
|
||||
delete, routed by setup-owning host
|
||||
- project settings action for creating pending setup/provisioning metadata on a
|
||||
known host before a checkout path exists
|
||||
- workspace creation host options distinguish pending/error/unsupported setup
|
||||
metadata from hosts that have no setup
|
||||
|
||||
Not complete yet:
|
||||
|
||||
|
||||
@@ -428,6 +428,8 @@ Already partially landed:
|
||||
delete, routed by setup-owning host
|
||||
- project settings action for creating pending setup/provisioning metadata on a
|
||||
known host before a checkout path exists
|
||||
- workspace creation host options distinguish pending/error/unsupported setup
|
||||
metadata from hosts that have no setup
|
||||
|
||||
Still needed:
|
||||
|
||||
|
||||
@@ -788,6 +788,9 @@ Landed so far:
|
||||
- Added a project settings action to create pending project-host setup metadata
|
||||
for a known host without requiring an existing checkout path. This lets the UI
|
||||
represent setup/provisioning intent before import/clone/provisioning finishes.
|
||||
- Updated workspace creation host options so non-ready setup metadata appears
|
||||
with its real pending/error/unsupported state instead of being collapsed into
|
||||
a generic "project not set up" row.
|
||||
- Added tests for local repos, SSH repos, same-provider multi-host grouping,
|
||||
no-identity same-name non-grouping, selector cache behavior, persistence
|
||||
backfill, repo mutation synchronization, renderer hydration, runtime RPC
|
||||
|
||||
@@ -100,4 +100,83 @@ describe('buildProjectHostSetupOptions', () => {
|
||||
})
|
||||
])
|
||||
})
|
||||
|
||||
it('shows pending setup status for known hosts with non-ready setup metadata', () => {
|
||||
const options = buildProjectHostSetupOptions({
|
||||
projectId: 'project-1',
|
||||
eligibleRepos: [repo('local-repo')],
|
||||
hosts: [host('local'), host('runtime:gpu', { label: 'GPU VM' })],
|
||||
projectHostSetups: [
|
||||
setup('local', 'project-1', 'local', 'local-repo'),
|
||||
setup('gpu-pending', 'project-1', 'runtime:gpu', '', {
|
||||
path: '',
|
||||
setupState: 'setting-up',
|
||||
setupMethod: 'provisioned'
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
expect(options).toEqual([
|
||||
expect.objectContaining({ id: 'local', kind: 'ready', label: 'Local Mac' }),
|
||||
expect.objectContaining({
|
||||
id: 'needs-setup:runtime:gpu',
|
||||
kind: 'needs-setup',
|
||||
label: 'GPU VM',
|
||||
detail: 'Project setup is in progress'
|
||||
})
|
||||
])
|
||||
})
|
||||
|
||||
it('uses specific pending details for not-set-up, error, and unsupported setup metadata', () => {
|
||||
const base = {
|
||||
projectId: 'project-1',
|
||||
eligibleRepos: [repo('local-repo')],
|
||||
projectHostSetups: [setup('local', 'project-1', 'local', 'local-repo')]
|
||||
}
|
||||
|
||||
expect(
|
||||
buildProjectHostSetupOptions({
|
||||
...base,
|
||||
hosts: [host('runtime:gpu', { label: 'GPU VM' })],
|
||||
projectHostSetups: [
|
||||
...base.projectHostSetups,
|
||||
setup('gpu-pending', 'project-1', 'runtime:gpu', '', {
|
||||
path: '',
|
||||
setupState: 'not-set-up',
|
||||
setupMethod: 'provisioned'
|
||||
})
|
||||
]
|
||||
}).at(-1)
|
||||
).toMatchObject({ detail: 'Project tracked on this host but not set up' })
|
||||
|
||||
expect(
|
||||
buildProjectHostSetupOptions({
|
||||
...base,
|
||||
hosts: [host('runtime:gpu', { label: 'GPU VM' })],
|
||||
projectHostSetups: [
|
||||
...base.projectHostSetups,
|
||||
setup('gpu-pending', 'project-1', 'runtime:gpu', '', {
|
||||
path: '',
|
||||
setupState: 'error',
|
||||
setupMethod: 'provisioned'
|
||||
})
|
||||
]
|
||||
}).at(-1)
|
||||
).toMatchObject({ detail: 'Project setup needs attention' })
|
||||
|
||||
expect(
|
||||
buildProjectHostSetupOptions({
|
||||
...base,
|
||||
hosts: [host('runtime:gpu', { label: 'GPU VM' })],
|
||||
projectHostSetups: [
|
||||
...base.projectHostSetups,
|
||||
setup('gpu-pending', 'project-1', 'runtime:gpu', '', {
|
||||
path: '',
|
||||
setupState: 'unsupported',
|
||||
setupMethod: 'provisioned'
|
||||
})
|
||||
]
|
||||
}).at(-1)
|
||||
).toMatchObject({ detail: 'Project is unsupported on this host' })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -40,6 +40,7 @@ type BuildNeedsSetupOptionsInput = {
|
||||
projectId: string
|
||||
hosts: readonly ExecutionHostRegistryEntry[]
|
||||
readySetupByHost: ReadonlyMap<ExecutionHostId, ReadyProjectHostSetupOption>
|
||||
pendingSetupByHost: ReadonlyMap<ExecutionHostId, ProjectHostSetup>
|
||||
}
|
||||
|
||||
type BuildProjectHostSetupOptionsInput = {
|
||||
@@ -60,16 +61,34 @@ export function buildProjectHostSetupOptions({
|
||||
}
|
||||
const readyOptions = buildReadySetupOptions({ projectId, projectHostSetups, eligibleRepos })
|
||||
const readySetupByHost = new Map(readyOptions.map((option) => [option.hostId, option]))
|
||||
const pendingSetupByHost = getPendingSetupByHost(projectId, projectHostSetups)
|
||||
return [
|
||||
...readyOptions,
|
||||
...buildNeedsSetupOptions({
|
||||
projectId,
|
||||
hosts,
|
||||
readySetupByHost
|
||||
readySetupByHost,
|
||||
pendingSetupByHost
|
||||
})
|
||||
].sort((a, b) => compareProjectHostSetupOptions(a, b))
|
||||
}
|
||||
|
||||
function getPendingSetupByHost(
|
||||
projectId: string,
|
||||
projectHostSetups: readonly ProjectHostSetup[]
|
||||
): Map<ExecutionHostId, ProjectHostSetup> {
|
||||
const setups = new Map<ExecutionHostId, ProjectHostSetup>()
|
||||
for (const setup of projectHostSetups) {
|
||||
if (setup.projectId !== projectId || setup.setupState === 'ready') {
|
||||
continue
|
||||
}
|
||||
if (!setups.has(setup.hostId)) {
|
||||
setups.set(setup.hostId, setup)
|
||||
}
|
||||
}
|
||||
return setups
|
||||
}
|
||||
|
||||
function buildReadySetupOptions({
|
||||
projectId,
|
||||
projectHostSetups,
|
||||
@@ -98,18 +117,39 @@ function buildReadySetupOptions({
|
||||
function buildNeedsSetupOptions({
|
||||
projectId,
|
||||
hosts,
|
||||
readySetupByHost
|
||||
readySetupByHost,
|
||||
pendingSetupByHost
|
||||
}: BuildNeedsSetupOptionsInput): NeedsSetupProjectHostOption[] {
|
||||
return hosts
|
||||
.filter((host) => !readySetupByHost.has(host.id))
|
||||
.map((host) => ({
|
||||
id: `needs-setup:${host.id}`,
|
||||
kind: 'needs-setup' as const,
|
||||
projectId,
|
||||
hostId: host.id,
|
||||
label: host.label || getExecutionHostLabel(host.id),
|
||||
detail: 'Project not set up on this host'
|
||||
}))
|
||||
.map((host) => {
|
||||
const pendingSetup = pendingSetupByHost.get(host.id)
|
||||
return {
|
||||
id: `needs-setup:${host.id}`,
|
||||
kind: 'needs-setup' as const,
|
||||
projectId,
|
||||
hostId: host.id,
|
||||
label: host.label || getExecutionHostLabel(host.id),
|
||||
detail: pendingSetup
|
||||
? getPendingSetupDetail(pendingSetup)
|
||||
: 'Project not set up on this host'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getPendingSetupDetail(setup: ProjectHostSetup): string {
|
||||
switch (setup.setupState) {
|
||||
case 'not-set-up':
|
||||
return 'Project tracked on this host but not set up'
|
||||
case 'setting-up':
|
||||
return 'Project setup is in progress'
|
||||
case 'error':
|
||||
return 'Project setup needs attention'
|
||||
case 'unsupported':
|
||||
return 'Project is unsupported on this host'
|
||||
case 'ready':
|
||||
return setup.path
|
||||
}
|
||||
}
|
||||
|
||||
function compareProjectHostSetupOptions(
|
||||
|
||||
Reference in New Issue
Block a user