Fix Windows setup hook script (#2106)
This commit is contained in:
parent
c017229149
commit
b8de007f03
|
|
@ -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())
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue