From 0cd68568a6f4c8eb93c45f4a7fb373fee1a62ec6 Mon Sep 17 00:00:00 2001 From: Neko Ayaka Date: Mon, 23 Mar 2026 19:19:10 +0800 Subject: [PATCH] fix(ci): re-calculate Windows artifact hash Authored-by-agent: Codex <267193182+codex@users.noreply.github.com> --- apps/stage-tamagotchi/package.json | 1 + .../scripts/regenerate-windows-latest.ts | 110 ++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 apps/stage-tamagotchi/scripts/regenerate-windows-latest.ts diff --git a/apps/stage-tamagotchi/package.json b/apps/stage-tamagotchi/package.json index e4a2175a0..ecd96bc58 100644 --- a/apps/stage-tamagotchi/package.json +++ b/apps/stage-tamagotchi/package.json @@ -28,6 +28,7 @@ "build:linux": "pnpm run build && electron-builder --linux", "rename-artifacts": "mkdir -p bundle && tsx scripts/rename-artifacts.ts", "merge-latest-mac": "tsx scripts/merge-latest-mac.ts", + "regenerate-windows-latest": "tsx scripts/regenerate-windows-latest.ts", "artifacts-metadata": "tsx scripts/artifacts-metadata.ts" }, "dependencies": { diff --git a/apps/stage-tamagotchi/scripts/regenerate-windows-latest.ts b/apps/stage-tamagotchi/scripts/regenerate-windows-latest.ts new file mode 100644 index 000000000..804ade151 --- /dev/null +++ b/apps/stage-tamagotchi/scripts/regenerate-windows-latest.ts @@ -0,0 +1,110 @@ +import { createHash } from 'node:crypto' +import { createReadStream } from 'node:fs' +import { readFile, stat, writeFile } from 'node:fs/promises' +import { basename, resolve } from 'node:path' +import { exit } from 'node:process' + +import { cac } from 'cac' + +import * as yaml from 'yaml' + +interface UpdateFileInfo { + url: string + sha512: string + size?: number +} + +interface WindowsUpdateInfo { + version: string + files: UpdateFileInfo[] + path: string + sha512: string + sha2?: string + releaseDate?: string + [key: string]: unknown +} + +async function hashFile(filePath: string): Promise<{ sha512: string, sha256: string }> { + return await new Promise((resolveHash, reject) => { + const sha512 = createHash('sha512') + const sha256 = createHash('sha256') + const stream = createReadStream(filePath) + + stream.on('data', (chunk) => { + sha512.update(chunk) + sha256.update(chunk) + }) + stream.on('error', reject) + stream.on('end', () => { + resolveHash({ + sha512: sha512.digest('base64'), + sha256: sha256.digest('hex'), + }) + }) + }) +} + +async function readExistingUpdateInfo(filePath: string): Promise> { + try { + const raw = await readFile(filePath, 'utf8') + return (yaml.parse(raw) ?? {}) as Partial + } + catch { + return {} + } +} + +async function main() { + const cli = cac('regenerate-windows-latest') + .option('--input ', 'Signed Windows installer path', { type: [String] }) + .option('--output ', 'Output latest.yml path', { default: 'bundle/latest.yml' }) + .option('--version ', 'Version to write into latest.yml', { type: [String] }) + .option('--release-date ', 'Release date to write into latest.yml', { type: [String] }) + + const args = cli.parse() + + const input = String(args.options.input?.[0] || '').trim() + const output = String(args.options.output || '').trim() + const version = String(args.options.version?.[0] || '').trim() + const releaseDate = String(args.options.releaseDate?.[0] || '').trim() + + if (!input) { + throw new Error('--input is required') + } + if (!output) { + throw new Error('--output is required') + } + if (!version) { + throw new Error('--version is required') + } + + const inputPath = resolve(input) + const outputPath = resolve(output) + const fileStats = await stat(inputPath) + const { sha512, sha256 } = await hashFile(inputPath) + const existing = await readExistingUpdateInfo(outputPath) + const url = basename(inputPath) + + const nextUpdateInfo: WindowsUpdateInfo = { + ...existing, + version, + files: [ + { + url, + sha512, + size: fileStats.size, + }, + ], + path: url, + sha512, + sha2: sha256, + releaseDate: releaseDate || existing.releaseDate || new Date().toISOString(), + } + + await writeFile(outputPath, yaml.stringify(nextUpdateInfo), 'utf8') +} + +main().catch((error) => { + console.error(error) + exit(1) +})