Skip to content

Commit 72e70ca

Browse files
authored
Merge pull request #762 from hangox/test/release-notes-fixture-env
test(release): isolate release-notes fixture env
2 parents 9cf4e1d + 98bcfd6 commit 72e70ca

1 file changed

Lines changed: 77 additions & 18 deletions

File tree

tests/unit/ci/release-notes-script.test.ts

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ import { beforeAll, describe, expect, it } from "vitest";
66

77
const ROOT = resolve(__dirname, "..", "..", "..");
88
const SCRIPT = resolve(ROOT, ".github", "scripts", "generate-release-notes.sh");
9+
const RELEASE_NOTES_ENV_KEYS = [
10+
"RELEASE_NOTES_BASE_URL",
11+
"RELEASE_NOTES_API_KEY",
12+
"RELEASE_NOTES_MODEL",
13+
"RELEASE_NOTES_REQUEST_TIMEOUT_MS",
14+
] as const;
15+
16+
function withoutReleaseNotesEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
17+
const sanitized = { ...env };
18+
for (const key of RELEASE_NOTES_ENV_KEYS) delete sanitized[key];
19+
return sanitized;
20+
}
921

1022
function git(cwd: string, args: string[]): string {
1123
return execFileSync("git", args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
@@ -22,10 +34,11 @@ function hasBash(): boolean {
2234

2335
const describeIfBash = hasBash() ? describe : describe.skip;
2436

25-
function runNotes(cwd: string, tag: string): string {
37+
function runNotes(cwd: string, tag: string, env: NodeJS.ProcessEnv = process.env): string {
2638
return execFileSync("bash", [SCRIPT, tag], {
2739
cwd,
2840
encoding: "utf-8",
41+
env: withoutReleaseNotesEnv(env),
2942
stdio: ["ignore", "pipe", "pipe"],
3043
});
3144
}
@@ -56,6 +69,27 @@ function createRepo(): string {
5669
return cwd;
5770
}
5871

72+
function createSquashPromotionRepo(): string {
73+
const cwd = createRepo();
74+
git(cwd, ["checkout", "-b", "dev"]);
75+
writeText(cwd, "src/app.txt", "real fix\n");
76+
commitAll(cwd, "fix: real user-facing fix (#10)");
77+
writeText(cwd, "src/helper.txt", "cleanup\n");
78+
commitAll(cwd, "feat: user-visible helper feature (#11)");
79+
git(cwd, ["update-ref", "refs/remotes/origin/dev", "dev"]);
80+
81+
git(cwd, ["checkout", "master"]);
82+
git(cwd, ["read-tree", "--reset", "-u", "dev"]);
83+
commitAll(cwd, "fix: promote dev release fixes to master");
84+
writeText(cwd, "README.md", "synced readme\n");
85+
writeText(cwd, "package.json", "{\"version\":\"1.0.1\"}\n");
86+
writeText(cwd, "package-lock.json", "{\"version\":\"1.0.1\",\"packages\":{\"\":{\"version\":\"1.0.1\"},\"packages/electron\":{\"version\":\"1.0.1\"}}}\n");
87+
writeText(cwd, "packages/electron/package.json", "{\"version\":\"1.0.1\"}\n");
88+
commitAll(cwd, "chore: bump version to 1.0.1 [skip ci]");
89+
git(cwd, ["tag", "v1.0.1"]);
90+
return cwd;
91+
}
92+
5993
describe("generate-release-notes.sh", () => {
6094
beforeAll(() => {
6195
expect(existsSync(SCRIPT), `script missing: ${SCRIPT}`).toBe(true);
@@ -69,6 +103,19 @@ describe("generate-release-notes.sh", () => {
69103
expect(workflow).toContain("bash .github/scripts/generate-release-notes.sh \"$TAG\" > /tmp/release-notes.md");
70104
});
71105

106+
it("does not pass release-notes LLM configuration to fallback fixtures", () => {
107+
const env = withoutReleaseNotesEnv({
108+
PATH: process.env.PATH,
109+
RELEASE_NOTES_BASE_URL: "http://example.test/v1",
110+
RELEASE_NOTES_API_KEY: "test-key",
111+
RELEASE_NOTES_MODEL: "test-model",
112+
RELEASE_NOTES_REQUEST_TIMEOUT_MS: "1",
113+
});
114+
115+
expect(env).toMatchObject({ PATH: process.env.PATH });
116+
for (const key of RELEASE_NOTES_ENV_KEYS) expect(env[key]).toBeUndefined();
117+
});
118+
72119
});
73120

74121
describeIfBash("generate-release-notes.sh bash behavior", () => {
@@ -91,23 +138,7 @@ describeIfBash("generate-release-notes.sh bash behavior", () => {
91138
});
92139

93140
it("falls back to dev history when a stable tag only contains a squash promotion", () => {
94-
const cwd = createRepo();
95-
git(cwd, ["checkout", "-b", "dev"]);
96-
writeText(cwd, "src/app.txt", "real fix\n");
97-
commitAll(cwd, "fix: real user-facing fix (#10)");
98-
writeText(cwd, "src/helper.txt", "cleanup\n");
99-
commitAll(cwd, "feat: user-visible helper feature (#11)");
100-
git(cwd, ["update-ref", "refs/remotes/origin/dev", "dev"]);
101-
102-
git(cwd, ["checkout", "master"]);
103-
git(cwd, ["read-tree", "--reset", "-u", "dev"]);
104-
commitAll(cwd, "fix: promote dev release fixes to master");
105-
writeText(cwd, "README.md", "synced readme\n");
106-
writeText(cwd, "package.json", "{\"version\":\"1.0.1\"}\n");
107-
writeText(cwd, "package-lock.json", "{\"version\":\"1.0.1\",\"packages\":{\"\":{\"version\":\"1.0.1\"},\"packages/electron\":{\"version\":\"1.0.1\"}}}\n");
108-
writeText(cwd, "packages/electron/package.json", "{\"version\":\"1.0.1\"}\n");
109-
commitAll(cwd, "chore: bump version to 1.0.1 [skip ci]");
110-
git(cwd, ["tag", "v1.0.1"]);
141+
const cwd = createSquashPromotionRepo();
111142

112143
const notes = runNotes(cwd, "v1.0.1");
113144

@@ -116,6 +147,34 @@ describeIfBash("generate-release-notes.sh bash behavior", () => {
116147
expect(notes).not.toContain("promote dev release fixes");
117148
});
118149

150+
it("keeps the squash-promotion fallback local when ambient LLM configuration is present", () => {
151+
const cwd = createSquashPromotionRepo();
152+
const shimDir = join(cwd, "node-shim");
153+
const leakedConfigFile = join(cwd, "release-notes-config-leaked");
154+
mkdirSync(shimDir, { recursive: true });
155+
writeFileSync(
156+
join(shimDir, "node"),
157+
"#!/bin/sh\nif [ -n \"${RELEASE_NOTES_BASE_URL:-}\" ] || [ -n \"${RELEASE_NOTES_API_KEY:-}\" ] || [ -n \"${RELEASE_NOTES_MODEL:-}\" ] || [ -n \"${RELEASE_NOTES_REQUEST_TIMEOUT_MS:-}\" ]; then\n touch \"$RELEASE_NOTES_ENV_LEAK_FILE\"\n exit 91\nfi\nexec \"$REAL_NODE\" \"$@\"\n",
158+
{ mode: 0o755 },
159+
);
160+
161+
const notes = runNotes(cwd, "v1.0.1", {
162+
...process.env,
163+
PATH: `${shimDir}:${process.env.PATH}`,
164+
REAL_NODE: process.execPath,
165+
RELEASE_NOTES_ENV_LEAK_FILE: leakedConfigFile,
166+
RELEASE_NOTES_BASE_URL: "http://127.0.0.1:9/v1",
167+
RELEASE_NOTES_API_KEY: "test-key",
168+
RELEASE_NOTES_MODEL: "test-model",
169+
RELEASE_NOTES_REQUEST_TIMEOUT_MS: "3000",
170+
});
171+
172+
expect(existsSync(leakedConfigFile)).toBe(false);
173+
expect(notes).toContain("### Fixes");
174+
expect(notes).toContain("real user-facing fix (#10)");
175+
expect(notes).toContain("user-visible helper feature (#11)");
176+
});
177+
119178
it("uses topological sorting (git describe) rather than semver sorting to avoid pulling in old history from unrelated higher-version tags", () => {
120179
const cwd = createRepo(); // v1.0.0 is created here (commit C1)
121180

0 commit comments

Comments
 (0)