Files
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

100 lines
3.1 KiB
JavaScript

import { createHash } from "node:crypto";
const candidateSchemaVersion = "tutti.desktop.release.candidate.v1";
function sha256(value) {
return createHash("sha256").update(value).digest("hex");
}
function requireString(value, label) {
const normalized = String(value ?? "").trim();
if (!normalized) {
throw new Error(`${label} is required`);
}
return normalized;
}
function createReleaseCandidateManifest(input) {
const tag = requireString(input.tag, "tag");
const version = requireString(input.version, "version");
const candidateId = requireString(input.candidateId, "candidateId");
if (tag !== `v${version}` || !/^v\d+\.\d+\.\d+$/u.test(tag)) {
throw new Error("stable candidate tag and version must match");
}
if (!candidateId.startsWith(`${tag}-`)) {
throw new Error("candidateId must start with the planned tag");
}
const targetCommit = requireString(input.targetCommit, "targetCommit");
if (!/^[a-f0-9]{40}$/u.test(targetCommit)) {
throw new Error("targetCommit must be a full Git commit SHA");
}
return {
schemaVersion: candidateSchemaVersion,
candidateId,
tag,
version,
channel: "stable",
targetCommit,
sourceRef: requireString(input.sourceRef, "sourceRef"),
createdAt: requireString(input.createdAt, "createdAt"),
workflowRunUrl: requireString(input.workflowRunUrl, "workflowRunUrl"),
checksumsSha256: sha256(requireString(input.checksums, "checksums")),
generatedSummarySha256: sha256(
requireString(input.generatedSummary, "generatedSummary")
)
};
}
function validateReleaseCandidateManifest(manifest, expected = {}) {
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
throw new Error("candidate manifest must be an object");
}
if (manifest.schemaVersion !== candidateSchemaVersion) {
throw new Error(`unexpected candidate schema: ${manifest.schemaVersion}`);
}
const normalized = createReleaseCandidateManifest({
...manifest,
checksums: expected.checksums,
generatedSummary: expected.generatedSummary
});
for (const [field, expectedValue] of Object.entries(expected)) {
if (
field !== "checksums" &&
field !== "generatedSummary" &&
expectedValue &&
normalized[field] !== expectedValue
) {
throw new Error(`candidate manifest ${field} does not match`);
}
}
if (manifest.checksumsSha256 !== normalized.checksumsSha256) {
throw new Error("candidate checksums digest does not match");
}
if (manifest.generatedSummarySha256 !== normalized.generatedSummarySha256) {
throw new Error("candidate summary digest does not match");
}
return manifest;
}
function buildReleaseApprovalDigest(manifest, approvedSummary) {
return sha256(
JSON.stringify({
candidateId: manifest.candidateId,
checksumsSha256: manifest.checksumsSha256,
generatedSummarySha256: manifest.generatedSummarySha256,
tag: manifest.tag,
targetCommit: manifest.targetCommit,
approvedSummary
})
);
}
export {
buildReleaseApprovalDigest,
candidateSchemaVersion,
createReleaseCandidateManifest,
validateReleaseCandidateManifest
};