test(e2e): one posixShellQuote; docker-ssh-host fixture names (Phase 3.99 0.11b) (#27449)

* test(e2e): one posixShellQuote; docker-ssh-host fixture names

* test(e2e): leave the main-failing stall-recovery spec untouched

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
This commit is contained in:
OrcaWin
2026-10-11 05:15:31 -07:00
committed by GitHub
co-authored by m4air
parent 4468a5375f
commit bdc0921718
39 changed files with 229 additions and 356 deletions
+1 -1
View File
@@ -125,7 +125,7 @@ it('routes managed target-owner guards and menus to the real launch regression',
'tests/e2e/helpers/orcad-convert-flow.ts',
'tests/e2e/helpers/orcad-convert-host.ts',
'tests/e2e/helpers/orcad-upgrade-profile.ts',
'tests/e2e/helpers/docker-ssh-relay-target.ts'
'tests/e2e/helpers/docker-ssh-host-target.ts'
],
ORCAD_OPEN_IN_OWNER_E2E_SPEC
)
+4 -4
View File
@@ -144,7 +144,7 @@ export const PR_E2E_SOURCE_ROUTES = [
id: 'ssh.orcad-browser-drop-owner',
specs: ['tests/e2e/ssh-orcad-browser-drop-owner.spec.ts'],
matches: (file) =>
/^tests\/e2e\/helpers\/(?:orcad-convert-(?:flow|host)|orcad-upgrade-profile|docker-ssh-relay-target|browser-split-(?:guest-probes|page-server))\.ts$/.test(
/^tests\/e2e\/helpers\/(?:orcad-convert-(?:flow|host)|orcad-upgrade-profile|docker-ssh-host-target|browser-split-(?:guest-probes|page-server))\.ts$/.test(
file
) ||
(isProductSource(file) &&
@@ -156,7 +156,7 @@ export const PR_E2E_SOURCE_ROUTES = [
id: 'ssh.orcad-open-in-owner',
specs: ['tests/e2e/ssh-orcad-open-in-owner.spec.ts'],
matches: (file) =>
/^tests\/e2e\/helpers\/(?:orcad-convert-(?:flow|host)|orcad-upgrade-profile|docker-ssh-relay-target)\.ts$/.test(
/^tests\/e2e\/helpers\/(?:orcad-convert-(?:flow|host)|orcad-upgrade-profile|docker-ssh-host-target)\.ts$/.test(
file
) ||
(isProductSource(file) &&
@@ -168,7 +168,7 @@ export const PR_E2E_SOURCE_ROUTES = [
id: 'ssh.orcad-terminal-root-owner',
specs: ['tests/e2e/ssh-orcad-terminal-root-owner.spec.ts'],
matches: (file) =>
/^tests\/e2e\/helpers\/(?:terminal-workspace-root-link|orcad-convert-(?:flow|host)|orcad-upgrade-profile|docker-ssh-relay-target)\.ts$/.test(
/^tests\/e2e\/helpers\/(?:terminal-workspace-root-link|orcad-convert-(?:flow|host)|orcad-upgrade-profile|docker-ssh-host-target)\.ts$/.test(
file
) ||
(isProductSource(file) &&
@@ -208,7 +208,7 @@ export const PR_E2E_SOURCE_ROUTES = [
specs: ['tests/e2e/ssh-browser-network-execution-route.docker.unit.test.ts'],
matches: (file) =>
file === 'tests/e2e/ssh-browser-network-execution-route.docker.unit.test.ts' ||
/^tests\/e2e\/helpers\/docker-ssh-relay-(?:image|target)\.ts$/.test(file) ||
/^tests\/e2e\/helpers\/docker-ssh-host-(?:image|target)\.ts$/.test(file) ||
(isProductSource(file) &&
/^src\/main\/(?:browser\/(?:ssh-browser-network-execution-route|browser-network-deferred-socket|browser-network-execution-route|system-ssh-socks-client-socket)|ssh\/system-ssh-dynamic-forward-process)\.ts$/.test(
file
@@ -32,13 +32,13 @@ it('executes both Docker network routes in a Node job with their opt-in enabled'
'src/main/browser/browser-network-execution-route.ts',
'src/main/browser/system-ssh-socks-client-socket.ts',
'src/main/ssh/system-ssh-dynamic-forward-process.ts',
'tests/e2e/helpers/docker-ssh-relay-target.ts',
'tests/e2e/helpers/docker-ssh-relay-image.ts'
'tests/e2e/helpers/docker-ssh-host-target.ts',
'tests/e2e/helpers/docker-ssh-host-image.ts'
]) {
expect(selectPrE2eSpecs([changed])).toContain(spec)
}
expect(selectPrE2eSpecs(['src/renderer/src/components/Unrelated.tsx'])).not.toContain(spec)
expect(selectPrE2eSpecs(['tests/e2e/helpers/docker-ssh-relay-terminal-tabs.ts'])).not.toContain(
expect(selectPrE2eSpecs(['tests/e2e/helpers/docker-ssh-host-terminal-tabs.ts'])).not.toContain(
spec
)
})
@@ -15,7 +15,7 @@ vi.mock('node:fs', () => ({
realpathSync: (value: string) => value,
writeFileSync: fixture.write
}))
vi.mock('./helpers/docker-ssh-relay-image', () => ({ prepareDockerSshRelayImage: vi.fn() }))
vi.mock('./helpers/docker-ssh-host-image', () => ({ prepareDockerSshHostImage: vi.fn() }))
import globalSetup, { workerTestRepositoryPathFile } from './global-setup'
+2 -2
View File
@@ -15,7 +15,7 @@ import { randomUUID } from 'node:crypto'
import { existsSync, mkdirSync, mkdtempSync, realpathSync, writeFileSync } from 'node:fs'
import path from 'node:path'
import os from 'node:os'
import { prepareDockerSshRelayImage } from './helpers/docker-ssh-relay-image'
import { prepareDockerSshHostImage } from './helpers/docker-ssh-host-image'
export const E2E_TEST_REPO_PATH_FILE_ENV = 'ORCA_E2E_TEST_REPO_PATH_FILE'
const RUN_TEST_REPO_PATH_FILE =
@@ -117,7 +117,7 @@ export default function globalSetup(): void {
}
if (process.env.ORCA_E2E_SSH_DOCKER === '1') {
console.error('[e2e] Preparing Docker OpenSSH fixture image...')
prepareDockerSshRelayImage(root)
prepareDockerSshHostImage(root)
}
// ── 2. Create a seeded test git repo ───────────────────────────────
@@ -8,6 +8,7 @@ import { expect, test } from './helpers/orca-app'
import { launchHeadlessPairedRuntimeHost } from './helpers/headless-paired-runtime-host'
import { launchPairedWebClient } from './helpers/paired-electron-client'
import { getTerminalContent, waitForActivePanePtyId } from './helpers/terminal'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const MIN_EXHAUSTED_ACK_BYTES = 400 * 1024
const scratch = mkdtempSync(path.join(os.tmpdir(), 'orca-headless-stalled-stream-'))
@@ -40,15 +41,11 @@ test.afterAll(() => {
rmSync(scratch, { recursive: true, force: true })
})
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
function fixtureCommand(): string {
const command = [process.execPath, fixturePath]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
async function callRuntime<TResult>(page: Page, method: string, params: unknown): Promise<TResult> {
@@ -30,20 +30,20 @@ function hashDockerFixtureDirectory(fixtureDir: string): string {
}
function fixtureImage(root: string): string {
const fixtureDir = path.join(root, 'tests', 'e2e', 'fixtures', 'docker-ssh-relay')
const fixtureDir = path.join(root, 'tests', 'e2e', 'fixtures', 'docker-ssh-host')
const digest = hashDockerFixtureDirectory(fixtureDir)
return `orca-e2e-ssh-relay:${digest}`
return `orca-e2e-ssh-host:${digest}`
}
export function getDockerSshRelayImage(): string {
export function getDockerSshHostImage(): string {
return process.env.ORCA_E2E_SSH_DOCKER_IMAGE ?? fixtureImage(process.cwd())
}
export function prepareDockerSshRelayImage(root: string): void {
export function prepareDockerSshHostImage(root: string): void {
if (process.env.ORCA_E2E_SSH_DOCKER_IMAGE) {
return
}
const fixtureDir = path.join(root, 'tests', 'e2e', 'fixtures', 'docker-ssh-relay')
const fixtureDir = path.join(root, 'tests', 'e2e', 'fixtures', 'docker-ssh-host')
const image = fixtureImage(root)
execFileSync(
'docker',
@@ -52,7 +52,7 @@ export function prepareDockerSshRelayImage(root: string): void {
)
}
export function ensureDockerSshRelayImage(root: string): void {
export function ensureDockerSshHostImage(root: string): void {
if (process.env.ORCA_E2E_SSH_DOCKER_IMAGE) {
return
}
@@ -60,5 +60,5 @@ export function ensureDockerSshRelayImage(root: string): void {
if (spawnSync('docker', ['image', 'inspect', image], { stdio: 'ignore' }).status === 0) {
return
}
prepareDockerSshRelayImage(root)
prepareDockerSshHostImage(root)
}
@@ -3,15 +3,14 @@ import { randomUUID } from 'node:crypto'
import { mkdtempSync, readFileSync, rmSync } from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { getDockerSshRelayImage } from './docker-ssh-relay-image'
import { getDockerSshHostImage } from './docker-ssh-host-image'
import { quotePosixShell } from '../../../src/shared/wsl-login-shell-command'
import type { TestInfo } from '@stablyai/playwright-test'
export const DOCKER_SSH_RELAY_REMOTE_REPO_PATH = '/tmp/orca-docker-relay-perf-repo'
export const DOCKER_SSH_PROXY_JUMP_REMOTE_REPO_PATH = '/tmp/orca-docker-proxy-jump-repo'
export const DOCKER_SSH_SECOND_HUB_REMOTE_REPO_PATH = '/tmp/orca-docker-second-hub-repo'
export const DOCKER_SSH_HOST_REMOTE_REPO_PATH = '/tmp/orca-docker-relay-perf-repo'
export type DockerSshRelayTarget = {
export type DockerSshHostTarget = {
containerName: string
containerIp: string
host: string
@@ -32,12 +31,8 @@ function tryRun(command: string, args: string[], opts: { timeoutMs?: number } =
spawnSync(command, args, { stdio: 'ignore', timeout: opts.timeoutMs ?? 10_000 })
}
export function shellQuote(value: string): string {
return `'${value.replaceAll("'", "'\\''")}'`
}
export function execDockerSshRelayTargetCommand(
target: DockerSshRelayTarget,
export function execDockerSshHostTargetCommand(
target: DockerSshHostTarget,
command: string
): string {
return run('docker', ['exec', target.containerName, 'bash', '-lc', command], {
@@ -45,8 +40,8 @@ export function execDockerSshRelayTargetCommand(
})
}
export function execDockerSshRelayTargetControlCommand(
target: DockerSshRelayTarget,
export function execDockerSshHostTargetControlCommand(
target: DockerSshHostTarget,
command: string
): string {
return run('docker', [
@@ -60,7 +55,7 @@ export function execDockerSshRelayTargetControlCommand(
])
}
function sshArgs(target: DockerSshRelayTarget, command: string): string[] {
function sshArgs(target: DockerSshHostTarget, command: string): string[] {
return [
'-i',
target.identityFile,
@@ -79,7 +74,7 @@ function sshArgs(target: DockerSshRelayTarget, command: string): string[] {
]
}
function waitForSsh(target: DockerSshRelayTarget): void {
function waitForSsh(target: DockerSshHostTarget): void {
const deadline = Date.now() + 90_000
let lastError = ''
while (Date.now() < deadline) {
@@ -103,42 +98,25 @@ function waitForSsh(target: DockerSshRelayTarget): void {
)
}
export function dockerSshRelayRepoSentinel(target: DockerSshRelayTarget, repoPath: string): string {
return `${target.containerName}:${repoPath}`
}
function seedRemoteRepo(target: DockerSshRelayTarget, repoPath: string): void {
const sentinel = dockerSshRelayRepoSentinel(target, repoPath)
execDockerSshRelayTargetCommand(
function seedRemoteRepo(target: DockerSshHostTarget, repoPath: string): void {
const sentinel = `${target.containerName}:${repoPath}`
execDockerSshHostTargetCommand(
target,
[
`rm -rf ${shellQuote(repoPath)}`,
`mkdir -p ${shellQuote(repoPath)}`,
`cd ${shellQuote(repoPath)}`,
`rm -rf ${quotePosixShell(repoPath)}`,
`mkdir -p ${quotePosixShell(repoPath)}`,
`cd ${quotePosixShell(repoPath)}`,
'git init',
'git config user.email e2e@test.local',
'git config user.name "Orca Docker SSH E2E"',
`printf '%s\\n' ${shellQuote(sentinel)} > .orca-e2e-destination-id`,
`printf '%s\\n' ${shellQuote(`remote relay ${sentinel}`)} > README.md`,
`printf '%s\\n' ${quotePosixShell(sentinel)} > .orca-e2e-destination-id`,
`printf '%s\\n' ${quotePosixShell(`remote relay ${sentinel}`)} > README.md`,
'git add README.md .orca-e2e-destination-id',
'git commit -m initial'
].join(' && ')
)
}
/**
* The fixture image ships Debian's `/etc/bash.bashrc` with the xterm title block commented out and
* an all-comments `/root/.bashrc`, so its shell never emits OSC 0. Orca derives a tab title from
* that sequence, so without this every SSH tab keeps its `Terminal N` placeholder no matter how
* healthy the shell is. Opt in from specs that assert on titles; a real user's shell sets one.
*/
export function enableDockerSshRelayTargetShellTitle(target: DockerSshRelayTarget): void {
execDockerSshRelayTargetControlCommand(
target,
`printf '%s\\n' ${shellQuote(String.raw`PS1="\[\e]0;\u@\h: \w\a\]$PS1"`)} >> /root/.bashrc`
)
}
/**
* Attempts a `direct-tcpip` channel to a closed loopback port using the host's own ssh client.
*
@@ -146,7 +124,7 @@ export function enableDockerSshRelayTargetShellTitle(target: DockerSshRelayTarge
* reports a connect failure (port 9 is closed on any sane host), while one running
* `AllowTcpForwarding no` refuses the channel itself with "administratively prohibited".
*/
function probeDockerSshRelayTargetForwarding(target: DockerSshRelayTarget): string {
function probeDockerSshHostTargetForwarding(target: DockerSshHostTarget): string {
const result = spawnSync(
'ssh',
[
@@ -181,15 +159,15 @@ function probeDockerSshRelayTargetForwarding(target: DockerSshRelayTarget): stri
* this before the app connects. Returns once a real ssh client has confirmed the refusal, so a
* config that silently failed to apply surfaces here rather than as a confusing assertion later.
*/
export function blockDockerSshRelayTargetTcpForwarding(target: DockerSshRelayTarget): void {
execDockerSshRelayTargetControlCommand(
export function blockDockerSshHostTargetTcpForwarding(target: DockerSshHostTarget): void {
execDockerSshHostTargetControlCommand(
target,
"printf '%s\\n' 'AllowTcpForwarding no' >> /etc/ssh/sshd_config; kill -HUP 1"
)
const deadline = Date.now() + 60_000
let lastProbe = ''
while (Date.now() < deadline) {
lastProbe = probeDockerSshRelayTargetForwarding(target)
lastProbe = probeDockerSshHostTargetForwarding(target)
if (/administratively prohibited/i.test(lastProbe)) {
return
}
@@ -200,58 +178,20 @@ export function blockDockerSshRelayTargetTcpForwarding(target: DockerSshRelayTar
)
}
/**
* Makes the target unreachable over SSH while the container, its fixtures and `docker exec` stay
* up: sshd re-binds to the container's loopback, so the published port stops reaching it. Only new
* connections are refused; an established session lives until its client closes it. Returns once
* a real ssh client fails to connect, so a config that silently failed to apply surfaces here.
*/
export function refuseDockerSshRelayTargetConnections(target: DockerSshRelayTarget): void {
// Why HUP, not a restart: see blockDockerSshRelayTargetTcpForwarding — PID 1 is sshd.
execDockerSshRelayTargetControlCommand(
target,
"printf '%s\\n' 'ListenAddress 127.0.0.1' >> /etc/ssh/sshd_config; kill -HUP 1"
)
const deadline = Date.now() + 60_000
let lastProbe = ''
while (Date.now() < deadline) {
const result = spawnSync('ssh', sshArgs(target, 'true'), {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
timeout: 15_000
})
lastProbe = result.stderr || result.stdout || `exit ${result.status}`
// Why the auth check: a refusal must look like a dead host, never a credential prompt.
if (result.status !== 0 && !/permission denied/i.test(lastProbe)) {
return
}
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 500)
}
throw new Error(`sshd kept accepting connections after ListenAddress 127.0.0.1: ${lastProbe}`)
}
export function writeDockerSshRelayTargetFile(
target: DockerSshRelayTarget,
export function writeDockerSshHostTargetFile(
target: DockerSshHostTarget,
filePath: string,
contents: string
): void {
execDockerSshRelayTargetCommand(
execDockerSshHostTargetCommand(
target,
`printf '%s' ${shellQuote(contents)} > ${shellQuote(filePath)}`
`printf '%s' ${quotePosixShell(contents)} > ${quotePosixShell(filePath)}`
)
}
/** Why not `writeDockerSshRelayTargetFile`: that one passes the contents as a shell argument, so a
* payload the size of a real repository's path list exceeds ARG_MAX before it reaches the shell. */
export function copyFileIntoDockerSshRelayTarget(
target: DockerSshRelayTarget,
localPath: string,
remotePath: string
): void {
run('docker', ['cp', localPath, `${target.containerName}:${remotePath}`], { timeoutMs: 120_000 })
}
export function startDockerSshRelayTarget(testInfo: TestInfo): DockerSshRelayTarget {
export function startDockerSshHostTarget(
testInfo: Pick<TestInfo, 'workerIndex'>
): DockerSshHostTarget {
const host = process.env.ORCA_E2E_SSH_TARGET_HOST?.trim() || '127.0.0.1'
if (host === 'localhost' || host === '::1' || host.startsWith('127.')) {
if (process.env.ORCA_E2E_SSH_TARGET_HOST) {
@@ -264,7 +204,7 @@ export function startDockerSshRelayTarget(testInfo: TestInfo): DockerSshRelayTar
run('ssh-keygen', ['-t', 'ed25519', '-N', '', '-f', identityFile, '-q'])
const publicKey = readFileSync(`${identityFile}.pub`, 'utf8').trim()
const containerName = `orca-ssh-e2e-${testInfo.workerIndex}-${Date.now()}-${randomUUID().slice(0, 8)}`
let target: DockerSshRelayTarget | null = null
let target: DockerSshHostTarget | null = null
try {
tryRun('docker', ['rm', '-f', containerName])
@@ -279,7 +219,7 @@ export function startDockerSshRelayTarget(testInfo: TestInfo): DockerSshRelayTar
`${bindHost}::22`,
'-e',
`AUTHORIZED_KEY=${publicKey}`,
getDockerSshRelayImage(),
getDockerSshHostImage(),
'bash',
'-lc',
[
@@ -308,19 +248,17 @@ export function startDockerSshRelayTarget(testInfo: TestInfo): DockerSshRelayTar
}
target = { containerName, containerIp, host, identityFile, port, tempDir }
waitForSsh(target)
seedRemoteRepo(target, DOCKER_SSH_RELAY_REMOTE_REPO_PATH)
seedRemoteRepo(target, DOCKER_SSH_PROXY_JUMP_REMOTE_REPO_PATH)
seedRemoteRepo(target, DOCKER_SSH_SECOND_HUB_REMOTE_REPO_PATH)
seedRemoteRepo(target, DOCKER_SSH_HOST_REMOTE_REPO_PATH)
return target
} catch (error) {
cleanupDockerSshRelayTarget(
cleanupDockerSshHostTarget(
target ?? { containerName, containerIp: '', host, identityFile, port: 0, tempDir }
)
throw error
}
}
export function cleanupDockerSshRelayTarget(target: DockerSshRelayTarget | null): void {
export function cleanupDockerSshHostTarget(target: DockerSshHostTarget | null): void {
if (!target) {
return
}
@@ -15,6 +15,7 @@ import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { expect, type Page } from '@stablyai/playwright-test'
import { quotePosixShell } from '../../../src/shared/wsl-login-shell-command'
export type RuntimeRpcCall = <TResult>(method: string, params: unknown) => Promise<TResult>
@@ -90,16 +91,12 @@ export function writeRetentionFixture(directory: string): string {
return fixturePath
}
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
export function retentionFixtureCommand(fixturePath: string, sinkPath: string): string {
const command = [process.execPath, fixturePath, sinkPath]
return process.platform === 'win32'
? // PowerShell needs the call operator when the executable is quoted; cmd.exe also accepts it.
`& ${command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')}`
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
export function readSink(sinkPath: string): string {
+13 -13
View File
@@ -8,13 +8,13 @@ import type { TestInfo } from '@stablyai/playwright-test'
import type { SshTargetCreateInput } from '../../../src/shared/ssh-types'
import { parseWindowsHostCellDescriptor } from '../../../src/main/ssh/ssh-windows-host-cells'
import {
blockDockerSshRelayTargetTcpForwarding,
cleanupDockerSshRelayTarget,
DOCKER_SSH_RELAY_REMOTE_REPO_PATH,
execDockerSshRelayTargetCommand,
execDockerSshRelayTargetControlCommand,
startDockerSshRelayTarget
} from './docker-ssh-relay-target'
blockDockerSshHostTargetTcpForwarding,
cleanupDockerSshHostTarget,
DOCKER_SSH_HOST_REMOTE_REPO_PATH,
execDockerSshHostTargetCommand,
execDockerSshHostTargetControlCommand,
startDockerSshHostTarget
} from './docker-ssh-host-target'
export const ORCAD_CONVERT_HOST_ENV = 'ORCA_E2E_ORCAD_CONVERT_HOST'
@@ -50,7 +50,7 @@ echo "$killed"
export function startOrcadConvertHost(source: string, testInfo: TestInfo): OrcadConvertHost {
const label = `orcad convert E2E ${Date.now()}`
if (source === 'docker') {
const target = startDockerSshRelayTarget(testInfo)
const target = startDockerSshHostTarget(testInfo)
return {
input: {
label,
@@ -61,14 +61,14 @@ export function startOrcadConvertHost(source: string, testInfo: TestInfo): Orcad
identitiesOnly: true,
relayGracePeriodSeconds: 1
},
remoteRepoPath: DOCKER_SSH_RELAY_REMOTE_REPO_PATH,
remoteRepoPath: DOCKER_SSH_HOST_REMOTE_REPO_PATH,
remoteFolderPath: '/tmp',
cleanup: () => cleanupDockerSshRelayTarget(target),
blockTcpForwarding: () => blockDockerSshRelayTargetTcpForwarding(target),
exec: (command) => execDockerSshRelayTargetCommand(target, command),
cleanup: () => cleanupDockerSshHostTarget(target),
blockTcpForwarding: () => blockDockerSshHostTargetTcpForwarding(target),
exec: (command) => execDockerSshHostTargetCommand(target, command),
dropSshConnections: () =>
Number(
execDockerSshRelayTargetControlCommand(target, DROP_SSH_CONNECTIONS).split('\n').at(-1)
execDockerSshHostTargetControlCommand(target, DROP_SSH_CONNECTIONS).split('\n').at(-1)
),
setFrozen: (frozen) =>
execFileSync('docker', [frozen ? 'pause' : 'unpause', target.containerName], {
@@ -1,6 +1,7 @@
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { quotePosixShell } from '../../../src/shared/wsl-login-shell-command'
const FILL_ROWS = 6_000
const FLOOD_ROWS = 4_000
@@ -8,15 +9,11 @@ const TITLE_FRAME_INTERVAL_MS = 70
const TITLE_FRAME_COUNT = 30
const TITLE_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
function fixtureCommand(fixturePath: string, marker: string): string {
const command = [process.execPath, fixturePath, marker]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
export function createPairedTerminalParkingFixture(): {
@@ -1,16 +1,13 @@
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import os from 'node:os'
import path from 'node:path'
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
import { quotePosixShell } from '../../../src/shared/wsl-login-shell-command'
function fixtureCommand(fixturePath: string, marker: string): string {
const command = [process.execPath, fixturePath, marker]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
/**
@@ -1,10 +1,10 @@
import {
execDockerSshRelayTargetCommand,
execDockerSshRelayTargetControlCommand,
shellQuote,
writeDockerSshRelayTargetFile,
type DockerSshRelayTarget
} from './docker-ssh-relay-target'
execDockerSshHostTargetCommand,
execDockerSshHostTargetControlCommand,
writeDockerSshHostTargetFile,
type DockerSshHostTarget
} from './docker-ssh-host-target'
import { quotePosixShell } from '../../../src/shared/wsl-login-shell-command'
/**
* An HTTP fixture that exists only inside the SSH container, on a hostname only the
@@ -59,17 +59,17 @@ const SERVER_SOURCE = [
].join('\n')
/** Publishes the remote-only origin inside the container and waits for it to accept sockets. */
export function startSshRemoteOnlyBrowserFixture(target: DockerSshRelayTarget): void {
execDockerSshRelayTargetCommand(
export function startSshRemoteOnlyBrowserFixture(target: DockerSshHostTarget): void {
execDockerSshHostTargetCommand(
target,
`grep -q ${shellQuote(SSH_REMOTE_ONLY_HOST)} /etc/hosts || printf '127.0.0.1 %s\\n' ${shellQuote(
`grep -q ${quotePosixShell(SSH_REMOTE_ONLY_HOST)} /etc/hosts || printf '127.0.0.1 %s\\n' ${quotePosixShell(
SSH_REMOTE_ONLY_HOST
)} >> /etc/hosts`
)
writeDockerSshRelayTargetFile(target, SERVER_PATH, SERVER_SOURCE)
execDockerSshRelayTargetCommand(
writeDockerSshHostTargetFile(target, SERVER_PATH, SERVER_SOURCE)
execDockerSshHostTargetCommand(
target,
`rm -f ${shellQuote(REQUEST_LOG_PATH)}; nohup node ${shellQuote(
`rm -f ${quotePosixShell(REQUEST_LOG_PATH)}; nohup node ${quotePosixShell(
SERVER_PATH
)} >/tmp/sta-4150-ssh-browser-fixture.log 2>&1 </dev/null &`
)
@@ -87,33 +87,18 @@ export function startSshRemoteOnlyBrowserFixture(target: DockerSshRelayTarget):
'}',
'probe()'
].join('\n')
writeDockerSshRelayTargetFile(target, '/tmp/sta-4150-ssh-browser-wait.js', waitForServer)
execDockerSshRelayTargetCommand(target, 'node /tmp/sta-4150-ssh-browser-wait.js')
writeDockerSshHostTargetFile(target, '/tmp/sta-4150-ssh-browser-wait.js', waitForServer)
execDockerSshHostTargetCommand(target, 'node /tmp/sta-4150-ssh-browser-wait.js')
}
/** Every request the remote-only origin served, in order, with the `Cookie` header it saw. */
export function readSshRemoteOnlyRequests(target: DockerSshRelayTarget): SshRemoteOnlyRequest[] {
const contents = execDockerSshRelayTargetControlCommand(
export function readSshRemoteOnlyRequests(target: DockerSshHostTarget): SshRemoteOnlyRequest[] {
const contents = execDockerSshHostTargetControlCommand(
target,
`cat ${shellQuote(REQUEST_LOG_PATH)} 2>/dev/null || true`
`cat ${quotePosixShell(REQUEST_LOG_PATH)} 2>/dev/null || true`
)
return contents
.split('\n')
.filter((line) => line.trim().length > 0)
.map((line) => JSON.parse(line) as SshRemoteOnlyRequest)
}
/**
* Kills the container's established SSH sessions without touching the listener, so the
* transport really dies and the same target can be reconnected.
*
* `pgrep -f '^sshd: '` matches only accepted-connection processes: the daemon's own command
* line is `/usr/sbin/sshd -D -e`, and the shell running this command starts with `bash`.
*/
export function killSshRelayTargetTransport(target: DockerSshRelayTarget): number {
const killed = execDockerSshRelayTargetControlCommand(
target,
"pids=$(pgrep -f '^sshd: ' || true); for pid in $pids; do kill -9 $pid || true; done; printf '%s' \"$(printf '%s\\n' $pids | grep -c . || true)\""
)
return Number(killed.trim() || '0')
}
@@ -10,6 +10,7 @@ import { toWebTerminalSurfaceTabId } from '../../../src/shared/terminal-surface-
import { expect } from './orca-app'
import { getTerminalContent, waitForActivePanePtyId } from './terminal'
import { readFreshTerminalInventory } from './terminal-inventory-observation'
import { quotePosixShell } from '../../../src/shared/wsl-login-shell-command'
const RECOVERY_DEADLINE_MS = 8_000
@@ -39,8 +40,8 @@ function execProfileContents(
const guard = `ORCA_STA4067_EXEC_${runId.replaceAll(/[^A-Za-z0-9_]/g, '_')}`
const barrierScript = barrier
? [
`: > ${shellQuote(barrier.startedPath)}`,
`while [[ ! -e ${shellQuote(barrier.releasePath)} ]]; do sleep 0.02; done`
`: > ${quotePosixShell(barrier.startedPath)}`,
`while [[ ! -e ${quotePosixShell(barrier.releasePath)} ]]; do sleep 0.02; done`
].join('\n ')
: ''
return `if [[ -z "\${${guard}:-}" ]]; then
@@ -59,10 +60,6 @@ export function zshExecProfileContents(runId: string, barrier?: BashExecProfileB
return execProfileContents(runId, ZSH_EXEC_LINE, barrier)
}
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
function splitMarker(marker: string): [string, string] {
const midpoint = Math.floor(marker.length / 2)
return [marker.slice(0, midpoint), marker.slice(midpoint)]
@@ -70,7 +67,7 @@ function splitMarker(marker: string): [string, string] {
function markerCommand(marker: string): string {
const [left, right] = splitMarker(marker)
return `printf '%s%s\\n' ${shellQuote(left)} ${shellQuote(right)}`
return `printf '%s%s\\n' ${quotePosixShell(left)} ${quotePosixShell(right)}`
}
function count(text: string, marker: string): number {
@@ -150,7 +147,7 @@ export async function createStartupExecTerminal(
): Promise<StartupExecTerminal> {
const startupMarker = `STA4067_STARTUP_READY_${runId}`
const command = [
`printf '%s|%s\\n' "$$" "$(tty)" > ${shellQuote(ledgerPath)}`,
`printf '%s|%s\\n' "$$" "$(tty)" > ${quotePosixShell(ledgerPath)}`,
markerCommand(startupMarker)
].join('; ')
const created = await callStartupExecRuntime<{
@@ -1,7 +1,7 @@
import type { Page, TestInfo } from '@stablyai/playwright-test'
import { expect } from './orca-app'
import { shellQuote } from './docker-ssh-relay-target'
import { focusActiveTerminalInput, getTerminalContent } from './terminal'
import { quotePosixShell } from '../../../src/shared/wsl-login-shell-command'
export async function openTerminalWorkspaceRootLink(
page: Page,
@@ -13,7 +13,7 @@ export async function openTerminalWorkspaceRootLink(
await focusActiveTerminalInput(page)
const readyMarker = `ROOT_LINK_PRINTED_${Date.now()}`
await page.keyboard.insertText(
String.raw`printf '\n%s\n%s\n' ${shellQuote(rootPath)} ${shellQuote(readyMarker)}`
String.raw`printf '\n%s\n%s\n' ${quotePosixShell(rootPath)} ${quotePosixShell(readyMarker)}`
)
await page.keyboard.press('Enter')
await expect
@@ -46,6 +46,7 @@ import {
} from './helpers/paired-electron-client'
import { focusActiveTerminalInput } from './helpers/terminal'
import { parkHiddenTabBehindDecoy } from './helpers/terminal-hidden-parking'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const PARK_DELAY_MS = 30_000
const PAINT_BUDGET_MS = 20_000
@@ -81,15 +82,11 @@ test.afterAll(() => {
// Why: the HOST must park quickly too, so its launch env carries the override.
test.use({ orcaAppExtraEnv: { ORCA_E2E_TERMINAL_PARKING_DELAY_MS: String(PARK_DELAY_MS) } })
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
function fixtureCommand(sinkPath: string): string {
const command = [process.execPath, fixturePath, sinkPath]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
function readSink(sinkPath: string): string {
@@ -39,6 +39,7 @@ import {
type PairedElectronClient
} from './helpers/paired-electron-client'
import { waitForActiveWorktree, waitForSessionReady } from './helpers/store'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
/** What a user would accept for "my terminal is back" after reopening the app. */
const RESTORE_BUDGET_MS = 60_000
@@ -72,15 +73,11 @@ test.afterAll(() => {
rmSync(scratch, { recursive: true, force: true })
})
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
function fixtureCommand(sinkPath: string): string {
const command = [process.execPath, fixturePath, sinkPath]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
function readSinkLines(sinkPath: string): string[] {
@@ -30,6 +30,7 @@ import {
holdWindowGraphPublication,
releaseWindowGraphPublication
} from './helpers/hold-window-graph-publication'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const scratch = mkdtempSync(path.join(os.tmpdir(), 'orca-paired-host-quit-input-'))
const fixturePath = path.join(scratch, 'paired-host-quit-terminal.mjs')
@@ -59,16 +60,12 @@ test.afterAll(() => {
rmSync(scratch, { recursive: true, force: true })
})
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
function fixtureCommand(sinkPath: string): string {
const command = [process.execPath, fixturePath, sinkPath]
return process.platform === 'win32'
? // Why: the isolated Windows profile starts PowerShell, which parses a leading quoted path as a string, not a command.
`& ${command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')}`
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
function seededRepoPathOrSkip(): string {
@@ -16,6 +16,7 @@ import {
} from './helpers/paired-electron-client'
import { attachRepoAndOpenTerminal, createRestartSession } from './helpers/orca-restart'
import { waitForTabParked } from './helpers/terminal-hidden-parking'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const PARK_DELAY_MS = 2_000
const scratch = mkdtempSync(path.join(os.tmpdir(), 'orca-paired-host-restart-background-'))
@@ -47,15 +48,11 @@ test.afterAll(() => {
rmSync(scratch, { recursive: true, force: true })
})
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
function fixtureCommand(sinkPath: string): string {
const command = [process.execPath, fixturePath, sinkPath]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
function seededRepoPathOrSkip(): string {
@@ -25,6 +25,7 @@ import {
readPairedPaneContent,
waitForPairedPaneMarker
} from './helpers/paired-host-terminal'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const LINE_COUNT = 300
const scratch = mkdtempSync(path.join(os.tmpdir(), 'orca-initial-scrollback-'))
@@ -48,7 +49,7 @@ function fixtureCommand(): string {
const command = [process.execPath, fixturePath]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map((value) => `'${value.replaceAll("'", `'\\''`)}'`).join(' ')
: command.map(quotePosixShell).join(' ')
}
test('a desktop client opening a remote terminal receives its scrollback, not just the screen', async ({
@@ -9,6 +9,7 @@ import {
createRuntimeDesktopPairingOffer,
launchPairedElectronClient
} from './helpers/paired-electron-client'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const scratch = mkdtempSync(path.join(os.tmpdir(), 'orca-paired-lossy-snapshot-'))
const fixturePath = path.join(scratch, 'lossy-snapshot-terminal.mjs')
@@ -29,15 +30,11 @@ test.use({
orcaAppExtraEnv: { ORCA_E2E_FORCE_REMOTE_TERMINAL_INITIAL_SNAPSHOT_TRUNCATED: '1' }
})
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
function fixtureCommand(marker: string): string {
const command = [process.execPath, fixturePath, marker]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
async function callRuntime<TResult>(
@@ -17,6 +17,7 @@ import {
} from './helpers/paired-electron-client'
import { getTerminalContent, waitForActivePanePtyId } from './helpers/terminal'
import { readFreshTerminalInventory } from './helpers/terminal-inventory-observation'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const scratch = mkdtempSync(path.join(os.tmpdir(), 'orca-paired-materialize-'))
const fixturePath = path.join(scratch, 'materialize-terminal.mjs')
@@ -49,15 +50,11 @@ test.afterAll(() => {
rmSync(scratch, { recursive: true, force: true })
})
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
function fixtureCommand(): string {
const command = [process.execPath, fixturePath, processedInputPath]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
async function callRuntime<TResult>(
@@ -37,6 +37,7 @@ import {
} from './helpers/paired-electron-client'
import { focusActiveTerminalInput } from './helpers/terminal'
import { waitForTabParked } from './helpers/terminal-hidden-parking'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const PARK_DELAY_MS = 2_000
const LIVE_PAINT_BUDGET_MS = 12_000
@@ -75,15 +76,11 @@ test.afterAll(() => {
rmSync(scratch, { recursive: true, force: true })
})
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
function fixtureCommand(sinkPath: string): string {
const command = [process.execPath, fixturePath, sinkPath]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
function readSink(sinkPath: string): string {
@@ -55,6 +55,7 @@ import {
import { focusActiveTerminalInput } from './helpers/terminal'
import { waitForTabParked } from './helpers/terminal-hidden-parking'
import { readPersistedProfileState } from './helpers/persisted-profile-state'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const PARK_DELAY_MS = 2_000
const PAINT_BUDGET_MS = 30_000
@@ -88,7 +89,7 @@ function fixtureCommand(): string {
const command = [process.execPath, fixturePath]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map((value) => `'${value.replaceAll("'", `'\\''`)}'`).join(' ')
: command.map(quotePosixShell).join(' ')
}
type OnDiskPartitionReading = {
@@ -49,6 +49,7 @@ import {
} from './helpers/paired-host-terminal'
import { focusActiveTerminalInput } from './helpers/terminal'
import { waitForTabParked } from './helpers/terminal-hidden-parking'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const PARK_DELAY_MS = 2_000
const PAINT_BUDGET_MS = 30_000
@@ -81,15 +82,11 @@ test.afterAll(() => {
rmSync(scratch, { recursive: true, force: true })
})
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
function fixtureCommand(): string {
const command = [process.execPath, fixturePath]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
type ParkKind = 'ordinary' | 'force'
@@ -14,6 +14,7 @@ import {
launchPairedWebClient
} from './helpers/paired-electron-client'
import { getTerminalContent, waitForActivePanePtyId } from './helpers/terminal'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const scratch = mkdtempSync(path.join(os.tmpdir(), 'orca-paired-probe-gap-'))
const fixturePath = path.join(scratch, 'probe-gap-terminal.mjs')
@@ -44,15 +45,11 @@ test.afterAll(() => {
rmSync(scratch, { recursive: true, force: true })
})
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
function fixtureCommand(): string {
const command = [process.execPath, fixturePath, processedInputPath]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
async function callRuntime<TResult>(page: Page, method: string, params: unknown): Promise<TResult> {
@@ -11,6 +11,7 @@ import {
launchPairedWebClient
} from './helpers/paired-electron-client'
import { getTerminalContent, waitForActivePanePtyId } from './helpers/terminal'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const RETENTION_PARK_DELAY_MS = 100
const scratch = mkdtempSync(path.join(os.tmpdir(), 'orca-paired-truncated-tail-'))
@@ -46,15 +47,11 @@ test.use({
}
})
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
function fixtureCommand(marker: string): string {
const command = [process.execPath, fixturePath, marker]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
async function callRuntime<TResult>(page: Page, method: string, params: unknown): Promise<TResult> {
@@ -9,6 +9,7 @@ import {
} from './helpers/paired-electron-client'
import { toWebTerminalSurfaceTabId } from '../../src/shared/terminal-surface-id'
import type { RuntimeTerminalSummary } from '../../src/shared/runtime-types'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
type ClientMirror = {
activeTabId: string | null
@@ -45,15 +46,11 @@ test.afterAll(() => {
rmSync(scratch, { recursive: true, force: true })
})
function shellQuote(value: string): string {
return `'${value.replaceAll("'", `'\\''`)}'`
}
function fixtureCommand(scriptPath: string, ...args: string[]): string {
const command = [process.execPath, scriptPath, ...args]
return process.platform === 'win32'
? command.map((value) => `"${value.replaceAll('"', '""')}"`).join(' ')
: command.map(shellQuote).join(' ')
: command.map(quotePosixShell).join(' ')
}
function countAgentSpawns(): number {
@@ -3,18 +3,17 @@ import { readFileSync, writeFileSync } from 'node:fs'
import path from 'node:path'
import { Client } from 'ssh2'
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
import type { TestInfo } from '@stablyai/playwright-test'
import type { SshConnection } from '../../src/main/ssh/ssh-connection'
import type { SshProviderEpoch } from '../../src/shared/ssh-types'
import { ensureDockerSshRelayImage } from './helpers/docker-ssh-relay-image'
import { ensureDockerSshHostImage } from './helpers/docker-ssh-host-image'
import {
cleanupDockerSshRelayTarget,
execDockerSshRelayTargetCommand,
shellQuote,
startDockerSshRelayTarget,
type DockerSshRelayTarget
} from './helpers/docker-ssh-relay-target'
cleanupDockerSshHostTarget,
execDockerSshHostTargetCommand,
startDockerSshHostTarget,
type DockerSshHostTarget
} from './helpers/docker-ssh-host-target'
import { resolveSshBrowserNetworkExecutionRoute } from '../../src/main/browser/ssh-browser-network-execution-route'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const runDocker = process.env.ORCA_RUN_DOCKER_SSH_BROWSER_E2E === '1'
const executionHost = {
@@ -25,13 +24,13 @@ const executionHost = {
}
describe.runIf(runDocker)('SSH browser network execution route Docker journey', () => {
let target: DockerSshRelayTarget | null = null
let target: DockerSshHostTarget | null = null
let client: Client | null = null
beforeAll(async () => {
ensureDockerSshRelayImage(process.cwd())
target = startDockerSshRelayTarget({ workerIndex: 0 } as TestInfo)
execDockerSshRelayTargetCommand(
ensureDockerSshHostImage(process.cwd())
target = startDockerSshHostTarget({ workerIndex: 0 })
execDockerSshHostTargetCommand(
target,
"grep -q 'remote-only.internal' /etc/hosts || printf '127.0.0.1 remote-only.internal\\n' >> /etc/hosts"
)
@@ -39,9 +38,9 @@ describe.runIf(runDocker)('SSH browser network execution route Docker journey',
"const http=require('http')",
"http.createServer((_request,response)=>response.end('sta-4150-remote-only')).listen(18080,'127.0.0.1')"
].join(';')
execDockerSshRelayTargetCommand(
execDockerSshHostTargetCommand(
target,
`nohup node -e ${shellQuote(server)} >/tmp/sta-4150-http.log 2>&1 </dev/null &`
`nohup node -e ${quotePosixShell(server)} >/tmp/sta-4150-http.log 2>&1 </dev/null &`
)
const waitForServer = [
"const net=require('net')",
@@ -51,7 +50,7 @@ describe.runIf(runDocker)('SSH browser network execution route Docker journey',
"socket.once('error',()=>{socket.destroy();if(Date.now()>=deadline)process.exit(1);setTimeout(probe,25)})}",
'probe()'
].join(';')
execDockerSshRelayTargetCommand(target, `node -e ${shellQuote(waitForServer)}`)
execDockerSshHostTargetCommand(target, `node -e ${quotePosixShell(waitForServer)}`)
client = new Client()
await new Promise<void>((resolve, reject) => {
client!.once('ready', resolve)
@@ -70,7 +69,7 @@ describe.runIf(runDocker)('SSH browser network execution route Docker journey',
if (client) {
client.end()
}
cleanupDockerSshRelayTarget(target)
cleanupDockerSshHostTarget(target)
})
it('resolves and connects the exact remote-only domain on the SSH host', async () => {
@@ -9,7 +9,6 @@ import { convertAndRetain, serverCall } from './helpers/orcad-convert-flow'
import { waitForSessionReady } from './helpers/store'
import { dismissTransientAnnouncement } from './helpers/ssh-config-host-picker'
import { openFileExplorer } from './helpers/file-explorer'
import { shellQuote } from './helpers/docker-ssh-relay-target'
import { startBrowserSplitPageServer } from './helpers/browser-split-page-server'
import {
navigateGuest,
@@ -18,6 +17,7 @@ import {
} from './helpers/browser-split-guest-probes'
import { toRuntimeExecutionHostId } from '../../src/shared/execution-host'
import { getWorktreeHostIdentity } from '../../src/shared/worktree/host-qualified-identity'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const TEMPLATE = process.env.ORCA_E2E_ORCAD_CONVERT_TEMPLATE
test.skip(!TEMPLATE || process.env.ORCA_E2E_SSH_DOCKER !== '1', 'Needs owned Docker host')
@@ -87,10 +87,10 @@ test('managed Files drag cannot load a desktop same-path HTML file', async ({
throw new Error('Missing owned host control')
}
host.exec(
`mkdir -p ${shellQuote(path.dirname(testRepoPath))} && git clone --quiet ${shellQuote(host.remoteRepoPath)} ${shellQuote(testRepoPath)} && printf '%s' '<html><body>REMOTE_BROWSER_DROP_OWNER</body></html>' > ${shellQuote(filePath)}`
`mkdir -p ${quotePosixShell(path.dirname(testRepoPath))} && git clone --quiet ${quotePosixShell(host.remoteRepoPath)} ${quotePosixShell(testRepoPath)} && printf '%s' '<html><body>REMOTE_BROWSER_DROP_OWNER</body></html>' > ${quotePosixShell(filePath)}`
)
host.exec(
`printf '%s' REMOTE_DROP_READY > ${shellQuote(path.join(testRepoPath, 'REMOTE_DROP_READY.txt'))}`
`printf '%s' REMOTE_DROP_READY > ${quotePosixShell(path.join(testRepoPath, 'REMOTE_DROP_READY.txt'))}`
)
const first = await session.launch()
app = first.app
+7 -7
View File
@@ -5,10 +5,10 @@ import { expect, test } from './helpers/orca-app'
import { createRestartSession } from './helpers/orca-restart'
import { waitForSessionReady, waitForStartupWorktreeRefresh } from './helpers/store'
import {
cleanupDockerSshRelayTarget,
DOCKER_SSH_RELAY_REMOTE_REPO_PATH,
startDockerSshRelayTarget
} from './helpers/docker-ssh-relay-target'
cleanupDockerSshHostTarget,
DOCKER_SSH_HOST_REMOTE_REPO_PATH,
startDockerSshHostTarget
} from './helpers/docker-ssh-host-target'
import { seedRelayEraProfile } from './helpers/orcad-upgrade-profile'
import {
startSshRemoteOnlyBrowserFixture,
@@ -58,7 +58,7 @@ async function guestMarker(page: Page, tabId: string): Promise<unknown> {
test('retained and new browser tabs keep SSH routing and login cookies after managed conversion', async (// oxlint-disable-next-line no-empty-pattern -- Owns app launch.
{}, testInfo) => {
test.setTimeout(5 * 60_000)
const target = startDockerSshRelayTarget(testInfo)
const target = startDockerSshHostTarget(testInfo)
const scratch = createRetentionFixtureDirectory()
const template = path.join(scratch, 'template')
const session = createRestartSession(testInfo, { ORCA_ORCAD_TEMPLATE_PATH: template })
@@ -82,7 +82,7 @@ test('retained and new browser tabs keep SSH routing and login cookies after man
identitiesOnly: true,
relayGracePeriodSeconds: 1
},
{ repoPath: DOCKER_SSH_RELAY_REMOTE_REPO_PATH, folderPath: '/tmp' }
{ repoPath: DOCKER_SSH_HOST_REMOTE_REPO_PATH, folderPath: '/tmp' }
)
const launched = await session.launch()
app = launched.app
@@ -366,7 +366,7 @@ test('retained and new browser tabs keep SSH routing and login cookies after man
await session.close(app)
}
await session.dispose()
cleanupDockerSshRelayTarget(target)
cleanupDockerSshHostTarget(target)
rmSync(scratch, { recursive: true, force: true })
}
})
@@ -5,10 +5,10 @@ import { expect, test } from './helpers/orca-app'
import { createRestartSession } from './helpers/orca-restart'
import { waitForSessionReady } from './helpers/store'
import {
cleanupDockerSshRelayTarget,
DOCKER_SSH_RELAY_REMOTE_REPO_PATH,
startDockerSshRelayTarget
} from './helpers/docker-ssh-relay-target'
cleanupDockerSshHostTarget,
DOCKER_SSH_HOST_REMOTE_REPO_PATH,
startDockerSshHostTarget
} from './helpers/docker-ssh-host-target'
import { seedRelayEraProfile } from './helpers/orcad-upgrade-profile'
import {
startSshRemoteOnlyBrowserFixture,
@@ -23,7 +23,7 @@ test.skip(!TEMPLATE || process.env.ORCA_E2E_SSH_DOCKER !== '1', 'Needs Docker an
test('an unavailable browser on a responding managed host does not report a server outage', async (// oxlint-disable-next-line no-empty-pattern -- Owns app launch.
{}, testInfo) => {
test.setTimeout(5 * 60_000)
const target = startDockerSshRelayTarget(testInfo)
const target = startDockerSshHostTarget(testInfo)
const scratch = createRetentionFixtureDirectory()
const template = path.join(scratch, 'template')
const session = createRestartSession(testInfo, { ORCA_ORCAD_TEMPLATE_PATH: template })
@@ -47,7 +47,7 @@ test('an unavailable browser on a responding managed host does not report a serv
identitiesOnly: true,
relayGracePeriodSeconds: 1
},
{ repoPath: DOCKER_SSH_RELAY_REMOTE_REPO_PATH, folderPath: '/tmp' }
{ repoPath: DOCKER_SSH_HOST_REMOTE_REPO_PATH, folderPath: '/tmp' }
)
const launched = await session.launch()
app = launched.app
@@ -168,7 +168,7 @@ test('an unavailable browser on a responding managed host does not report a serv
await session.close(app)
}
await session.dispose()
cleanupDockerSshRelayTarget(target)
cleanupDockerSshHostTarget(target)
rmSync(scratch, { recursive: true, force: true })
}
})
+28 -28
View File
@@ -16,12 +16,12 @@ import { createRestartSession } from './helpers/orca-restart'
import { reconnect } from './helpers/orcad-convert-flow'
import { ORCAD_CONVERT_HOST_ENV } from './helpers/orcad-convert-host'
import {
cleanupDockerSshRelayTarget,
DOCKER_SSH_RELAY_REMOTE_REPO_PATH,
execDockerSshRelayTargetCommand,
startDockerSshRelayTarget,
type DockerSshRelayTarget
} from './helpers/docker-ssh-relay-target'
cleanupDockerSshHostTarget,
DOCKER_SSH_HOST_REMOTE_REPO_PATH,
execDockerSshHostTargetCommand,
startDockerSshHostTarget,
type DockerSshHostTarget
} from './helpers/docker-ssh-host-target'
import { ORCAD_E2E_IDLE_TIMEOUT_ENV } from '../../src/shared/orcad-idle-exit'
const HOST = process.env[ORCAD_CONVERT_HOST_ENV]
@@ -31,8 +31,8 @@ const IDLE_TIMEOUT_MS = 15_000
const RECORD = '/root/.orca/orcad-idle-stop.json'
/** PIDs of running orcad slots; empty once every slot has exited. */
function runningOrcadPids(target: DockerSshRelayTarget): string[] {
return execDockerSshRelayTargetCommand(
function runningOrcadPids(target: DockerSshHostTarget): string[] {
return execDockerSshHostTargetCommand(
target,
'for f in /root/.orca-remote/orcad-*/.orcad-pid; do pid=$(cat "$f" 2>/dev/null) && ' +
'kill -0 "$pid" 2>/dev/null && echo "$pid"; done; true'
@@ -42,8 +42,8 @@ function runningOrcadPids(target: DockerSshRelayTarget): string[] {
.filter(Boolean)
}
function readIdleStopRecord(target: DockerSshRelayTarget): unknown {
const raw = execDockerSshRelayTargetCommand(target, `cat ${RECORD} 2>/dev/null || true`).trim()
function readIdleStopRecord(target: DockerSshHostTarget): unknown {
const raw = execDockerSshHostTargetCommand(target, `cat ${RECORD} 2>/dev/null || true`).trim()
return raw ? JSON.parse(raw) : null
}
@@ -54,7 +54,7 @@ test('a managed orcad stops after idling and starts again on the next connect',
`Set ${ORCAD_CONVERT_HOST_ENV}=docker and ORCA_E2E_ORCAD_CONVERT_TEMPLATE`
)
test.setTimeout(15 * 60_000)
const target = startDockerSshRelayTarget(testInfo)
const target = startDockerSshHostTarget(testInfo)
const session = createRestartSession(testInfo, {
ORCA_ORCAD_TEMPLATE_PATH: TEMPLATE_SOURCE!,
[ORCAD_E2E_IDLE_TIMEOUT_ENV]: String(IDLE_TIMEOUT_MS)
@@ -80,7 +80,7 @@ test('a managed orcad stops after idling and starts again on the next connect',
.catch((error: unknown) => {
// The server logs what kept it up; without it a timeout explains nothing.
console.error(
execDockerSshRelayTargetCommand(target, 'tail -n 40 /root/.orca-remote/orcad-*/orcad.log')
execDockerSshHostTargetCommand(target, 'tail -n 40 /root/.orca-remote/orcad-*/orcad.log')
)
throw error
})
@@ -111,13 +111,13 @@ test('a managed orcad stops after idling and starts again on the next connect',
await session.close(app)
}
await session.dispose()
cleanupDockerSshRelayTarget(target)
cleanupDockerSshHostTarget(target)
}
})
async function connectManagedHost(
page: Page,
target: DockerSshRelayTarget
target: DockerSshHostTarget
): Promise<{ targetId: string; environmentId: string }> {
const connected = await page.evaluate(
async (input) => {
@@ -157,8 +157,8 @@ async function callEnvironment(page: Page, environmentId: string, method: string
}
/** The bracket keeps pgrep from matching the shell that runs it. */
function processAlive(target: DockerSshRelayTarget, marker: string): boolean {
const found = execDockerSshRelayTargetCommand(
function processAlive(target: DockerSshHostTarget, marker: string): boolean {
const found = execDockerSshHostTargetCommand(
target,
`pgrep -f '[s]leep ${marker}' >/dev/null && echo yes || true`
)
@@ -172,7 +172,7 @@ test('a live managed process that does not answer remains unverifiable', async (
'Requires the owned Docker SSH host and template'
)
test.setTimeout(5 * 60_000)
const target = startDockerSshRelayTarget(testInfo)
const target = startDockerSshHostTarget(testInfo)
const session = createRestartSession(testInfo, { ORCA_ORCAD_TEMPLATE_PATH: TEMPLATE_SOURCE! })
let app: ElectronApplication | null = null
let pausedPid: string | null = null
@@ -187,7 +187,7 @@ test('a live managed process that does not answer remains unverifiable', async (
expect(pids).toHaveLength(1)
const pid = pids[0]
expect(pid).toMatch(/^\d+$/)
execDockerSshRelayTargetCommand(target, `kill -STOP ${pid}`)
execDockerSshHostTargetCommand(target, `kill -STOP ${pid}`)
pausedPid = pid
const held = await page.evaluate((targetId) => window.api.ssh.connect({ targetId }), targetId)
console.log(
@@ -200,7 +200,7 @@ test('a live managed process that does not answer remains unverifiable', async (
environmentId,
serving: { state: 'unverifiable' }
})
execDockerSshRelayTargetCommand(target, `kill -CONT ${pid}`)
execDockerSshHostTargetCommand(target, `kill -CONT ${pid}`)
pausedPid = null
const recovered = JSON.parse(await reconnect(page, targetId))
console.log(
@@ -215,13 +215,13 @@ test('a live managed process that does not answer remains unverifiable', async (
expect(runningOrcadPids(target)).toEqual([pid])
} finally {
if (pausedPid) {
execDockerSshRelayTargetCommand(target, `kill -CONT ${pausedPid}`)
execDockerSshHostTargetCommand(target, `kill -CONT ${pausedPid}`)
}
if (app) {
await session.close(app)
}
await session.dispose()
cleanupDockerSshRelayTarget(target)
cleanupDockerSshHostTarget(target)
}
})
@@ -232,7 +232,7 @@ test('a killed managed orcad comes back with its terminal, and starts fresh afte
`Set ${ORCAD_CONVERT_HOST_ENV}=docker and ORCA_E2E_ORCAD_CONVERT_TEMPLATE`
)
test.setTimeout(15 * 60_000)
const target = startDockerSshRelayTarget(testInfo)
const target = startDockerSshHostTarget(testInfo)
const session = createRestartSession(testInfo, { ORCA_ORCAD_TEMPLATE_PATH: TEMPLATE_SOURCE! })
let app: ElectronApplication | null = null
try {
@@ -245,19 +245,19 @@ test('a killed managed orcad comes back with its terminal, and starts fresh afte
const marker = String(800_000 + Math.floor(Math.random() * 100_000))
expect(
await callEnvironment(page, environmentId, 'repo.add', {
path: DOCKER_SSH_RELAY_REMOTE_REPO_PATH
path: DOCKER_SSH_HOST_REMOTE_REPO_PATH
})
).toMatchObject({ ok: true })
expect(
await callEnvironment(page, environmentId, 'terminal.create', {
worktree: `path:${DOCKER_SSH_RELAY_REMOTE_REPO_PATH}`,
worktree: `path:${DOCKER_SSH_HOST_REMOTE_REPO_PATH}`,
command: `exec sleep ${marker}`
})
).toMatchObject({ ok: true })
await expect.poll(() => processAlive(target, marker), { timeout: 60_000 }).toBe(true)
// Killed: the daemon keeps the terminal, and the next call starts orcad, which adopts it.
execDockerSshRelayTargetCommand(
execDockerSshHostTargetCommand(
target,
'kill -TERM $(cat /root/.orca-remote/orcad-*/.orcad-pid)'
)
@@ -272,8 +272,8 @@ test('a killed managed orcad comes back with its terminal, and starts fresh afte
expect(processAlive(target, marker)).toBe(true)
// A reboot takes orcad, the daemon and its terminal; the next connect starts both fresh.
execDockerSshRelayTargetCommand(target, "pkill -KILL -f '/root/[.]orca-remote/' || true")
execDockerSshRelayTargetCommand(target, `pkill -KILL -f '[s]leep ${marker}' || true`)
execDockerSshHostTargetCommand(target, "pkill -KILL -f '/root/[.]orca-remote/' || true")
execDockerSshHostTargetCommand(target, `pkill -KILL -f '[s]leep ${marker}' || true`)
await expect.poll(() => runningOrcadPids(target), { timeout: 60_000 }).toEqual([])
const connected = JSON.parse(await reconnect(page, targetId))
expect(connected).toMatchObject({ kind: 'managed', environmentId })
@@ -288,6 +288,6 @@ test('a killed managed orcad comes back with its terminal, and starts fresh afte
await session.close(app)
}
await session.dispose()
cleanupDockerSshRelayTarget(target)
cleanupDockerSshHostTarget(target)
}
})
+3 -3
View File
@@ -17,9 +17,9 @@ import { seedRelayEraProfile } from './helpers/orcad-upgrade-profile'
import { convertAndRetain, serverCall } from './helpers/orcad-convert-flow'
import { waitForSessionReady } from './helpers/store'
import { dismissTransientAnnouncement } from './helpers/ssh-config-host-picker'
import { shellQuote } from './helpers/docker-ssh-relay-target'
import { toRuntimeExecutionHostId } from '../../src/shared/execution-host'
import { folderWorkspaceKey } from '../../src/shared/workspace-scope'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const TEMPLATE = process.env.ORCA_E2E_ORCAD_CONVERT_TEMPLATE
@@ -52,7 +52,7 @@ for (const surface of [
// This recorder writes argv/content and exits; it never opens or focuses a window.
writeFileSync(
recorder,
`#!/bin/sh\nprintf 'DESKTOP_PID=%s\\nARG=%s\\n' "$$" "$1" > ${shellQuote(receiptPath)}\nif [ -d "$1" ]; then cat "$1/README.md"; else cat "$1"; fi >> ${shellQuote(receiptPath)}\n`
`#!/bin/sh\nprintf 'DESKTOP_PID=%s\\nARG=%s\\n' "$$" "$1" > ${quotePosixShell(receiptPath)}\nif [ -d "$1" ]; then cat "$1/README.md"; else cat "$1"; fi >> ${quotePosixShell(receiptPath)}\n`
)
chmodSync(recorder, 0o755)
const receipt = (): string => (existsSync(receiptPath) ? readFileSync(receiptPath, 'utf8') : '')
@@ -65,7 +65,7 @@ for (const surface of [
}
const execute = host.exec
execute(
`mkdir -p ${shellQuote(owned)} && git clone --quiet ${shellQuote(host.remoteRepoPath)} ${shellQuote(repoPath)} && printf '%s\\n' ${shellQuote(`# ${remoteMarker}`)} > ${shellQuote(filePath)} && mkdir -p ${shellQuote(folderPath)} && printf '%s\\n' ${shellQuote(`# ${remoteMarker}`)} > ${shellQuote(path.join(folderPath, 'README.md'))}`
`mkdir -p ${quotePosixShell(owned)} && git clone --quiet ${quotePosixShell(host.remoteRepoPath)} ${quotePosixShell(repoPath)} && printf '%s\\n' ${quotePosixShell(`# ${remoteMarker}`)} > ${quotePosixShell(filePath)} && mkdir -p ${quotePosixShell(folderPath)} && printf '%s\\n' ${quotePosixShell(`# ${remoteMarker}`)} > ${quotePosixShell(path.join(folderPath, 'README.md'))}`
)
const first = await session.launch()
app = first.app
@@ -10,7 +10,6 @@ import { convertAndRetain, serverCall } from './helpers/orcad-convert-flow'
import { waitForSessionReady } from './helpers/store'
import { dismissTransientAnnouncement } from './helpers/ssh-config-host-picker'
import { openFileExplorer } from './helpers/file-explorer'
import { shellQuote } from './helpers/docker-ssh-relay-target'
import { openTerminalWorkspaceRootLink } from './helpers/terminal-workspace-root-link'
import { toRuntimeExecutionHostId } from '../../src/shared/execution-host'
import { getWorktreeHostIdentity } from '../../src/shared/worktree/host-qualified-identity'
@@ -20,6 +19,7 @@ import {
getTerminalContent,
waitForActivePanePtyId
} from './helpers/terminal'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
const TEMPLATE = process.env.ORCA_E2E_ORCAD_CONVERT_TEMPLATE
test.skip(!TEMPLATE || process.env.ORCA_E2E_SSH_DOCKER !== '1', 'Needs owned Docker host')
@@ -45,7 +45,7 @@ test('a managed terminal path link cannot switch to a desktop workspace at the s
}
const execute = host.exec
execute(
`mkdir -p ${shellQuote(path.dirname(testRepoPath))} && git clone --quiet ${shellQuote(host.remoteRepoPath)} ${shellQuote(testRepoPath)} && printf 'REMOTE_COMMAND_FILE_OWNER\n' > ${shellQuote(filePath)}`
`mkdir -p ${quotePosixShell(path.dirname(testRepoPath))} && git clone --quiet ${quotePosixShell(host.remoteRepoPath)} ${quotePosixShell(testRepoPath)} && printf 'REMOTE_COMMAND_FILE_OWNER\n' > ${quotePosixShell(filePath)}`
)
const first = await session.launch()
app = first.app
@@ -88,7 +88,7 @@ test('a managed terminal path link cannot switch to a desktop workspace at the s
'DESKTOP_TERMINAL_DIRECTORY_OWNER\n'
)
execute(
`mkdir -p ${shellQuote(markerDirectory)} && printf 'REMOTE_TERMINAL_DIRECTORY_OWNER\n' > ${shellQuote(path.join(markerDirectory, 'TERMINAL_OWNER.txt'))}`
`mkdir -p ${quotePosixShell(markerDirectory)} && printf 'REMOTE_TERMINAL_DIRECTORY_OWNER\n' > ${quotePosixShell(path.join(markerDirectory, 'TERMINAL_OWNER.txt'))}`
)
await dismissTransientAnnouncement(page)
const localIdentity = getWorktreeHostIdentity({ id: ids[0], hostId: 'local' })
@@ -173,11 +173,11 @@ test('a managed terminal path link cannot switch to a desktop workspace at the s
.toContain('REMOTE_TERMINAL_DIRECTORY_OWNER')
console.log('[actual-managed-terminal]', await getTerminalContent(page))
execute(
`mkdir -p ${shellQuote(intermediate)} && printf 'REMOTE_LINK_DIRECTORY_OWNER\n' > ${shellQuote(path.join(intermediate, 'LINK_DIRECTORY_OWNER.txt'))}`
`mkdir -p ${quotePosixShell(intermediate)} && printf 'REMOTE_LINK_DIRECTORY_OWNER\n' > ${quotePosixShell(path.join(intermediate, 'LINK_DIRECTORY_OWNER.txt'))}`
)
console.log(
'[actual-host-link-directory-marker]',
execute(`cat ${shellQuote(path.join(intermediate, 'LINK_DIRECTORY_OWNER.txt'))}`)
execute(`cat ${quotePosixShell(path.join(intermediate, 'LINK_DIRECTORY_OWNER.txt'))}`)
)
const foreignTarget = await openTerminalWorkspaceRootLink(page, testInfo, intermediate)
const popover = page.locator('[data-terminal-link-action-popover]')
+31 -31
View File
@@ -4,17 +4,17 @@ import { chmodSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { homedir, tmpdir } from 'node:os'
import path from 'node:path'
import { expect, test } from './helpers/orca-app'
import { ensureDockerSshRelayImage } from './helpers/docker-ssh-relay-image'
import { ensureDockerSshHostImage } from './helpers/docker-ssh-host-image'
import {
cleanupDockerSshRelayTarget,
execDockerSshRelayTargetCommand,
shellQuote,
startDockerSshRelayTarget,
DOCKER_SSH_RELAY_REMOTE_REPO_PATH,
type DockerSshRelayTarget
} from './helpers/docker-ssh-relay-target'
cleanupDockerSshHostTarget,
execDockerSshHostTargetCommand,
startDockerSshHostTarget,
DOCKER_SSH_HOST_REMOTE_REPO_PATH,
type DockerSshHostTarget
} from './helpers/docker-ssh-host-target'
import { ensureTerminalVisible, waitForSessionReady } from './helpers/store'
import { ORCAD_TEMPLATE } from './helpers/orcad-managed-workspace'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
/** A recipe VM's SSH root, served by a managed orcad the first connect deploys. */
test.skip(
@@ -32,11 +32,11 @@ test('adopts a recipe-provisioned SSH root without creating a linked worktree',
}, testInfo) => {
// The first connect deploys the VM's managed server before the root is adopted.
test.setTimeout(420_000)
let target: DockerSshRelayTarget | null = null
let target: DockerSshHostTarget | null = null
const sourceRepo = mkdtempSync(path.join(tmpdir(), 'orca-provisioned-root-source-'))
try {
ensureDockerSshRelayImage(process.cwd())
target = startDockerSshRelayTarget(testInfo)
ensureDockerSshHostImage(process.cwd())
target = startDockerSshHostTarget(testInfo)
const expectedRefHead = seedRecipeRepo(sourceRepo, target)
await waitForSessionReady(orcaPage)
const sourceRepoId = await addRecipeRepo(orcaPage, sourceRepo)
@@ -80,28 +80,28 @@ test('adopts a recipe-provisioned SSH root without creating a linked worktree',
const adopted = await findAdopted()
await ensureTerminalVisible(orcaPage)
expect(adopted).toMatchObject({
path: DOCKER_SSH_RELAY_REMOTE_REPO_PATH,
path: DOCKER_SSH_HOST_REMOTE_REPO_PATH,
isMainWorktree: true,
ephemeralVmCheckoutMode: 'provisioned-root'
})
// The VM's managed server holds the workspace; no relay serves it.
expect(adopted?.hostId).toMatch(/^runtime:/)
expect(
execDockerSshRelayTargetCommand(
execDockerSshHostTargetCommand(
target,
`git -C ${shellQuote(DOCKER_SSH_RELAY_REMOTE_REPO_PATH)} worktree list --porcelain | grep -c '^worktree '`
`git -C ${quotePosixShell(DOCKER_SSH_HOST_REMOTE_REPO_PATH)} worktree list --porcelain | grep -c '^worktree '`
)
).toBe('1')
expect(
execDockerSshRelayTargetCommand(
execDockerSshHostTargetCommand(
target,
`git -C ${shellQuote(DOCKER_SSH_RELAY_REMOTE_REPO_PATH)} branch --show-current`
`git -C ${quotePosixShell(DOCKER_SSH_HOST_REMOTE_REPO_PATH)} branch --show-current`
)
).toBe(workspaceName)
expect(
execDockerSshRelayTargetCommand(
execDockerSshHostTargetCommand(
target,
`git -C ${shellQuote(DOCKER_SSH_RELAY_REMOTE_REPO_PATH)} rev-parse HEAD`
`git -C ${quotePosixShell(DOCKER_SSH_HOST_REMOTE_REPO_PATH)} rev-parse HEAD`
)
).toBe(expectedRefHead)
@@ -130,9 +130,9 @@ test('adopts a recipe-provisioned SSH root without creating a linked worktree',
)
.toBe(false)
const destroyed = target
expect(() => execDockerSshRelayTargetCommand(destroyed, 'true')).toThrow()
expect(() => execDockerSshHostTargetCommand(destroyed, 'true')).toThrow()
} finally {
cleanupDockerSshRelayTarget(target)
cleanupDockerSshHostTarget(target)
rmSync(sourceRepo, { recursive: true, force: true })
}
})
@@ -151,11 +151,11 @@ async function addRecipeRepo(page: Parameters<typeof waitForSessionReady>[0], re
}, repoPath)
}
function seedRecipeRepo(repoPath: string, target: DockerSshRelayTarget): string {
function seedRecipeRepo(repoPath: string, target: DockerSshHostTarget): string {
const createScript = path.join(repoPath, 'create.sh')
const destroyScript = path.join(repoPath, 'destroy.sh')
// The recipe's isolated HOME must still address the engine that owns the fixture container.
const docker = `docker --config ${shellQuote(process.env.DOCKER_CONFIG ?? path.join(homedir(), '.docker'))}`
const docker = `docker --config ${quotePosixShell(process.env.DOCKER_CONFIG ?? path.join(homedir(), '.docker'))}`
writeFileSync(
createScript,
`#!/usr/bin/env bash
@@ -165,9 +165,9 @@ set -euo pipefail
[ -n "\${ORCA_REPO_REF:-}" ]
[ -n "\${ORCA_REPO_REF_HEAD:-}" ]
[ -n "\${ORCA_REPO_BRANCH:-}" ]
${docker} exec ${shellQuote(target.containerName)} git -C ${shellQuote(DOCKER_SSH_RELAY_REMOTE_REPO_PATH)} cat-file -e "$ORCA_REPO_REF_HEAD^{commit}"
${docker} exec ${shellQuote(target.containerName)} git -C ${shellQuote(DOCKER_SSH_RELAY_REMOTE_REPO_PATH)} checkout -B "$ORCA_REPO_BRANCH" "$ORCA_REPO_REF_HEAD" >&2
node -e 'console.log(JSON.stringify({schemaVersion:2,checkoutMode:"provisioned-root",connection:{type:"ssh",projectRoot:process.argv[1],target:{label:"Docker provisioned root",host:process.argv[2],port:Number(process.argv[3]),username:"root",identityFile:process.argv[4],identitiesOnly:true}}}))' ${shellQuote(DOCKER_SSH_RELAY_REMOTE_REPO_PATH)} ${shellQuote(target.host)} ${target.port} ${shellQuote(target.identityFile)}
${docker} exec ${quotePosixShell(target.containerName)} git -C ${quotePosixShell(DOCKER_SSH_HOST_REMOTE_REPO_PATH)} cat-file -e "$ORCA_REPO_REF_HEAD^{commit}"
${docker} exec ${quotePosixShell(target.containerName)} git -C ${quotePosixShell(DOCKER_SSH_HOST_REMOTE_REPO_PATH)} checkout -B "$ORCA_REPO_BRANCH" "$ORCA_REPO_REF_HEAD" >&2
node -e 'console.log(JSON.stringify({schemaVersion:2,checkoutMode:"provisioned-root",connection:{type:"ssh",projectRoot:process.argv[1],target:{label:"Docker provisioned root",host:process.argv[2],port:Number(process.argv[3]),username:"root",identityFile:process.argv[4],identitiesOnly:true}}}))' ${quotePosixShell(DOCKER_SSH_HOST_REMOTE_REPO_PATH)} ${quotePosixShell(target.host)} ${target.port} ${quotePosixShell(target.identityFile)}
`
)
writeFileSync(
@@ -175,7 +175,7 @@ node -e 'console.log(JSON.stringify({schemaVersion:2,checkoutMode:"provisioned-r
`#!/usr/bin/env bash
set -euo pipefail
cat >/dev/null
${docker} rm -f ${shellQuote(target.containerName)} >/dev/null
${docker} rm -f ${quotePosixShell(target.containerName)} >/dev/null
`
)
chmodSync(createScript, 0o755)
@@ -202,18 +202,18 @@ ${docker} rm -f ${shellQuote(target.containerName)} >/dev/null
cwd: repoPath,
encoding: 'utf8'
}).trim()
execDockerSshRelayTargetCommand(
execDockerSshHostTargetCommand(
target,
`rm -rf ${shellQuote(DOCKER_SSH_RELAY_REMOTE_REPO_PATH)} && mkdir -p ${shellQuote(DOCKER_SSH_RELAY_REMOTE_REPO_PATH)}`
`rm -rf ${quotePosixShell(DOCKER_SSH_HOST_REMOTE_REPO_PATH)} && mkdir -p ${quotePosixShell(DOCKER_SSH_HOST_REMOTE_REPO_PATH)}`
)
execFileSync('docker', [
'cp',
`${repoPath}${path.sep}.`,
`${target.containerName}:${DOCKER_SSH_RELAY_REMOTE_REPO_PATH}`
`${target.containerName}:${DOCKER_SSH_HOST_REMOTE_REPO_PATH}`
])
execDockerSshRelayTargetCommand(
execDockerSshHostTargetCommand(
target,
`chown -R root:root ${shellQuote(DOCKER_SSH_RELAY_REMOTE_REPO_PATH)}`
`chown -R root:root ${quotePosixShell(DOCKER_SSH_HOST_REMOTE_REPO_PATH)}`
)
return expectedRefHead
}
+5 -5
View File
@@ -5,7 +5,6 @@
*/
import { expect, test } from './helpers/orca-app'
import { reconnect } from './helpers/orcad-convert-flow'
import { shellQuote } from './helpers/docker-ssh-relay-target'
import { callEnvironment, createPairedHostTerminal } from './helpers/paired-host-terminal'
import { reserveLocalPort } from './helpers/reserve-local-port'
import { openTerminalWorkspaceRootLink } from './helpers/terminal-workspace-root-link'
@@ -18,6 +17,7 @@ import {
typeInTerminal,
withManagedHost
} from './helpers/orcad-managed-workspace'
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
test.skip(
!ORCAD_TEMPLATE || process.env.ORCA_E2E_SSH_DOCKER !== '1',
@@ -29,7 +29,7 @@ test('Quick Open finds a file in a monorepo-sized managed workspace', async (//
{}, testInfo) => {
test.setTimeout(10 * 60_000)
await withManagedHost(testInfo, async (fixture) => {
const repo = shellQuote(fixture.host.remoteRepoPath)
const repo = quotePosixShell(fixture.host.remoteRepoPath)
// ~22.6k tracked paths: past the size whose single reply once overflowed the transport (#12547).
fixture.host.exec!(
[
@@ -70,7 +70,7 @@ for (const acrossReconnect of [false, true]) {
const script = `require('node:http').createServer((_q, r) => r.end(${JSON.stringify(marker)})).listen(${remotePort}, '127.0.0.1')`
// Started from the workspace so the scan attributes it to this workspace.
fixture.host.exec!(
`cd ${shellQuote(fixture.host.remoteRepoPath)} && nohup node -e ${shellQuote(script)} >/tmp/orca-http.log 2>&1 < /dev/null &`
`cd ${quotePosixShell(fixture.host.remoteRepoPath)} && nohup node -e ${quotePosixShell(script)} >/tmp/orca-http.log 2>&1 < /dev/null &`
)
// The scan the Ports panel shows for a workspace on this host.
await expect
@@ -120,7 +120,7 @@ test('a terminal link opens an image outside the managed workspace', async (// o
'iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVR4AWN8z8DwnwEJMDGgAcICAO2mBAXmO4drAAAAAElFTkSuQmCC'
await withManagedHost(testInfo, async (fixture) => {
fixture.host.exec!(
`printf '%s' ${shellQuote(imageBase64)} | base64 -d > ${shellQuote(imagePath)}`
`printf '%s' ${quotePosixShell(imageBase64)} | base64 -d > ${quotePosixShell(imagePath)}`
)
// Why /root: the default folder workspace is /tmp, which would make the image a workspace file.
const { page, seeded } = await launchManagedWorkspace(fixture, { folderPath: '/root' })
@@ -154,7 +154,7 @@ test('a managed terminal reports agent status from hooks and from Pi-style title
'--data-urlencode "paneKey=${ORCA_PANE_KEY}" --data-urlencode "tabId=${ORCA_TAB_ID}"',
'--data-urlencode "worktreeId=${ORCA_WORKTREE_ID}" --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}"',
'--data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}"',
`--data-urlencode ${shellQuote(`payload=${payload}`)} >/tmp/orca-hook.log 2>&1 ) &`
`--data-urlencode ${quotePosixShell(`payload=${payload}`)} >/tmp/orca-hook.log 2>&1 ) &`
].join(' ')
)
await expect
+2 -5
View File
@@ -1,11 +1,8 @@
import { quotePowerShellLiteral } from '../../src/shared/powershell-native-argument'
function quotePosixShellArg(value: string): string {
return `'${value.replaceAll("'", "'\\''")}'`
}
import { quotePosixShell } from '../../src/shared/wsl-login-shell-command'
function quoteTerminalArg(value: string): string {
return process.platform === 'win32' ? quotePowerShellLiteral(value) : quotePosixShellArg(value)
return process.platform === 'win32' ? quotePowerShellLiteral(value) : quotePosixShell(value)
}
export function nodeTerminalCommand(args: readonly string[]): string {