From 14ebfc88c4dd7971a250bf0df35bd5eb9efdefb2 Mon Sep 17 00:00:00 2001 From: Vincent Koc <25068+vincentkoc@users.noreply.github.com> Date: Fri, 10 Jul 2026 16:01:05 -0700 Subject: [PATCH] fix(release): avoid caching GitHub errors --- .../scripts/verify-release-notes.mjs | 13 ++++- test/scripts/verify-release-notes.test.ts | 53 ++++++++++++++++--- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/.agents/skills/openclaw-changelog-update/scripts/verify-release-notes.mjs b/.agents/skills/openclaw-changelog-update/scripts/verify-release-notes.mjs index 7804fd853aff..07c442a685e3 100644 --- a/.agents/skills/openclaw-changelog-update/scripts/verify-release-notes.mjs +++ b/.agents/skills/openclaw-changelog-update/scripts/verify-release-notes.mjs @@ -285,9 +285,18 @@ export function githubApiWithSnapshot(args, fetchApi, snapshotState) { snapshotState.hits += 1; return structuredClone(cached); } - const response = fetchApi(args); - snapshotState.responses[key] = structuredClone(response); snapshotState.misses += 1; + const response = fetchApi(args); + if ( + !response || + typeof response !== "object" || + Array.isArray(response) || + response.data === undefined || + (Array.isArray(response.errors) && response.errors.length > 0) + ) { + return response; + } + snapshotState.responses[key] = structuredClone(response); snapshotState.dirty = true; return response; } diff --git a/test/scripts/verify-release-notes.test.ts b/test/scripts/verify-release-notes.test.ts index f1fe1d54e303..bcd4216f4326 100644 --- a/test/scripts/verify-release-notes.test.ts +++ b/test/scripts/verify-release-notes.test.ts @@ -43,7 +43,7 @@ describe("release-note verification", () => { let fetches = 0; const fetchApi = (args: string[]) => { fetches += 1; - return { request: args, fetches }; + return { data: { request: args, fetches } }; }; const first = createGithubSnapshotState({ base: "a".repeat(40), @@ -52,14 +52,18 @@ describe("release-note verification", () => { }); expect(githubApiWithSnapshot(["graphql", "-f", "query=one"], fetchApi, first)).toEqual({ - request: ["graphql", "-f", "query=one"], - fetches: 1, + data: { + request: ["graphql", "-f", "query=one"], + fetches: 1, + }, }); expect( githubApiWithSnapshot(["repos/openclaw/openclaw/releases/tags/v1"], fetchApi, first), ).toEqual({ - request: ["repos/openclaw/openclaw/releases/tags/v1"], - fetches: 2, + data: { + request: ["repos/openclaw/openclaw/releases/tags/v1"], + fetches: 2, + }, }); persistGithubSnapshot(first); @@ -69,8 +73,10 @@ describe("release-note verification", () => { target: "b".repeat(40), }); expect(githubApiWithSnapshot(["graphql", "-f", "query=one"], fetchApi, second)).toEqual({ - request: ["graphql", "-f", "query=one"], - fetches: 1, + data: { + request: ["graphql", "-f", "query=one"], + fetches: 1, + }, }); expect(second.hits).toBe(1); expect(second.misses).toBe(0); @@ -80,6 +86,39 @@ describe("release-note verification", () => { } }); + it("does not cache transient GraphQL errors", () => { + const cwd = mkdtempSync(join(tmpdir(), "openclaw-release-notes-snapshot-")); + try { + const filePath = join(cwd, "snapshot.json"); + const state = createGithubSnapshotState({ + base: "a".repeat(40), + filePath, + target: "b".repeat(40), + }); + let fetches = 0; + const fetchApi = () => { + fetches += 1; + return fetches === 1 + ? { errors: [{ message: "rate limited" }] } + : { data: { repository: { id: "repository-id" } } }; + }; + const args = ["graphql", "-f", "query=one"]; + + expect(githubApiWithSnapshot(args, fetchApi, state)).toEqual({ + errors: [{ message: "rate limited" }], + }); + expect(state.dirty).toBe(false); + expect(state.responses).toEqual({}); + expect(githubApiWithSnapshot(args, fetchApi, state)).toEqual({ + data: { repository: { id: "repository-id" } }, + }); + expect(state.misses).toBe(2); + expect(fetches).toBe(2); + } finally { + rmSync(cwd, { recursive: true, force: true }); + } + }); + it("rejects a snapshot bound to a different release target", () => { const cwd = mkdtempSync(join(tmpdir(), "openclaw-release-notes-snapshot-")); try {