Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions bin/gstack-gbrain-sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1145,25 +1145,35 @@ function runBrainSyncPush(args: CliArgs): StageResult {
return { name: "brain-sync", ran: false, ok: true, duration_ms: 0, summary: "skipped (gstack-brain-sync not installed)" };
}

// #1731: gstack-brain-sync is a bash shebang script; Windows can't spawn it
// without a shell, which surfaced as "brain-sync exited undefined".
spawnSync(brainSyncPath, ["--discover-new"], {
stdio: args.quiet ? ["ignore", "ignore", "ignore"] : ["ignore", "inherit", "inherit"],
timeout: 60 * 1000,
shell: NEEDS_SHELL_ON_WINDOWS,
});
const result = spawnSync(brainSyncPath, ["--once"], {
stdio: args.quiet ? ["ignore", "ignore", "ignore"] : ["ignore", "inherit", "inherit"],
timeout: 60 * 1000,
shell: NEEDS_SHELL_ON_WINDOWS,
});
// #1731 gated these spawns behind `shell: NEEDS_SHELL_ON_WINDOWS`, but
// cmd.exe cannot execute an extensionless bash shebang script either — the
// stage errored "'gstack-brain-sync' is not recognized as an internal or
// external command". Invoke the script through bash explicitly on Windows
// (Git Bash ships with git, a hard gstack dependency); POSIX keeps the
// direct shebang exec.
const runBrainSync = (flag: string) =>
spawnSync(
NEEDS_SHELL_ON_WINDOWS ? "bash" : brainSyncPath,
NEEDS_SHELL_ON_WINDOWS ? [brainSyncPath, flag] : [flag],
{
stdio: args.quiet ? ["ignore", "ignore", "ignore"] : ["ignore", "inherit", "inherit"],
timeout: 60 * 1000,
},
);
runBrainSync("--discover-new");
const result = runBrainSync("--once");

return {
name: "brain-sync",
ran: true,
ok: result.status === 0,
duration_ms: Date.now() - t0,
summary: result.status === 0 ? "curated artifacts pushed" : `gstack-brain-sync exited ${result.status}`,
summary:
result.status === 0
? "curated artifacts pushed"
: result.error
? `gstack-brain-sync spawn failed (${result.error.message})`
: `gstack-brain-sync exited ${result.status}`,
};
}

Expand Down
14 changes: 8 additions & 6 deletions test/gbrain-spawn-windows-shell.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ describe("#1731 gbrain spawns carry the Windows shell flag", () => {
});
}

test("orchestrator brain-sync spawns carry the Windows shell flag", () => {
test("orchestrator brain-sync spawns route through bash on Windows", () => {
const src = read("bin/gstack-gbrain-sync.ts");
const brainSyncSpawns = src.match(/spawnSync\(brainSyncPath,/g)?.length ?? 0;
expect(brainSyncSpawns).toBe(2);
// Both spawnSync(brainSyncPath, ...) blocks must include the shell flag.
const withShell = src.match(/spawnSync\(brainSyncPath,[\s\S]*?shell:\s*NEEDS_SHELL_ON_WINDOWS/g)?.length ?? 0;
expect(withShell).toBe(2);
// cmd.exe (shell: true) cannot execute an extensionless bash shebang
// script, so the brain-sync invocations must exec bash explicitly on
// Windows instead of relying on the shell flag.
expect(src).toMatch(/NEEDS_SHELL_ON_WINDOWS \? "bash" : brainSyncPath/);
expect(src).toMatch(/NEEDS_SHELL_ON_WINDOWS \? \[brainSyncPath, flag\] : \[flag\]/);
// And no brain-sync spawn may still route through cmd via shell:true.
expect(src).not.toMatch(/spawnSync\(brainSyncPath,[\s\S]{0,300}?shell:\s*NEEDS_SHELL_ON_WINDOWS/);
});
});