Files
orca/src/shared/setup-script-shebang.test.ts
T
OrcaWin 1f37e5ac54 fix(worktrees): stop silently switching existing Windows setup scripts to Git Bash
#6967 derived the Windows setup-runner shell from `terminalWindowsShell`. On
upgrade, any Windows user whose terminal preference resolved to Git Bash had
their existing `orca.yaml` setup script (and issue command) handed to bash
instead of cmd.exe. Scripts authored against the cmd runner — `copy`, `xcopy`,
`set VAR=value`, `if errorlevel 1`, `%VAR%`, backslash paths — broke with no
migration and no warning, and the failure looked like Orca broke the project.

The conflation is also wrong in the steady state: a terminal preference is
per-user, so two people on the same repo got different interpreters for the
same orca.yaml and no project could write a setup script that worked for all
of its Windows contributors.

The interpreter is now a property of the script, declared the standard way:
a leading `#!` line. Native Windows keeps the historical `.cmd` runner unless
the script declares a POSIX shell, so no existing script changes behavior.
`resolveSetupRunnerShell` keeps its role as the feasibility gate — a bash
runner still requires the terminal to resolve to Git Bash, since the launch
command is typed into that shell and uses MSYS `/c/...` paths.

`buildWindowsRunnerScript` now drops a leading `#!` line rather than `call`ing
it, so a declared-bash script that falls back to cmd (Git Bash missing) fails
on a real setup line instead of aborting on errorlevel at line one.

WSL worktrees, POSIX platforms, and SSH hosts are untouched.
2026-08-03 22:36:31 -04:00

41 lines
1.8 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { isShebangLine, scriptDeclaresPosixShell } from './setup-script-shebang'
describe('scriptDeclaresPosixShell', () => {
it('accepts the common env and absolute-path forms', () => {
expect(scriptDeclaresPosixShell('#!/usr/bin/env bash\npnpm install')).toBe(true)
expect(scriptDeclaresPosixShell('#!/bin/sh -e\npnpm install')).toBe(true)
expect(scriptDeclaresPosixShell('#!/usr/bin/env -S bash -euo pipefail\npnpm install')).toBe(
true
)
expect(scriptDeclaresPosixShell('#!/bin/zsh')).toBe(true)
})
it('rejects scripts with no interpreter line', () => {
// Regression: batch-syntax setup scripts must stay on the cmd runner.
expect(scriptDeclaresPosixShell('copy .env.example .env\nxcopy /E assets dist')).toBe(false)
expect(scriptDeclaresPosixShell('')).toBe(false)
expect(scriptDeclaresPosixShell('pnpm install\n#!/usr/bin/env bash')).toBe(false)
expect(scriptDeclaresPosixShell('# !/usr/bin/env bash\npnpm install')).toBe(false)
})
it('rejects interpreters that are not POSIX shells', () => {
expect(scriptDeclaresPosixShell('#!/usr/bin/env node\nconsole.log(1)')).toBe(false)
expect(scriptDeclaresPosixShell('#!/usr/bin/env python3\nprint(1)')).toBe(false)
})
it('tolerates CRLF and Windows-style interpreter paths', () => {
expect(scriptDeclaresPosixShell('#!/usr/bin/env bash\r\npnpm install')).toBe(true)
expect(scriptDeclaresPosixShell('#!C:\\tools\\git\\bin\\bash.exe\r\npnpm install')).toBe(true)
})
})
describe('isShebangLine', () => {
it('detects interpreter lines regardless of leading whitespace', () => {
expect(isShebangLine('#!/usr/bin/env bash')).toBe(true)
expect(isShebangLine(' #!/bin/sh')).toBe(true)
expect(isShebangLine('#comment')).toBe(false)
expect(isShebangLine('pnpm install')).toBe(false)
})
})