From 704f72b589a4e4f3e2075119ce214a88430d95f9 Mon Sep 17 00:00:00 2001 From: pallyoung Date: Thu, 13 Aug 2026 18:35:54 +0800 Subject: [PATCH] fix(ci): parse Windows release reports safely --- .github/workflows/desktop-release.yml | 9 +++++++-- .github/workflows/publish.yml | 6 +++++- scripts/github-workflows.test.ts | 6 ++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/desktop-release.yml b/.github/workflows/desktop-release.yml index 52ff5dcc..ea7333e7 100644 --- a/.github/workflows/desktop-release.yml +++ b/.github/workflows/desktop-release.yml @@ -653,6 +653,7 @@ jobs: run: | mkdir -p release/promotion-reports/channel gh release download "${{ needs.prepare.outputs.tag }}" \ + --repo "${GITHUB_REPOSITORY}" \ --pattern desktop-channel.json \ --dir release/promotion-reports/channel @@ -667,13 +668,17 @@ jobs: const commitSha = process.env.GITHUB_SHA; const releaseTag = "${{ needs.prepare.outputs.tag }}"; const productVersion = "${{ needs.prepare.outputs.runtime_version }}"; - const channel = JSON.parse(readFileSync("release/promotion-reports/channel/desktop-channel.json", "utf8")); + const parseJson = (path) => { + const text = readFileSync(path, "utf8"); + return JSON.parse(text.charCodeAt(0) === 0xfeff ? text.slice(1) : text); + }; + const channel = parseJson("release/promotion-reports/channel/desktop-channel.json"); const channelSignatureDigest = createHash("sha256") .update(channel.signature.value, "utf8") .digest("hex"); const readReports = (directory) => readdirSync(directory, { recursive: true }) .filter((name) => String(name).endsWith(".json")) - .map((name) => JSON.parse(readFileSync(join(directory, String(name)), "utf8"))); + .map((name) => parseJson(join(directory, String(name)))); const production = readReports("release/promotion-reports/production"); if (production.length !== 2) throw new Error("Both native and WSL production reports are required"); const requiredProductionScenarios = new Set(${{ needs.prepare.outputs.has_previous_desktop == 'true' }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 425bd896..42527ded 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -212,9 +212,13 @@ jobs: import { readdirSync, readFileSync } from "node:fs"; import { join } from "node:path"; const root = "release/desktop-acceptance-reports"; + const parseJson = (path) => { + const text = readFileSync(path, "utf8"); + return JSON.parse(text.charCodeAt(0) === 0xfeff ? text.slice(1) : text); + }; const reports = readdirSync(root, { recursive: true }) .filter((name) => String(name).endsWith(".json")) - .map((name) => JSON.parse(readFileSync(join(root, String(name)), "utf8"))); + .map((name) => parseJson(join(root, String(name)))); const bootstrap = reports.some((report) => report.scenario === "fresh-native"); const required = new Set(bootstrap ? ["fresh-native", "fresh-wsl"] diff --git a/scripts/github-workflows.test.ts b/scripts/github-workflows.test.ts index f8162598..2700d262 100644 --- a/scripts/github-workflows.test.ts +++ b/scripts/github-workflows.test.ts @@ -509,8 +509,13 @@ describe("GitHub workflow boundaries", () => { const validateReports = promotionSteps.find( (step) => step.name === "Validate promotion report identities" ); + const downloadChannel = promotionSteps.find( + (step) => step.name === "Download immutable channel identity" + ); const promote = promotionSteps.find((step) => step.name === "Promote existing prerelease"); + expect(downloadChannel?.run).toContain('--repo "${GITHUB_REPOSITORY}"'); expect(validateReports?.run).toContain("channelSignatureDigest"); + expect(validateReports?.run).toContain("text.charCodeAt(0) === 0xfeff"); expect(validateReports?.run).toContain("commitSha"); expect(validateReports?.run).toContain("wslRuntimeVersion"); expect(validateReports?.run).toContain("wsl-combined"); @@ -579,6 +584,7 @@ describe("GitHub workflow boundaries", () => { expect(steps[desktopReportIndex]?.run).toContain("wsl-combined"); expect(steps[desktopReportIndex]?.run).toContain("fresh-native"); expect(steps[desktopReportIndex]?.run).toContain("fresh-wsl"); + expect(steps[desktopReportIndex]?.run).toContain("text.charCodeAt(0) === 0xfeff"); expect(steps[preserveDesktopIndex]?.if).toBe("inputs.promote"); expect(steps[promoteIndex]?.run).toContain("npm dist-tag add"); expect(steps[promoteIndex]?.run).toContain("if ! npm dist-tag rm");