Files
tutti/apps/desktop/scripts/validate-windows-release-artifacts.mjs
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

152 lines
4.5 KiB
JavaScript

#!/usr/bin/env node
import { createHash } from "node:crypto";
import { createReadStream } from "node:fs";
import { readdir, readFile } from "node:fs/promises";
import path from "node:path";
import { pathToFileURL } from "node:url";
function parseScalar(rawValue) {
const value = rawValue.trim();
if (value.startsWith('"')) {
return JSON.parse(value);
}
if (value.startsWith("'") && value.endsWith("'")) {
return value.slice(1, -1).replaceAll("''", "'");
}
return value;
}
function parseUpdaterMetadata(source) {
const metadata = { files: [] };
let inFiles = false;
let currentFile;
for (const line of source.split(/\r?\n/u)) {
const topLevel = /^(\S[^:]*):(?:\s*(.*))?$/u.exec(line);
if (topLevel) {
inFiles = topLevel[1] === "files";
currentFile = undefined;
if (["path", "sha512", "version"].includes(topLevel[1])) {
metadata[topLevel[1]] = parseScalar(topLevel[2] ?? "");
}
continue;
}
if (!inFiles) continue;
const fileStart = /^\s+-\s+url:\s*(.+)$/u.exec(line);
if (fileStart) {
currentFile = { url: parseScalar(fileStart[1]) };
metadata.files.push(currentFile);
continue;
}
const fileSha = /^\s+sha512:\s*(.+)$/u.exec(line);
if (fileSha && currentFile) {
currentFile.sha512 = parseScalar(fileSha[1]);
}
}
return metadata;
}
async function sha512(filePath) {
const hash = createHash("sha512");
for await (const chunk of createReadStream(filePath)) hash.update(chunk);
return hash.digest("base64");
}
async function validateWindowsReleaseArtifacts(directory, channel, releaseTag) {
if (!new Set(["stable", "rc", "beta"]).has(channel)) {
throw new Error(`Unsupported Windows release channel: ${channel}`);
}
const versionMatch =
/^v(?<version>\d+\.\d+\.\d+(?:-(?:rc|beta)\.\d+)?)$/u.exec(releaseTag);
if (!versionMatch?.groups) {
throw new Error(`Unsupported Windows release tag: ${releaseTag}`);
}
const names = await readdir(directory);
const installers = names.filter((name) => name.endsWith("-win-x64.exe"));
if (installers.length !== 1) {
throw new Error(
`Expected exactly one Windows x64 installer, found ${installers.length}`
);
}
const installerName = installers[0];
const blockmapName = `${installerName}.blockmap`;
if (!names.includes(blockmapName)) {
throw new Error(`Missing Windows blockmap: ${blockmapName}`);
}
const metadataName = channel === "stable" ? "latest.yml" : `${channel}.yml`;
if (!names.includes(metadataName)) {
throw new Error(`Missing Windows updater metadata: ${metadataName}`);
}
const metadata = parseUpdaterMetadata(
await readFile(path.join(directory, metadataName), "utf8")
);
const expectedVersion = versionMatch.groups.version;
if (metadata.version !== expectedVersion) {
throw new Error(
`Windows updater version mismatch: expected ${expectedVersion}, found ${metadata.version}`
);
}
if (metadata.path !== installerName) {
throw new Error(
`Windows updater path mismatch: expected ${installerName}, found ${metadata.path}`
);
}
const installerSha512 = await sha512(path.join(directory, installerName));
if (metadata.sha512 !== installerSha512) {
throw new Error(
"Windows updater top-level SHA-512 does not match installer"
);
}
if (metadata.files.length !== 1) {
throw new Error(
`Expected exactly one Windows updater file entry, found ${metadata.files.length}`
);
}
const [file] = metadata.files;
if (file.url !== installerName) {
throw new Error(
`Windows updater file URL mismatch: expected ${installerName}, found ${file.url}`
);
}
if (file.sha512 !== installerSha512) {
throw new Error("Windows updater file SHA-512 does not match installer");
}
return {
blockmapName,
installerName,
metadataName,
version: expectedVersion
};
}
async function main() {
const [directory, channel, releaseTag] = process.argv.slice(2);
if (!directory || !channel || !releaseTag) {
throw new Error(
"Usage: validate-windows-release-artifacts.mjs <directory> <stable|rc|beta> <release-tag>"
);
}
await validateWindowsReleaseArtifacts(directory, channel, releaseTag);
}
if (
process.argv[1] &&
import.meta.url === pathToFileURL(process.argv[1]).href
) {
void main().catch((error) => {
process.stderr.write(
`${error instanceof Error ? error.message : String(error)}\n`
);
process.exit(1);
});
}
export { parseUpdaterMetadata, validateWindowsReleaseArtifacts };