feat(core,tooling): Windows support — shell selection, install.ps1, win-x64 release package, Windows CI (#79)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Unit tests for the command-session shell resolver (pure function; platform, env and the
|
||||
* PATH probe are injected — no real shells are spawned here).
|
||||
*/
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveShell } from "../src/environment/tools/command/shell.js";
|
||||
|
||||
const POWERSHELL_ARGS = ["-NoLogo", "-NoProfile", "-Command"];
|
||||
|
||||
/** A whichAll stub resolving only the given names (value = returned PATH matches). */
|
||||
function which(table: Record<string, string[]>): (cmd: string) => string[] {
|
||||
return (cmd) => table[cmd] ?? [];
|
||||
}
|
||||
|
||||
describe("resolveShell — POSIX", () => {
|
||||
it("uses bash -lc on linux without probing (today's behavior, unchanged)", () => {
|
||||
let probed = false;
|
||||
const shell = resolveShell({
|
||||
platform: "linux",
|
||||
env: {},
|
||||
whichAll: () => {
|
||||
probed = true;
|
||||
return [];
|
||||
},
|
||||
});
|
||||
expect(shell).toEqual({ command: "bash", args: ["-lc"], name: "bash" });
|
||||
expect(probed).toBe(false);
|
||||
});
|
||||
|
||||
it("uses bash -lc on darwin", () => {
|
||||
const shell = resolveShell({ platform: "darwin", env: {} });
|
||||
expect(shell).toEqual({ command: "bash", args: ["-lc"], name: "bash" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveShell — win32 probing", () => {
|
||||
it("prefers bash on PATH (Git for Windows)", () => {
|
||||
const shell = resolveShell({
|
||||
platform: "win32",
|
||||
env: {},
|
||||
whichAll: which({
|
||||
bash: ["C:\\Program Files\\Git\\bin\\bash.exe"],
|
||||
pwsh: ["C:\\Program Files\\PowerShell\\7\\pwsh.exe"],
|
||||
}),
|
||||
});
|
||||
expect(shell).toEqual({ command: "bash", args: ["-lc"], name: "bash" });
|
||||
});
|
||||
|
||||
it("skips the WSL launcher bash under the system root and falls through to pwsh", () => {
|
||||
const shell = resolveShell({
|
||||
platform: "win32",
|
||||
env: { SystemRoot: "C:\\WINDOWS" },
|
||||
whichAll: which({
|
||||
bash: ["C:\\Windows\\System32\\bash.exe"],
|
||||
pwsh: ["C:\\Program Files\\PowerShell\\7\\pwsh.exe"],
|
||||
}),
|
||||
});
|
||||
expect(shell).toEqual({ command: "pwsh", args: POWERSHELL_ARGS, name: "pwsh" });
|
||||
});
|
||||
|
||||
it("falls back to pwsh when bash is absent", () => {
|
||||
const shell = resolveShell({
|
||||
platform: "win32",
|
||||
env: {},
|
||||
whichAll: which({ pwsh: ["C:\\Program Files\\PowerShell\\7\\pwsh.exe"] }),
|
||||
});
|
||||
expect(shell).toEqual({ command: "pwsh", args: POWERSHELL_ARGS, name: "pwsh" });
|
||||
});
|
||||
|
||||
it("falls back to powershell when neither bash nor pwsh resolve", () => {
|
||||
const shell = resolveShell({ platform: "win32", env: {}, whichAll: which({}) });
|
||||
expect(shell).toEqual({ command: "powershell", args: POWERSHELL_ARGS, name: "powershell" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveShell — PENGUIN_SHELL override", () => {
|
||||
it("wins on every platform and keeps POSIX-style args for a POSIX shell path", () => {
|
||||
const shell = resolveShell({
|
||||
platform: "linux",
|
||||
env: { PENGUIN_SHELL: "/usr/bin/zsh" },
|
||||
});
|
||||
expect(shell).toEqual({ command: "/usr/bin/zsh", args: ["-lc"], name: "zsh" });
|
||||
});
|
||||
|
||||
it("uses PowerShell-style args when the basename is pwsh (case/extension-insensitive)", () => {
|
||||
const shell = resolveShell({
|
||||
platform: "win32",
|
||||
env: { PENGUIN_SHELL: "C:\\Program Files\\PowerShell\\7\\pwsh.EXE" },
|
||||
whichAll: which({ bash: ["C:\\Program Files\\Git\\bin\\bash.exe"] }),
|
||||
});
|
||||
expect(shell).toEqual({
|
||||
command: "C:\\Program Files\\PowerShell\\7\\pwsh.EXE",
|
||||
args: POWERSHELL_ARGS,
|
||||
name: "pwsh",
|
||||
});
|
||||
});
|
||||
|
||||
it("uses PowerShell-style args for a bare powershell name", () => {
|
||||
const shell = resolveShell({ platform: "win32", env: { PENGUIN_SHELL: "powershell" } });
|
||||
expect(shell).toEqual({ command: "powershell", args: POWERSHELL_ARGS, name: "powershell" });
|
||||
});
|
||||
|
||||
it("uses cmd-style args when the basename is cmd", () => {
|
||||
const shell = resolveShell({ platform: "win32", env: { PENGUIN_SHELL: "cmd" } });
|
||||
expect(shell).toEqual({ command: "cmd", args: ["/d", "/s", "/c"], name: "cmd" });
|
||||
});
|
||||
|
||||
it("ignores a blank PENGUIN_SHELL", () => {
|
||||
const shell = resolveShell({ platform: "linux", env: { PENGUIN_SHELL: " " } });
|
||||
expect(shell).toEqual({ command: "bash", args: ["-lc"], name: "bash" });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user