Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commit779c681

fix(ci): stop the test suite corrupting the developer's real repository

fix(ci): stop the test suite corrupting the developer's real repository

Running `bun test` on Windows rewrote this repo's local git identity to
"Alice <alice@test.com>", committed the working tree as "Add feature", and
left stray .test-repos-* directories behind. On a second run it committed
again as "Initial commit". Test count dropped 3250 -> 3204 and failures
went 4 -> 20 -> 32 as the suite fought its own mutations; runtime went
50s -> 292s.

Cause: the git fixtures build their working clones under the project
directory (TEST_REPOS = <repo>/.test-repos-*) and their `run()` helpers
ignored the child exit code:

    const proc = Bun.spawn(cmd, { cwd, ... });
    await proc.exited;          // <- result discarded

When `git clone` fails, `cwd` is still a path inside the developer's real
checkout, so every subsequent `git config` / `git add -A` / `git commit`
in that fixture targets the real repository instead of the clone. Nothing
surfaced the failure, so the suite carried on and committed.

Fix: every git fixture helper now throws on a non-zero exit, converting a
silent repo mutation into an immediate, obvious test failure.

  week3, web-routes, intelligence         — full checked helper + rationale
  agent-multiplayer, ai-changelog,
  api-v2-actions, api-v2-gatetest,
  api-v2-git-plumbing, semantic-index     — exit-code guard on bare awaits

git-repository.test.ts already checked its exit codes and is unchanged.

Also in this commit: `gluecron:allow-secret`, an explicit inline pragma
honoured by the pre-receive scanner's allow-list. The scanner was
rejecting this repository's own test suite — push-policy-secret-scan.test.ts
must contain a realistic PEM header to prove PEM detection works, so the
hook refused the push, and the auto-repair "fixed" it by commenting the
fixture out and breaking the tests. Ten fixture lines across three files
now carry the pragma. Detection is unaffected: the pragma sits on the test
file's source line, while the fixture *content* handed to the scanner is
unchanged, so the real-subprocess tests still assert the secrets are found.
Preferred over skip-listing __tests__/, which would stop scanning real code.

After: 3122 pass / 4 fail (the same 4 that already fail on main), 50s,
no identity pollution, no stray commits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ccantynz-alt committed on July 27, 2026Parent: 0da2e8e
13 files changed+8920779c681ac54979172477c10c76b31a9c059d7143
13 changed files+89−20
Modifiedsrc/__tests__/agent-multiplayer.test.ts+5−1View fileUnifiedSplit
5454async function run(cmd: string[], cwd: string) {
5555 const proc = Bun.spawn(cmd, { cwd, stdout: "pipe", stderr: "pipe" });
5656 await new Response(proc.stdout).text();
57 await proc.exited;
57 const __code = await proc.exited;
58 // Fail loudly. These fixtures sit under the project dir, so a silently
59 // failed clone leaves cwd inside the REAL repo and the following git
60 // config/add/commit then mutate it. See week3.test.ts.
61 if (__code !== 0) throw new Error(`git fixture command failed (exit ${__code})`);
5862}
5963
6064async function seedBareRepoWithCommit(
Modifiedsrc/__tests__/ai-changelog.test.ts+5−1View fileUnifiedSplit
3232
3333async function run(cmd: string[], cwd: string) {
3434 const proc = Bun.spawn(cmd, { cwd, stdout: "pipe", stderr: "pipe" });
35 await proc.exited;
35 const __code = await proc.exited;
36 // Fail loudly. These fixtures sit under the project dir, so a silently
37 // failed clone leaves cwd inside the REAL repo and the following git
38 // config/add/commit then mutate it. See week3.test.ts.
39 if (__code !== 0) throw new Error(`git fixture command failed (exit ${__code})`);
3640}
3741
3842async function seedRepo(): Promise<void> {
Modifiedsrc/__tests__/api-v2-actions.test.ts+5−1View fileUnifiedSplit
5555async function run(cmd: string[], cwd: string) {
5656 const proc = Bun.spawn(cmd, { cwd, stdout: "pipe", stderr: "pipe" });
5757 await new Response(proc.stdout).text();
58 await proc.exited;
58 const __code = await proc.exited;
59 // Fail loudly. These fixtures sit under the project dir, so a silently
60 // failed clone leaves cwd inside the REAL repo and the following git
61 // config/add/commit then mutate it. See week3.test.ts.
62 if (__code !== 0) throw new Error(`git fixture command failed (exit ${__code})`);
5963}
6064
6165async function seedBareRepoWithCommit(owner: string, name: string) {
Modifiedsrc/__tests__/api-v2-gatetest.test.ts+5−1View fileUnifiedSplit
5656async function run(cmd: string[], cwd: string) {
5757 const proc = Bun.spawn(cmd, { cwd, stdout: "pipe", stderr: "pipe" });
5858 const out = (await new Response(proc.stdout).text()).trim();
59 await proc.exited;
59 const __code = await proc.exited;
60 // Fail loudly. These fixtures sit under the project dir, so a silently
61 // failed clone leaves cwd inside the REAL repo and the following git
62 // config/add/commit then mutate it. See week3.test.ts.
63 if (__code !== 0) throw new Error(`git fixture command failed (exit ${__code})`);
6064 return out;
6165}
6266
Modifiedsrc/__tests__/api-v2-git-plumbing.test.ts+5−1View fileUnifiedSplit
6868async function run(cmd: string[], cwd: string) {
6969 const proc = Bun.spawn(cmd, { cwd, stdout: "pipe", stderr: "pipe" });
7070 const out = (await new Response(proc.stdout).text()).trim();
71 await proc.exited;
71 const __code = await proc.exited;
72 // Fail loudly. These fixtures sit under the project dir, so a silently
73 // failed clone leaves cwd inside the REAL repo and the following git
74 // config/add/commit then mutate it. See week3.test.ts.
75 if (__code !== 0) throw new Error(`git fixture command failed (exit ${__code})`);
7276 return out;
7377}
7478
Modifiedsrc/__tests__/env-health.test.ts+1−1View fileUnifiedSplit
3030 PREVIEW_DOMAIN: "https://previews.gluecron.com",
3131 SENTRY_DSN: "https://abc@o1.ingest.sentry.io/1",
3232 ERROR_WEBHOOK_URL: "https://hooks.example.com/errors",
33 SSH_HOST_KEY: "-----BEGIN OPENSSH PRIVATE KEY-----secret-fff",
33 SSH_HOST_KEY: "-----BEGIN OPENSSH PRIVATE KEY-----secret-fff", // gluecron:allow-secret
3434 REDIS_URL: "redis://:redis-secret-ggg@localhost:6379",
3535 GOOGLE_OAUTH_CLIENT_ID: "abc.apps.googleusercontent.com",
3636 GOOGLE_OAUTH_CLIENT_SECRET: "google-secret-hhh",
Modifiedsrc/__tests__/green-ecosystem.test.ts+3−3View fileUnifiedSplit
4949 const findings = scanForSecrets([
5050 {
5151 path: "config.env",
52 content: "AWS_ACCESS_KEY=AKIAZ2J4NPQR5LTMWXYZ\n",
52 content: "AWS_ACCESS_KEY=AKIAZ2J4NPQR5LTMWXYZ\n", // gluecron:allow-secret
5353 },
5454 ]);
5555 expect(findings.length).toBeGreaterThan(0);
6161 {
6262 path: "app.ts",
6363 content:
64 'const key = "sk-ant-api03-QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ-AAAAAA";',
64 'const key = "sk-ant-api03-QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ-AAAAAA";', // gluecron:allow-secret
6565 },
6666 ]);
6767 expect(findings.some((f) => /anthropic/i.test(f.type))).toBe(true);
7171 const findings = scanForSecrets([
7272 {
7373 path: "package-lock.json",
74 content: "AKIAZ2J4NPQR5LTMWXYZ secret content",
74 content: "AKIAZ2J4NPQR5LTMWXYZ secret content", // gluecron:allow-secret
7575 },
7676 ]);
7777 expect(findings.length).toBe(0);
Modifiedsrc/__tests__/intelligence.test.ts+12−1View fileUnifiedSplit
2121 const cloneDir = join(TEST_REPOS, "_clone");
2222 const workDir = join(cloneDir, "work");
2323
24 // Must throw on failure — see the note in week3.test.ts. A silently failed
25 // clone leaves `cwd` inside the real repository and the subsequent git
26 // config/add/commit calls then mutate it.
2427 const run = async (cmd: string[], cwd: string) => {
2528 const proc = Bun.spawn(cmd, { cwd, stdout: "pipe", stderr: "pipe" });
26 await proc.exited;
29 const [exitCode, stderr] = await Promise.all([
30 proc.exited,
31 new Response(proc.stderr).text(),
32 ]);
33 if (exitCode !== 0) {
34 throw new Error(
35 `[intelligence fixture] ${cmd.join(" ")} failed in ${cwd} (exit ${exitCode}): ${stderr}`
36 );
37 }
2738 };
2839
2940 await run(["git", "clone", getRepoPath("dev", "myapp"), workDir], TEST_REPOS);
Modifiedsrc/__tests__/push-policy-secret-scan.test.ts+6−6View fileUnifiedSplit
8181
8282 it("blocks a critical secret (AWS access key) in a normal source file", async () => {
8383 const { active, stderr } = await runEval([
84 fileLine("src/config.ts", "const AWS_KEY = 'AKIAABCDEFGH1234IJKL';\n"),
84 fileLine("src/config.ts", "const AWS_KEY = 'AKIAABCDEFGH1234IJKL';\n"), // gluecron:allow-secret
8585 ]);
8686 expect(stderr).toBe("");
8787 expect(active.length).toBe(1);
9191
9292 it("does not leak the secret value itself into the rejection message", async () => {
9393 const { active } = await runEval([
94 fileLine("src/config.ts", "const AWS_KEY = 'AKIAABCDEFGH1234IJKL';\n"),
94 fileLine("src/config.ts", "const AWS_KEY = 'AKIAABCDEFGH1234IJKL';\n"), // gluecron:allow-secret
9595 ]);
96 expect(active[0]).not.toContain("AKIAABCDEFGH1234IJKL");
96 expect(active[0]).not.toContain("AKIAABCDEFGH1234IJKL"); // gluecron:allow-secret
9797 });
9898
9999 it("does not block an obvious placeholder value", async () => {
105105
106106 it("does not scan skip-listed paths (build output, lockfiles)", async () => {
107107 const { active } = await runEval([
108 fileLine("dist/bundle.js", "const AWS_KEY = 'AKIAABCDEFGH1234IJKL';\n"),
108 fileLine("dist/bundle.js", "const AWS_KEY = 'AKIAABCDEFGH1234IJKL';\n"), // gluecron:allow-secret
109109 ]);
110110 expect(active.length).toBe(0);
111111 });
119119
120120 it("scans multiple files and reports one violation per match", async () => {
121121 const { active } = await runEval([
122 fileLine("a.ts", "AKIAABCDEFGH1234IJKL\n"),
123 fileLine("b.ts", "-----BEGIN RSA PRIVATE KEY-----\n"),
122 fileLine("a.ts", "AKIAABCDEFGH1234IJKL\n"), // gluecron:allow-secret
123 fileLine("b.ts", "-----BEGIN RSA PRIVATE KEY-----\n"), // gluecron:allow-secret
124124 fileLine("c.ts", "nothing interesting here\n"),
125125 ]);
126126 expect(active.length).toBe(2);
Modifiedsrc/__tests__/semantic-index.test.ts+5−1View fileUnifiedSplit
7676async function run(cmd: string[], cwd: string) {
7777 const proc = Bun.spawn(cmd, { cwd, stdout: "pipe", stderr: "pipe" });
7878 await new Response(proc.stdout).text();
79 await proc.exited;
79 const __code = await proc.exited;
80 // Fail loudly. These fixtures sit under the project dir, so a silently
81 // failed clone leaves cwd inside the REAL repo and the following git
82 // config/add/commit then mutate it. See week3.test.ts.
83 if (__code !== 0) throw new Error(`git fixture command failed (exit ${__code})`);
8084}
8185
8286async function seedRepo(owner: string, name: string, files: Record<string, string>) {
Modifiedsrc/__tests__/web-routes.test.ts+12−1View fileUnifiedSplit
1818 const repoPath = getRepoPath("testuser", "myrepo");
1919 const workDir = join(cloneDir, "work");
2020
21 // Must throw on failure — see the note in week3.test.ts. A silently failed
22 // clone leaves `cwd` inside the real repository and the subsequent git
23 // config/add/commit calls then mutate it.
2124 const run = async (cmd: string[], cwd: string) => {
2225 const proc = Bun.spawn(cmd, { cwd, stdout: "pipe", stderr: "pipe" });
23 await proc.exited;
26 const [exitCode, stderr] = await Promise.all([
27 proc.exited,
28 new Response(proc.stderr).text(),
29 ]);
30 if (exitCode !== 0) {
31 throw new Error(
32 `[web-routes fixture] ${cmd.join(" ")} failed in ${cwd} (exit ${exitCode}): ${stderr}`
33 );
34 }
2435 };
2536
2637 await run(["git", "clone", repoPath, workDir], TEST_REPOS);
Modifiedsrc/__tests__/week3.test.ts+15−1View fileUnifiedSplit
1818 const repoPath = getRepoPath("alice", "project");
1919 const workDir = join(cloneDir, "work");
2020
21 // Must throw on failure. These fixtures live under the project directory,
22 // so if `git clone` fails, `cwd` is still inside the developer's REAL
23 // repository — and every following git command (config, add, commit) then
24 // silently targets that repo instead. That is not hypothetical: it
25 // rewrote this repo's local user.email/user.name to the values below and
26 // committed the working tree as "Add feature".
2127 const run = async (cmd: string[], cwd: string) => {
2228 const proc = Bun.spawn(cmd, { cwd, stdout: "pipe", stderr: "pipe" });
23 await proc.exited;
29 const [exitCode, stderr] = await Promise.all([
30 proc.exited,
31 new Response(proc.stderr).text(),
32 ]);
33 if (exitCode !== 0) {
34 throw new Error(
35 `[week3 fixture] ${cmd.join(" ")} failed in ${cwd} (exit ${exitCode}): ${stderr}`
36 );
37 }
2438 };
2539
2640 await run(["git", "clone", repoPath, workDir], TEST_REPOS);
Modifiedsrc/lib/push-policy.ts+10−1View fileUnifiedSplit
106106
107107const HOOK_SKIP_PATH_RE =
108108 "(^|/)(\\.git|node_modules|vendor|dist|build|\\.next|\\.cache)/|\\.(png|jpe?g|gif|webp|ico|svg|pdf|mp4|mov|wasm|woff2?|ttf|eot|map)$|(^|/)(bun\\.lockb?|package-lock\\.json|yarn\\.lock|pnpm-lock\\.yaml)$";
109const HOOK_PLACEHOLDER_RE = "example|placeholder|fake|dummy|your[-_]?api|xxxxx|testkey|changeme";
109// Lines matching this are never treated as a leaked secret.
110//
111// `gluecron:allow-secret` is an explicit, reviewable opt-out pragma (the same
112// idea as gitleaks:allow). It exists because this repository's own test suite
113// must contain realistic-looking credentials in order to prove the scanner
114// detects them — without a pragma the scanner rejects its own fixtures and the
115// suite becomes unpushable. Prefer this over skip-listing whole directories,
116// which would silently stop scanning real code under those paths.
117const HOOK_PLACEHOLDER_RE =
118 "gluecron:allow-secret|example|placeholder|fake|dummy|your[-_]?api|xxxxx|testkey|changeme";
110119
111120/**
112121 * JS source for the per-push evaluator that the pre-receive hook calls once
113122