feat: add fail-closed signed release provenance
This commit is contained in:
@@ -116,13 +116,20 @@ app.whenReady().then(async () => {
|
||||
target_commitish: commit,
|
||||
name: `ForgeFlow ${version}`,
|
||||
body,
|
||||
draft: false,
|
||||
draft: true,
|
||||
prerelease: false,
|
||||
}),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (release.draft !== true) {
|
||||
release = await api(baseUrl, token, `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/${release.id}`, {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ draft: true }),
|
||||
});
|
||||
}
|
||||
const binaries = [
|
||||
path.join(root, "dist", `ForgeFlow-Setup-${version}-win-x64.exe`),
|
||||
path.join(root, "dist", `ForgeFlow-Portable-${version}-win-x64.exe`),
|
||||
@@ -171,6 +178,30 @@ app.whenReady().then(async () => {
|
||||
console.log(`PASS published ${name}`);
|
||||
}
|
||||
}
|
||||
for (const [name, type] of [
|
||||
[`ForgeFlow-${version}-provenance.json`, "application/json"],
|
||||
[`ForgeFlow-${version}-sbom.cdx.json`, "application/vnd.cyclonedx+json"],
|
||||
]) {
|
||||
const bytes = await fs.readFile(path.join(root, "dist", name));
|
||||
const existing = (release.assets || []).find((asset) => asset.name === name);
|
||||
if (existing) await api(baseUrl, token, `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/${release.id}/assets/${existing.id}`, { method: "DELETE" });
|
||||
const form = new FormData();
|
||||
form.append("attachment", new Blob([bytes], { type }), name);
|
||||
const uploaded = await api(baseUrl, token, `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/${release.id}/assets?name=${encodeURIComponent(name)}`, { method: "POST", body: form, timeout: 300_000 });
|
||||
release.assets = [...(release.assets || []).filter((asset) => asset.name !== name), uploaded];
|
||||
}
|
||||
const requiredAssets = [
|
||||
...binaries.flatMap((binaryPath) => [path.basename(binaryPath), `${path.basename(binaryPath)}.sha256`]),
|
||||
`ForgeFlow-${version}-provenance.json`,
|
||||
`ForgeFlow-${version}-sbom.cdx.json`,
|
||||
];
|
||||
const missingAssets = requiredAssets.filter((name) => !(release.assets || []).some((asset) => asset.name === name));
|
||||
if (missingAssets.length) throw new Error(`Release remains draft because required assets are missing: ${missingAssets.join(", ")}`);
|
||||
release = await api(baseUrl, token, `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/releases/${release.id}`, {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ draft: false }),
|
||||
});
|
||||
console.log(
|
||||
`PASS ForgeFlow ${version} binary release published to ${owner}/${repo} for ${commit.slice(0, 7)}`,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user