mirror of
https://github.com/stablyai/orca.git
synced 2026-11-04 19:17:28 +00:00
* fix(runtime): project registration dedupes only against rows on this machine Codex review 6 #7: create() matched paths across hosts, so creating a local project at a path an SSH project used returned the SSH row and made no directory. add() with no host had the same gap. One predicate, runtimeRepoIsRegistrationCandidate, now backs add, create and clone. * test(runtime): cover nested-SSH rows and clone reuse in host-qualified dedupe Review follow-up (Opus P3s): runtime:<env> rows carrying a nested SSH connectionId, add() for a runtime host, the clone controller, and a stricter create() assertion. * test(runtime): clone dedupe fixture commits without signing
108 lines
4.1 KiB
TypeScript
108 lines
4.1 KiB
TypeScript
import type { Repo } from '../../shared/repo-types'
|
|
import {
|
|
getRepoExecutionHostId,
|
|
getRepoSshConnectionId,
|
|
type ExecutionHostId
|
|
} from '../../shared/execution-host'
|
|
import { splitWorktreeId } from '../../shared/worktree/id'
|
|
import type { GitPushTarget } from '../../shared/worktree/types'
|
|
|
|
export type RuntimeWorktreeRemovalTarget = {
|
|
id: string
|
|
repoId: string
|
|
path: string
|
|
pushTarget?: GitPushTarget
|
|
}
|
|
|
|
/**
|
|
* Options for `removeManagedWorktree`. Named rather than positional on purpose: three of the
|
|
* four are interchangeable booleans that each waive a different safety check on a destructive
|
|
* delete, so a transposition would silently delete a checkout the caller meant to protect.
|
|
*/
|
|
export type RemoveManagedWorktreeOptions = {
|
|
force?: boolean
|
|
runHooks?: boolean
|
|
/** Waives proof that every PTY stopped (#11960). Set by explicit Force Delete only. */
|
|
allowUnverifiedPtyStop?: boolean
|
|
/** Waives a FAILED archive hook (#19334). Never implied by `force`, never by `runHooks`. */
|
|
allowFailedArchiveHook?: boolean
|
|
hostId?: string
|
|
/** Reply with the finished delete's result; otherwise a background delete replies on acceptance. */
|
|
waitForBackgroundRemoval?: boolean
|
|
}
|
|
|
|
export function getRuntimeWorktreeRemovalOptionsKey(
|
|
options: Pick<
|
|
RemoveManagedWorktreeOptions,
|
|
'force' | 'runHooks' | 'allowUnverifiedPtyStop' | 'allowFailedArchiveHook'
|
|
>
|
|
): string {
|
|
// Why: a forced retry must not coalesce onto the in-flight attempt that just
|
|
// failed the PTY gate — it would inherit that failure instead of retrying.
|
|
const ptyKey = options.allowUnverifiedPtyStop ? 'allow-unverified-pty' : 'require-pty-stop'
|
|
// Same reason for the archive waiver: a retry that waives the failed hook must not coalesce
|
|
// onto the in-flight attempt that is about to refuse on it.
|
|
const archiveKey = options.allowFailedArchiveHook ? 'allow-failed-archive' : 'require-archive'
|
|
const hooksKey = options.runHooks ? 'run-hooks' : 'skip-hooks'
|
|
return `${options.force ? 'force' : 'normal'}:${hooksKey}:${ptyKey}:${archiveKey}`
|
|
}
|
|
|
|
// Null executionHostId means host-unaware: path-only callers match any repo, and the first runtime
|
|
// host can adopt a legacy (unstamped) repo. A repo that names a host in *either* spelling matches
|
|
// only that host — including its own ssh:<connectionId>, which an executionHostId-only comparison
|
|
// used to reject, so an unstamped SSH repo failed to dedupe against itself.
|
|
export function runtimeRepoMatchesExecutionHost(
|
|
repo: Pick<Repo, 'connectionId' | 'executionHostId'>,
|
|
executionHostId?: ExecutionHostId | null
|
|
): boolean {
|
|
if (executionHostId == null) {
|
|
return true
|
|
}
|
|
if (repo.executionHostId == null && repo.connectionId == null) {
|
|
return true
|
|
}
|
|
return getRepoExecutionHostId(repo) === executionHostId
|
|
}
|
|
|
|
/**
|
|
* Whether `repo` can be the row a registration in this process dedupes onto. Registration reads its
|
|
* path on this machine, so an SSH row at the same path string is another machine's project.
|
|
*/
|
|
export function runtimeRepoIsRegistrationCandidate(
|
|
repo: Pick<Repo, 'connectionId' | 'executionHostId'>,
|
|
executionHostId?: ExecutionHostId | null
|
|
): boolean {
|
|
return runtimeRepoMatchesExecutionHost(repo, executionHostId) && !getRepoSshConnectionId(repo)
|
|
}
|
|
|
|
export function parseExactWorktreeIdSelector(
|
|
selector: string
|
|
): RuntimeWorktreeRemovalTarget | null {
|
|
const worktreeId = selector.startsWith('id:') ? selector.slice(3) : selector
|
|
const parsed = splitWorktreeId(worktreeId)
|
|
if (!parsed || !parsed.repoId || !parsed.worktreePath) {
|
|
return null
|
|
}
|
|
return {
|
|
id: worktreeId,
|
|
repoId: parsed.repoId,
|
|
path: parsed.worktreePath
|
|
}
|
|
}
|
|
|
|
export function normalizeLocalBranchName(branchName: string | undefined): string {
|
|
return branchName?.replace(/^refs\/heads\//, '') ?? ''
|
|
}
|
|
|
|
export function getExplicitWorktreeIdSelector(selector: string | undefined): string | null {
|
|
if (!selector?.startsWith('id:')) {
|
|
return null
|
|
}
|
|
const id = selector.slice(3)
|
|
return id.length > 0 ? id : null
|
|
}
|
|
|
|
export function hasLocalGitOptions(gitOptions: { wslDistro?: string }): boolean {
|
|
return Object.keys(gitOptions).length > 0
|
|
}
|