stabilize native Windows integration gates

This commit is contained in:
Sinabina
2026-07-17 14:41:24 -07:00
parent 6d3eb795e8
commit a8a5fa1aa3
4 changed files with 24 additions and 9 deletions
+1 -1
View File
@@ -25,7 +25,7 @@
"server": "bun run browse/src/server.ts", "server": "bun run browse/src/server.ts",
"test": "bun run scripts/test-free-strict.ts", "test": "bun run scripts/test-free-strict.ts",
"check:gstack2-generated": "bun run scripts/gstack2/check-generated.ts", "check:gstack2-generated": "bun run scripts/gstack2/check-generated.ts",
"test:gstack2": "bun run gen:gstack2 && bun run check:gstack2-generated && bun test test/gstack2-*.test.ts", "test:gstack2": "bun run gen:gstack2 && bun run check:gstack2-generated && bun test --timeout 30000 test/gstack2-*.test.ts",
"test:gstack2:install": "bun run scripts/gstack2/test-install-matrix.ts --full", "test:gstack2:install": "bun run scripts/gstack2/test-install-matrix.ts --full",
"test:gstack2:parity": "bun run ensure:gstack2-runtime && bun run scripts/gstack2/run-parity.ts", "test:gstack2:parity": "bun run ensure:gstack2-runtime && bun run scripts/gstack2/run-parity.ts",
"test:free": "bun run scripts/test-free-shards.ts", "test:free": "bun run scripts/test-free-shards.ts",
+5 -1
View File
@@ -227,7 +227,11 @@ describe("gstack 2 host-neutral paths and state", () => {
const repo = path.join(root, "main repo"); const repo = path.join(root, "main repo");
const linked = path.join(root, "linked worktree"); const linked = path.join(root, "linked worktree");
await fs.mkdir(repo); await fs.mkdir(repo);
const git = (args: string[], cwd = repo) => spawnSync("git", args, { cwd, encoding: "utf8" }); const git = (args: string[], cwd = repo) => spawnSync("git", args, {
cwd,
encoding: "utf8",
timeout: 15_000,
});
expect(git(["init", "--quiet", "-b", "main"]).status).toBe(0); expect(git(["init", "--quiet", "-b", "main"]).status).toBe(0);
git(["config", "user.email", "runtime@example.com"]); git(["config", "user.email", "runtime@example.com"]);
git(["config", "user.name", "Runtime Test"]); git(["config", "user.name", "Runtime Test"]);
+7 -1
View File
@@ -27,7 +27,12 @@ async function fixture() {
} }
afterEach(async () => { afterEach(async () => {
await Promise.all(temporaryRoots.splice(0).map((root) => fs.rm(root, { recursive: true, force: true }))); await Promise.all(temporaryRoots.splice(0).map((root) => fs.rm(root, {
recursive: true,
force: true,
maxRetries: 5,
retryDelay: 50,
})));
}); });
describe('gstack state external-effect CLI', () => { describe('gstack state external-effect CLI', () => {
@@ -37,6 +42,7 @@ describe('gstack state external-effect CLI', () => {
const git = async (args: string[], workingDirectory = cwd) => (await execFile('git', args, { const git = async (args: string[], workingDirectory = cwd) => (await execFile('git', args, {
cwd: workingDirectory, cwd: workingDirectory,
encoding: 'utf8', encoding: 'utf8',
timeout: 15_000,
})).stdout.trim(); })).stdout.trim();
await git(['init']); await git(['init']);
await git(['config', 'user.name', 'GStack Test']); await git(['config', 'user.name', 'GStack Test']);
+11 -6
View File
@@ -24,6 +24,7 @@ const ENTRIES = [
]; ];
const CAPABILITIES = { "fixture-tool": "cap/tool" }; const CAPABILITIES = { "fixture-tool": "cap/tool" };
const REPO_ROOT = path.resolve(import.meta.dir, ".."); const REPO_ROOT = path.resolve(import.meta.dir, "..");
const FULL_RUNTIME_TEST_TIMEOUT_MS = process.platform === "win32" ? 120_000 : 30_000;
describe("GStack 2 managed runtime installer", () => { describe("GStack 2 managed runtime installer", () => {
test("installs, validates, activates, and writes an uninstall-friendly manifest", async () => { test("installs, validates, activates, and writes an uninstall-friendly manifest", async () => {
@@ -194,7 +195,7 @@ describe("GStack 2 managed runtime installer", () => {
], { capture: true, env: { ...process.env, GSTACK_HOME: home } }); ], { capture: true, env: { ...process.env, GSTACK_HOME: home } });
expect(await fs.readFile(path.join(home, "security", "semantic-reviews.jsonl"), "utf8")).toContain('"outcome":"clean"'); expect(await fs.readFile(path.join(home, "security", "semantic-reviews.jsonl"), "utf8")).toContain('"outcome":"clean"');
}, { createDefaultSource: false }); }, { createDefaultSource: false });
}, 30_000); }, FULL_RUNTIME_TEST_TIMEOUT_MS);
test("bounded subprocess execution confirms a timed-out command has exited", async () => { test("bounded subprocess execution confirms a timed-out command has exited", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "gstack-command-timeout-")); const root = await fs.mkdtemp(path.join(os.tmpdir(), "gstack-command-timeout-"));
@@ -428,9 +429,13 @@ describe("GStack 2 managed runtime installer", () => {
await installFixture(source, home, "1.0.0"); await installFixture(source, home, "1.0.0");
const pointer = await readJson(path.join(home, "versions", "current.json")); const pointer = await readJson(path.join(home, "versions", "current.json"));
const manifest = await fs.readFile(path.join(home, "runtime-install.json")); const manifest = await fs.readFile(path.join(home, "runtime-install.json"));
const windowsLauncher = await fs.readFile(path.join(home, "bin", "gstack.cmd")); // Keep the launcher for this host executable so it can enter the shared
// recovery path. The transaction restores the inactive host variant.
const recoverableLauncher = process.platform === "win32" ? "gstack" : "gstack.cmd";
const launcherPath = path.join(home, "bin", recoverableLauncher);
const launcherBefore = await fs.readFile(launcherPath);
await fs.writeFile(path.join(home, "runtime-install.json"), '{"activeVersion":"crashed"}\n'); await fs.writeFile(path.join(home, "runtime-install.json"), '{"activeVersion":"crashed"}\n');
await fs.writeFile(path.join(home, "bin", "gstack.cmd"), "candidate launcher\n"); await fs.writeFile(launcherPath, "candidate launcher\n");
await fs.writeFile(path.join(home, "versions", "current.json"), `${JSON.stringify({ await fs.writeFile(path.join(home, "versions", "current.json"), `${JSON.stringify({
schemaVersion: 2, schemaVersion: 2,
status: "active", status: "active",
@@ -447,7 +452,7 @@ describe("GStack 2 managed runtime installer", () => {
previousPointer: pointer, previousPointer: pointer,
files: [ files: [
{ path: "runtime-install.json", existed: true, mode: 0o600, dataBase64: manifest.toString("base64") }, { path: "runtime-install.json", existed: true, mode: 0o600, dataBase64: manifest.toString("base64") },
{ path: "bin/gstack.cmd", existed: true, mode: 0o644, dataBase64: windowsLauncher.toString("base64") }, { path: `bin/${recoverableLauncher}`, existed: true, mode: 0o644, dataBase64: launcherBefore.toString("base64") },
], ],
}, null, 2)}\n`, { mode: 0o600 }); }, null, 2)}\n`, { mode: 0o600 });
const orphanedLock = `${home}.runtime-lifecycle.lock`; const orphanedLock = `${home}.runtime-lifecycle.lock`;
@@ -462,7 +467,7 @@ describe("GStack 2 managed runtime installer", () => {
expect(launched.stdout).toContain("gstack fixture doctor"); expect(launched.stdout).toContain("gstack fixture doctor");
expect(await readJson(path.join(home, "versions", "current.json"))).toEqual(pointer); expect(await readJson(path.join(home, "versions", "current.json"))).toEqual(pointer);
expect(await fs.readFile(path.join(home, "runtime-install.json"))).toEqual(manifest); expect(await fs.readFile(path.join(home, "runtime-install.json"))).toEqual(manifest);
expect(await fs.readFile(path.join(home, "bin", "gstack.cmd"))).toEqual(windowsLauncher); expect(await fs.readFile(launcherPath)).toEqual(launcherBefore);
expect(await exists(path.join(home, ".gstack-runtime-transaction.json"))).toBe(false); expect(await exists(path.join(home, ".gstack-runtime-transaction.json"))).toBe(false);
expect(await exists(orphanedLock)).toBe(false); expect(await exists(orphanedLock)).toBe(false);
}); });
@@ -774,7 +779,7 @@ async function withFixture(
if (options.createDefaultSource !== false) await createSource(source); if (options.createDefaultSource !== false) await createSource(source);
await callback({ root, source, home }); await callback({ root, source, home });
} finally { } finally {
await fs.rm(root, { recursive: true, force: true }); await fs.rm(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
} }
} }