mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-21 04:10:47 +02:00
feat: componentize GStack 2 runtime and release integrity
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env node
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
const [directory, repository = process.env.GITHUB_REPOSITORY, version = "2.0.0"] = process.argv.slice(2);
|
||||
if (!directory || !repository) {
|
||||
console.error("Usage: create-runtime-release-manifest.mjs <artifact-dir> <owner/repo> [version]");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const targets = [
|
||||
"darwin-arm64",
|
||||
"darwin-x64",
|
||||
"linux-arm64",
|
||||
"linux-x64",
|
||||
"windows-arm64",
|
||||
"windows-x64",
|
||||
];
|
||||
const componentDependencies = {
|
||||
core: [],
|
||||
"browser-code": ["core"],
|
||||
"browser-headless": ["browser-code"],
|
||||
"browser-visible": ["browser-code"],
|
||||
design: ["core"],
|
||||
diagram: ["browser-headless"],
|
||||
pdf: ["diagram"],
|
||||
ios: ["core"],
|
||||
};
|
||||
const capabilityComponents = {
|
||||
browser: ["browser-code", "browser-headless"],
|
||||
"browser-visible": ["browser-code", "browser-visible"],
|
||||
design: ["design"],
|
||||
diagram: ["diagram"],
|
||||
pdf: ["pdf"],
|
||||
ios: ["ios"],
|
||||
};
|
||||
const commonComponents = ["core", "browser-code", "browser-headless", "browser-visible", "design", "diagram", "pdf"];
|
||||
const release = `https://github.com/${repository}/releases/download/v${version}`;
|
||||
const certificateIdentity = `https://github.com/${repository}/.github/workflows/release-artifacts.yml@refs/tags/v${version}`;
|
||||
const targetRecords = {};
|
||||
|
||||
for (const target of targets) {
|
||||
const ids = [...commonComponents, ...(target.startsWith("darwin-") ? ["ios"] : [])];
|
||||
const components = {};
|
||||
for (const component of ids) {
|
||||
const name = `gstack-runtime-${version}-${target}-${component}.tar.gz`;
|
||||
const archive = path.join(directory, name);
|
||||
const bundle = `${archive}.sigstore.json`;
|
||||
const digestFile = `${archive}.sha256`;
|
||||
const [stat, digest, bundleStat] = await Promise.all([
|
||||
fs.stat(archive),
|
||||
fs.readFile(digestFile, "utf8"),
|
||||
fs.stat(bundle),
|
||||
]);
|
||||
const sha256 = digest.trim().split(/\s+/)[0];
|
||||
if (!stat.isFile() || stat.size <= 0 || stat.size > 2 * 1024 * 1024 * 1024) {
|
||||
throw new Error(`Invalid artifact size for ${name}: ${stat.size}`);
|
||||
}
|
||||
if (!bundleStat.isFile() || bundleStat.size <= 0) throw new Error(`Missing Sigstore bundle for ${name}`);
|
||||
if (!/^[a-f0-9]{64}$/.test(sha256)) throw new Error(`Invalid SHA-256 for ${name}`);
|
||||
components[component] = {
|
||||
url: `${release}/${name}`,
|
||||
sha256,
|
||||
bytes: stat.size,
|
||||
format: "tar.gz",
|
||||
root: "gstack",
|
||||
cosignBundleUrl: `${release}/${name}.sigstore.json`,
|
||||
certificateIdentity,
|
||||
certificateOidcIssuer: "https://token.actions.githubusercontent.com",
|
||||
};
|
||||
}
|
||||
targetRecords[target] = { components };
|
||||
}
|
||||
|
||||
const manifest = {
|
||||
schemaVersion: 2,
|
||||
version,
|
||||
skillApi: "2.0",
|
||||
capabilityComponents,
|
||||
componentDependencies,
|
||||
targets: targetRecords,
|
||||
};
|
||||
await fs.writeFile(
|
||||
path.join(directory, "gstack-runtime-manifest.json"),
|
||||
`${JSON.stringify(manifest, null, 2)}\n`,
|
||||
{ flag: "wx", mode: 0o644 },
|
||||
);
|
||||
Reference in New Issue
Block a user