Skip to content

Commit a5eee8f

Browse files
committed
fix(scripts): detect timed changed gates
1 parent 3c6fd49 commit a5eee8f

2 files changed

Lines changed: 137 additions & 1 deletion

File tree

scripts/crabbox-wrapper.mjs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,7 @@ function commandRuntimeEntrypoint(commandArgs) {
562562
}
563563

564564
function commandWordsRuntimeEntrypoint(words) {
565+
words = normalizeExecutableWords(words);
565566
const first = (words[0] ?? "").split("/").pop();
566567
if (jsRuntimeEntrypoints.has(first)) {
567568
return first;
@@ -591,6 +592,7 @@ function commandNeedsAwsMacosPackageManager(commandArgs) {
591592
}
592593

593594
function commandWordsNeedAwsMacosPackageManager(words) {
595+
words = normalizeExecutableWords(words);
594596
const first = (words[0] ?? "").split("/").pop();
595597
if (awsMacosCorepackEntrypoints.has(first)) {
596598
return true;
@@ -612,6 +614,7 @@ function isChangedGateCommand(commandArgs) {
612614
}
613615

614616
function isChangedGateCommandWords(words) {
617+
words = normalizeExecutableWords(words);
615618
if (isChangedGateWords(words)) {
616619
return true;
617620
}
@@ -623,7 +626,7 @@ function isChangedGateCommandWords(words) {
623626
}
624627

625628
function isChangedGateWords(words) {
626-
words = [...words];
629+
words = normalizeExecutableWords(words);
627630
if (words[0] === "corepack") {
628631
words.shift();
629632
}
@@ -686,6 +689,10 @@ function normalizedShellSegmentWords(segment) {
686689
return normalizedCommandWords(stripShellExecutionPrefixes(normalizedWords));
687690
}
688691

692+
function normalizeExecutableWords(words) {
693+
return normalizedCommandWords(stripShellExecutionPrefixes(words));
694+
}
695+
689696
function stripShellExecutionPrefixes(words) {
690697
words = [...words];
691698
for (;;) {
@@ -706,6 +713,10 @@ function stripShellExecutionPrefixes(words) {
706713
stripTimeOptions(words);
707714
continue;
708715
}
716+
if (first === "timeout") {
717+
stripTimeoutOptions(words);
718+
continue;
719+
}
709720
return words;
710721
}
711722
}
@@ -738,6 +749,39 @@ function stripTimeOptions(words) {
738749
}
739750
}
740751

752+
function stripTimeoutOptions(words) {
753+
words.shift();
754+
for (;;) {
755+
const word = words[0] ?? "";
756+
if (!word) {
757+
return;
758+
}
759+
if (word === "--") {
760+
words.shift();
761+
break;
762+
}
763+
if (word === "-k" || word === "--kill-after" || word === "-s" || word === "--signal") {
764+
words.shift();
765+
if (words[0]) {
766+
words.shift();
767+
}
768+
continue;
769+
}
770+
if (word.startsWith("--kill-after=") || word.startsWith("--signal=")) {
771+
words.shift();
772+
continue;
773+
}
774+
if (word.startsWith("-") && word !== "-") {
775+
words.shift();
776+
continue;
777+
}
778+
break;
779+
}
780+
if (words[0]) {
781+
words.shift();
782+
}
783+
}
784+
741785
function splitShellWords(value) {
742786
const words = [];
743787
let word = "";

test/scripts/crabbox-wrapper.test.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,98 @@ describe("scripts/crabbox-wrapper", () => {
13291329
expect(remoteCommand).toContain(`&& ${shellScript}`);
13301330
});
13311331

1332+
it("preserves sparse changed-gate Git bootstrap for timeout-wrapped shell commands", () => {
1333+
const shellScript =
1334+
"/usr/bin/time -v timeout 1200s node scripts/check-changed.mjs --base origin/main --head HEAD";
1335+
const result = runWrapper(
1336+
"provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n",
1337+
["run", "--provider", "aws", "--shell", "--", shellScript],
1338+
{
1339+
gitResponses: {
1340+
["config\u0000--bool\u0000core.sparseCheckout"]: { stdout: "true\n" },
1341+
["status\u0000--porcelain=v1"]: { stdout: "" },
1342+
["merge-base\u0000origin/main\u0000HEAD"]: { stdout: "abc123\n" },
1343+
},
1344+
},
1345+
);
1346+
1347+
const output = parseFakeCrabboxOutput(result);
1348+
const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? "");
1349+
expect(result.status).toBe(0);
1350+
expect(remoteCommand).toContain("git init -q");
1351+
expect(remoteCommand).toContain(
1352+
"git fetch -q --depth=1 origin abc123:refs/remotes/origin/main",
1353+
);
1354+
expect(remoteCommand).toContain(`&& ${shellScript}`);
1355+
});
1356+
1357+
it("preserves sparse changed-gate Git bootstrap for direct timeout-wrapped node commands", () => {
1358+
const result = runWrapper(
1359+
"provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n",
1360+
[
1361+
"run",
1362+
"--provider",
1363+
"aws",
1364+
"--",
1365+
"timeout",
1366+
"1200s",
1367+
"node",
1368+
"scripts/check-changed.mjs",
1369+
"--base",
1370+
"origin/main",
1371+
"--head",
1372+
"HEAD",
1373+
],
1374+
{
1375+
gitResponses: {
1376+
["config\u0000--bool\u0000core.sparseCheckout"]: { stdout: "true\n" },
1377+
["status\u0000--porcelain=v1"]: { stdout: "" },
1378+
["merge-base\u0000origin/main\u0000HEAD"]: { stdout: "abc123\n" },
1379+
},
1380+
},
1381+
);
1382+
1383+
const output = parseFakeCrabboxOutput(result);
1384+
const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? "");
1385+
expect(result.status).toBe(0);
1386+
expect(output.args).toContain("--shell");
1387+
expect(remoteCommand).toContain("git init -q");
1388+
expect(remoteCommand).toMatch(
1389+
/&& timeout 1200s node scripts\/check-changed\.mjs --base origin\/main --head HEAD$/u,
1390+
);
1391+
});
1392+
1393+
it("preserves sparse changed-gate Git bootstrap for direct timeout-wrapped shell commands", () => {
1394+
const result = runWrapper(
1395+
"provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n",
1396+
[
1397+
"run",
1398+
"--provider",
1399+
"aws",
1400+
"--",
1401+
"timeout",
1402+
"1200s",
1403+
"bash",
1404+
"-lc",
1405+
"pnpm check:changed",
1406+
],
1407+
{
1408+
gitResponses: {
1409+
["config\u0000--bool\u0000core.sparseCheckout"]: { stdout: "true\n" },
1410+
["status\u0000--porcelain=v1"]: { stdout: "" },
1411+
["merge-base\u0000origin/main\u0000HEAD"]: { stdout: "abc123\n" },
1412+
},
1413+
},
1414+
);
1415+
1416+
const output = parseFakeCrabboxOutput(result);
1417+
const remoteCommand = normalizeShellLineEndings(output.args.at(-1) ?? "");
1418+
expect(result.status).toBe(0);
1419+
expect(output.args).toContain("--shell");
1420+
expect(remoteCommand).toContain("git init -q");
1421+
expect(remoteCommand).toMatch(/&& timeout 1200s bash -lc 'pnpm check:changed'$/u);
1422+
});
1423+
13321424
it("does not treat quoted sparse shell text as a changed gate", () => {
13331425
const result = runWrapper(
13341426
"provider: hetzner, aws, local-container, blacksmith-testbox, or cloudflare\n",

0 commit comments

Comments
 (0)