Fix Windows setup hook script (#2106)

This commit is contained in:
Jinwoo Hong 2026-05-16 17:44:42 -04:00 committed by GitHub
parent c017229149
commit b8de007f03
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 185 additions and 25 deletions

View File

@ -0,0 +1,64 @@
#!/usr/bin/env node
import { accessSync, constants, existsSync } from 'node:fs'
import { spawnSync } from 'node:child_process'
import { resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
function isExecutable(filePath, platform, access = accessSync) {
if (platform === 'win32') {
return true
}
try {
access(filePath, constants.X_OK)
return true
} catch {
return false
}
}
function quoteWindowsArg(value) {
return `"${value.replace(/"/g, '""')}"`
}
function spawnOptionalSetup(spawn, setupPath, worktreePath, platform, env) {
if (platform === 'win32') {
spawn(
env.ComSpec || 'cmd.exe',
['/d', '/s', '/c', `call ${quoteWindowsArg(setupPath)} ${quoteWindowsArg(worktreePath)}`],
{
stdio: 'inherit',
windowsVerbatimArguments: true
}
)
return
}
spawn(setupPath, [worktreePath], {
stdio: 'inherit'
})
}
export function runInternalDevSetup({
env = process.env,
cwd = process.cwd(),
platform = process.platform,
exists = existsSync,
access = accessSync,
spawn = spawnSync
} = {}) {
const setupPath = env.ORCA_INTERNAL_DEV_SETUP?.trim()
if (!setupPath || !exists(setupPath) || !isExecutable(setupPath, platform, access)) {
return 0
}
// Why: this hook is an optional local accelerator; failures should not block
// creating a worktree or running the normal dependency install.
spawnOptionalSetup(spawn, setupPath, env.ORCA_WORKTREE_PATH || cwd, platform, env)
return 0
}
if (process.argv[1] && resolve(fileURLToPath(import.meta.url)) === resolve(process.argv[1])) {
process.exit(runInternalDevSetup())
}

View File

@ -0,0 +1,85 @@
import { describe, expect, it, vi } from 'vitest'
import { runInternalDevSetup } from './run-internal-dev-setup.mjs'
describe('runInternalDevSetup', () => {
it('does nothing when ORCA_INTERNAL_DEV_SETUP is unset', () => {
const spawn = vi.fn()
expect(runInternalDevSetup({ env: {}, exists: vi.fn(), spawn })).toBe(0)
expect(spawn).not.toHaveBeenCalled()
})
it('does nothing when the configured setup path does not exist', () => {
const spawn = vi.fn()
expect(
runInternalDevSetup({
env: { ORCA_INTERNAL_DEV_SETUP: '/tmp/missing' },
exists: () => false,
spawn
})
).toBe(0)
expect(spawn).not.toHaveBeenCalled()
})
it('skips non-executable setup paths on POSIX', () => {
const spawn = vi.fn()
expect(
runInternalDevSetup({
env: { ORCA_INTERNAL_DEV_SETUP: '/tmp/setup' },
platform: 'linux',
exists: () => true,
access: () => {
throw new Error('EACCES')
},
spawn
})
).toBe(0)
expect(spawn).not.toHaveBeenCalled()
})
it('runs the optional setup with the worktree path and ignores its exit status', () => {
const spawn = vi.fn(() => ({ status: 1 }))
expect(
runInternalDevSetup({
env: {
ORCA_INTERNAL_DEV_SETUP: '/tmp/setup',
ORCA_WORKTREE_PATH: '/tmp/worktree'
},
platform: 'linux',
exists: () => true,
access: () => undefined,
spawn
})
).toBe(0)
expect(spawn).toHaveBeenCalledWith('/tmp/setup', ['/tmp/worktree'], {
stdio: 'inherit'
})
})
it('uses cmd.exe with explicit quoting so .cmd setup shims receive paths with spaces', () => {
const spawn = vi.fn(() => ({ status: 0 }))
expect(
runInternalDevSetup({
env: {
ORCA_INTERNAL_DEV_SETUP: 'C:\\tools\\setup.cmd',
ORCA_WORKTREE_PATH: 'C:\\repo\\worktree'
},
platform: 'win32',
exists: () => true,
spawn
})
).toBe(0)
expect(spawn).toHaveBeenCalledWith(
'cmd.exe',
['/d', '/s', '/c', 'call "C:\\tools\\setup.cmd" "C:\\repo\\worktree"'],
{
stdio: 'inherit',
windowsVerbatimArguments: true
}
)
})
})

View File

@ -1,5 +1,4 @@
scripts:
setup: |
[ -n "$ORCA_INTERNAL_DEV_SETUP" ] && [ -x "$ORCA_INTERNAL_DEV_SETUP" ] && \
"$ORCA_INTERNAL_DEV_SETUP" "$ORCA_WORKTREE_PATH" || true
node config/scripts/run-internal-dev-setup.mjs
pnpm install

View File

@ -185,33 +185,45 @@ describe('createIssueCommandRunnerScript', () => {
it('writes a POSIX runner under the worktree git dir for long issue commands', async () => {
const fs = await import('fs')
const originalPlatform = process.platform
execFileSyncMock.mockReturnValue(
'/test/repo/.git/worktrees/feature/orca/issue-command-runner.sh'
)
const { createIssueCommandRunnerScript } = await import('./hooks')
const result = createIssueCommandRunnerScript(
makeRepo(),
'/test/repo-feature',
'codex exec "long command"\nclaude -p "review it"'
)
expect(result).toEqual({
runnerScriptPath: '/test/repo/.git/worktrees/feature/orca/issue-command-runner.sh',
envVars: expect.objectContaining({
ORCA_ROOT_PATH: '/test/repo',
ORCA_WORKTREE_PATH: '/test/repo-feature'
})
Object.defineProperty(process, 'platform', {
configurable: true,
value: 'linux'
})
expect(vi.mocked(fs.writeFileSync)).toHaveBeenCalledWith(
'/test/repo/.git/worktrees/feature/orca/issue-command-runner.sh',
'#!/usr/bin/env bash\nset -e\ncodex exec "long command"\nclaude -p "review it"\n',
'utf-8'
)
expect(vi.mocked(fs.chmodSync)).toHaveBeenCalledWith(
'/test/repo/.git/worktrees/feature/orca/issue-command-runner.sh',
0o755
)
try {
const { createIssueCommandRunnerScript } = await import('./hooks')
const result = createIssueCommandRunnerScript(
makeRepo(),
'/test/repo-feature',
'codex exec "long command"\nclaude -p "review it"'
)
expect(result).toEqual({
runnerScriptPath: '/test/repo/.git/worktrees/feature/orca/issue-command-runner.sh',
envVars: expect.objectContaining({
ORCA_ROOT_PATH: '/test/repo',
ORCA_WORKTREE_PATH: '/test/repo-feature'
})
})
expect(vi.mocked(fs.writeFileSync)).toHaveBeenCalledWith(
'/test/repo/.git/worktrees/feature/orca/issue-command-runner.sh',
'#!/usr/bin/env bash\nset -e\ncodex exec "long command"\nclaude -p "review it"\n',
'utf-8'
)
expect(vi.mocked(fs.chmodSync)).toHaveBeenCalledWith(
'/test/repo/.git/worktrees/feature/orca/issue-command-runner.sh',
0o755
)
} finally {
Object.defineProperty(process, 'platform', {
configurable: true,
value: originalPlatform
})
}
})
})