@kungfu-tech/buildchain 3.0.2-alpha.4 → 3.0.2-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/actions/github-artifact-attestation/README.md +10 -0
- package/actions/promote-buildchain-ref/README.md +7 -0
- package/bin/buildchain.mjs +5 -0
- package/bin/internal/trust-release-cli.mjs +74 -3
- package/dist/site/artifact-schemas.json +5 -1
- package/dist/site/buildchain-contract.json +79 -28
- package/dist/site/buildchain-site.json +110 -27
- package/dist/site/capability-registry.json +8 -7
- package/dist/site/cli-registry.json +12 -0
- package/dist/site/controller-registry.json +40 -4
- package/dist/site/kfd-claims.json +205 -19
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +19 -5
- package/dist/site/node-api-registry.json +22 -9
- package/dist/site/page-registry.json +91 -17
- package/dist/site/public-surface-audit.json +132 -17
- package/dist/site/publication-authority-registry.json +27 -2
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-model.json +2 -1
- package/dist/site/release-passport-check-manifest.json +1 -0
- package/dist/site/release-provenance.json +1 -0
- package/dist/site/schemas/release-passport-v1.schema.json +6 -0
- package/dist/site/site-manifest.json +17 -9
- package/dist/site/workflow-registry.json +91 -9
- package/docs/MAP.md +3 -1
- package/docs/binary-distribution.md +7 -0
- package/docs/cli.md +9 -0
- package/docs/dev-alpha-candidate-patrol.md +51 -13
- package/docs/github-artifact-attestation.md +219 -0
- package/docs/release-passport.md +13 -0
- package/docs/reusable-build-surface.md +6 -0
- package/package.json +2 -1
- package/packages/core/buildchain-contract.js +6 -0
- package/packages/core/buildchain-kfd-claims.js +5 -0
- package/packages/core/buildchain-publication-authority.js +1 -0
- package/packages/core/github-artifact-attestation.js +642 -0
- package/packages/core/index.js +19 -0
- package/packages/core/publication-authority.js +1 -1
- package/packages/core/release-passport-contract.js +2 -0
- package/packages/core/release-passport.js +60 -3
- package/scripts/check-inventory.mjs +4 -0
- package/scripts/create-github-artifact-attestation-policy.mjs +62 -0
- package/scripts/dev-alpha-candidate-patrol.mjs +471 -71
- package/scripts/generate-channel-promotion-workflow.mjs +6 -0
- package/scripts/generate-site-bundle.mjs +12 -0
- package/scripts/publish-github-artifact-attestation-evidence.mjs +201 -0
- package/scripts/release-candidate-resolver.mjs +12 -0
- package/scripts/stage-github-artifact-attestation-inputs.mjs +65 -0
|
@@ -21,6 +21,9 @@ import {
|
|
|
21
21
|
import { createSurfaceTimestampPolicy } from "./surface-manifest.js";
|
|
22
22
|
import { validatePublishEvidence as validateTransactionPublishEvidence } from "./publish-transaction.js";
|
|
23
23
|
import { normalizeControllerReceiptReferences } from "./controller-evidence.js";
|
|
24
|
+
import {
|
|
25
|
+
normalizeGitHubArtifactAttestationPolicy,
|
|
26
|
+
} from "./github-artifact-attestation.js";
|
|
24
27
|
|
|
25
28
|
export const RELEASE_PASSPORT_CONTRACT = "kungfu-buildchain-release-passport";
|
|
26
29
|
export const ARTIFACT_EVIDENCE_CONTRACT = "kungfu-buildchain-artifact-evidence";
|
|
@@ -1166,6 +1169,8 @@ export function createReleasePassport({
|
|
|
1166
1169
|
kfdAgentHubEvidencePath = "",
|
|
1167
1170
|
controllerReceipts = [],
|
|
1168
1171
|
controllerReceiptReferences = [],
|
|
1172
|
+
githubArtifactAttestations = [],
|
|
1173
|
+
checkedAt = "",
|
|
1169
1174
|
} = {}) {
|
|
1170
1175
|
const normalizedTag = nonEmptyString(tag, "tag");
|
|
1171
1176
|
const artifactEvidence = createArtifactEvidence({ assets, repository, tag: normalizedTag, sourceSha, workflow });
|
|
@@ -1185,7 +1190,7 @@ export function createReleasePassport({
|
|
|
1185
1190
|
? normalizeEvidenceDocument(distTagPromotionEvidence, "distTagPromotionEvidence")
|
|
1186
1191
|
: undefined;
|
|
1187
1192
|
const normalizedImpact = normalizeImpactLedger(impact, { tag: normalizedTag, line });
|
|
1188
|
-
const generatedAt = nowIso();
|
|
1193
|
+
const generatedAt = optionalString(checkedAt) || nowIso();
|
|
1189
1194
|
const kfd1Metadata = resolveKfd1Metadata();
|
|
1190
1195
|
const normalizedKfd1 = kfd1?.passportSection ? kfd1 : undefined;
|
|
1191
1196
|
const normalizedKfd3 = kfd3?.passportSection ? kfd3 : undefined;
|
|
@@ -1231,6 +1236,8 @@ export function createReleasePassport({
|
|
|
1231
1236
|
: [],
|
|
1232
1237
|
requirePassed: true,
|
|
1233
1238
|
});
|
|
1239
|
+
const normalizedGitHubArtifactAttestations = (githubArtifactAttestations || [])
|
|
1240
|
+
.map(normalizeGitHubArtifactAttestationPolicy);
|
|
1234
1241
|
const publishArtifacts = normalizedPublishEvidence?.artifacts || [];
|
|
1235
1242
|
const normalizedPublishSummary = normalizePublishSummary({
|
|
1236
1243
|
packageSet: normalizedPackageSet,
|
|
@@ -1360,6 +1367,9 @@ export function createReleasePassport({
|
|
|
1360
1367
|
...(normalizedKfdAgentHub ? { kfdAgentHub: normalizedKfdAgentHub } : {}),
|
|
1361
1368
|
...(invariantPassports ? { invariantPassports } : {}),
|
|
1362
1369
|
...(normalizedControllerReceipts.length > 0 ? { controllerReceipts: normalizedControllerReceipts } : {}),
|
|
1370
|
+
...(normalizedGitHubArtifactAttestations.length > 0
|
|
1371
|
+
? { githubArtifactAttestations: normalizedGitHubArtifactAttestations }
|
|
1372
|
+
: {}),
|
|
1363
1373
|
versionImpact: normalizedImpact.versionImpact,
|
|
1364
1374
|
surfaceImpacts: normalizedImpact.surfaceImpacts,
|
|
1365
1375
|
artifacts: [
|
|
@@ -1450,11 +1460,13 @@ export function collectGitHubReleasePassport({
|
|
|
1450
1460
|
invariantPassportCommand = "",
|
|
1451
1461
|
kfdAgentHubEvidenceJson = "",
|
|
1452
1462
|
controllerReceiptReferences = [],
|
|
1463
|
+
githubArtifactAttestationPolicyJsons = [],
|
|
1453
1464
|
basePassportJson = "",
|
|
1454
1465
|
requireBaseKfd = false,
|
|
1455
1466
|
releaseJsonExtra = "",
|
|
1456
1467
|
publishJson = "",
|
|
1457
1468
|
workflow = {},
|
|
1469
|
+
checkedAt = "",
|
|
1458
1470
|
} = {}) {
|
|
1459
1471
|
const release = parseJsonInput(releaseJson, {}, { cwd, label: "releaseJson" });
|
|
1460
1472
|
const releaseExtra = parseJsonInput(releaseJsonExtra, {}, { cwd, label: "releaseJsonExtra" });
|
|
@@ -1526,6 +1538,13 @@ export function collectGitHubReleasePassport({
|
|
|
1526
1538
|
undefined,
|
|
1527
1539
|
{ cwd, label: "kfdAgentHubEvidenceJson" },
|
|
1528
1540
|
);
|
|
1541
|
+
const githubArtifactAttestationPolicies = (githubArtifactAttestationPolicyJsons || [])
|
|
1542
|
+
.filter(Boolean)
|
|
1543
|
+
.map((policyJson) => parseJsonInput(policyJson, undefined, {
|
|
1544
|
+
cwd,
|
|
1545
|
+
label: "githubArtifactAttestationPolicyJsons entry",
|
|
1546
|
+
}))
|
|
1547
|
+
.map(normalizeGitHubArtifactAttestationPolicy);
|
|
1529
1548
|
const kfd3ArtifactWitnesses = [
|
|
1530
1549
|
...kfd3ArtifactWitnessMetas.map((meta) => meta.value),
|
|
1531
1550
|
...(kfd3ArtifactCommandMeta.value ? [kfd3ArtifactCommandMeta.value] : []),
|
|
@@ -1541,6 +1560,7 @@ export function collectGitHubReleasePassport({
|
|
|
1541
1560
|
];
|
|
1542
1561
|
const resolvedTag = tag || release.tag_name || release.name || "";
|
|
1543
1562
|
const resolvedOutputDir = path.resolve(cwd, outputDir);
|
|
1563
|
+
const resolvedCheckedAt = optionalString(checkedAt) || nowIso();
|
|
1544
1564
|
const productMechanism = defaultProductMechanism({ repository, productName });
|
|
1545
1565
|
const artifactEvidence = createArtifactEvidence({ assets, repository, tag: resolvedTag, sourceSha, workflow });
|
|
1546
1566
|
const bundledPublishEvidencePath = publishEvidenceMeta.value ? "evidence.json" : "";
|
|
@@ -1568,7 +1588,7 @@ export function collectGitHubReleasePassport({
|
|
|
1568
1588
|
matrixRoot: kfdSupportMatrixMeta.sha256 ? `sha256:${kfdSupportMatrixMeta.sha256}` : "",
|
|
1569
1589
|
gateResults: kfdProductGateMetas.map((meta) => meta.value),
|
|
1570
1590
|
expectedSourceSha: sourceSha,
|
|
1571
|
-
checkedAt:
|
|
1591
|
+
checkedAt: resolvedCheckedAt,
|
|
1572
1592
|
})
|
|
1573
1593
|
: undefined;
|
|
1574
1594
|
const passport = mergeAuthoritativePassportBase(createReleasePassport({
|
|
@@ -1623,9 +1643,11 @@ export function collectGitHubReleasePassport({
|
|
|
1623
1643
|
: undefined,
|
|
1624
1644
|
kfdAgentHubEvidencePath: kfdAgentHubEvidenceMeta.value ? "kfd-agent-hub-evidence.json" : "",
|
|
1625
1645
|
controllerReceiptReferences,
|
|
1646
|
+
githubArtifactAttestations: githubArtifactAttestationPolicies,
|
|
1626
1647
|
publishEvidencePath: publishEvidenceMeta.path ? path.relative(resolvedOutputDir, publishEvidenceMeta.path).split(path.sep).join("/") : "",
|
|
1627
1648
|
transactionStatePath: transactionMeta.path ? path.relative(resolvedOutputDir, transactionMeta.path).split(path.sep).join("/") : "",
|
|
1628
1649
|
workflow,
|
|
1650
|
+
checkedAt: resolvedCheckedAt,
|
|
1629
1651
|
}), basePassportMeta.value, { requireKfd: requireBaseKfd });
|
|
1630
1652
|
if (bundledPublishEvidencePath) {
|
|
1631
1653
|
passport.evidence.publishEvidence = bundledPublishEvidencePath;
|
|
@@ -1639,7 +1661,7 @@ export function collectGitHubReleasePassport({
|
|
|
1639
1661
|
productMechanism,
|
|
1640
1662
|
kfdAgentHubEvidence: kfdAgentHubEvidenceMeta.value,
|
|
1641
1663
|
kfdSupportEvidence: kfdSupport,
|
|
1642
|
-
checkedAt:
|
|
1664
|
+
checkedAt: resolvedCheckedAt,
|
|
1643
1665
|
});
|
|
1644
1666
|
const files = {
|
|
1645
1667
|
"product-mechanism.json": productMechanism,
|
|
@@ -1972,6 +1994,39 @@ export function createReleaseCheckReport({
|
|
|
1972
1994
|
issues.push(issue("error", "kfdSupport.section", "KFD support evidence is present without a release-passport projection"));
|
|
1973
1995
|
}
|
|
1974
1996
|
|
|
1997
|
+
for (const [index, value] of (passport?.githubArtifactAttestations || []).entries()) {
|
|
1998
|
+
try {
|
|
1999
|
+
const policy = normalizeGitHubArtifactAttestationPolicy(value);
|
|
2000
|
+
if (policy.caller.sourceSha !== String(passport?.release?.sourceSha || "").toLowerCase()) {
|
|
2001
|
+
issues.push(issue(
|
|
2002
|
+
"error",
|
|
2003
|
+
`githubArtifactAttestations[${index}].caller.sourceSha`,
|
|
2004
|
+
"attestation policy source SHA must match passport.release.sourceSha",
|
|
2005
|
+
));
|
|
2006
|
+
}
|
|
2007
|
+
const artifact = (passport?.artifacts || []).find((entry) => entry.name === policy.subject.name);
|
|
2008
|
+
if (!artifact) {
|
|
2009
|
+
issues.push(issue(
|
|
2010
|
+
"error",
|
|
2011
|
+
`githubArtifactAttestations[${index}].subject.name`,
|
|
2012
|
+
`attestation subject ${policy.subject.name} is absent from the Release Passport artifacts`,
|
|
2013
|
+
));
|
|
2014
|
+
} else if (artifact.sha256 !== policy.subject.digest.sha256) {
|
|
2015
|
+
issues.push(issue(
|
|
2016
|
+
"error",
|
|
2017
|
+
`githubArtifactAttestations[${index}].subject.digest`,
|
|
2018
|
+
`attestation subject ${policy.subject.name} digest differs from the Release Passport artifact`,
|
|
2019
|
+
));
|
|
2020
|
+
}
|
|
2021
|
+
} catch (error) {
|
|
2022
|
+
issues.push(issue(
|
|
2023
|
+
"error",
|
|
2024
|
+
`githubArtifactAttestations[${index}]`,
|
|
2025
|
+
error.message,
|
|
2026
|
+
));
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
|
|
1975
2030
|
const tag = passport?.release?.tag || "";
|
|
1976
2031
|
if (!tag) {
|
|
1977
2032
|
issues.push(issue("error", "release.tag", "release.tag is required"));
|
|
@@ -2422,6 +2477,7 @@ export async function verifyReleasePassport({
|
|
|
2422
2477
|
productMechanismLocation = "",
|
|
2423
2478
|
kfdAgentHubEvidenceLocation = "",
|
|
2424
2479
|
kfdSupportEvidenceLocation = "",
|
|
2480
|
+
checkedAt = nowIso(),
|
|
2425
2481
|
} = {}) {
|
|
2426
2482
|
const passport = await readJsonFromLocation(passportLocation);
|
|
2427
2483
|
const basePath = /^https?:\/\//.test(passportLocation) ? passportLocation : path.resolve(passportLocation);
|
|
@@ -2462,6 +2518,7 @@ export async function verifyReleasePassport({
|
|
|
2462
2518
|
productMechanism,
|
|
2463
2519
|
kfdAgentHubEvidence,
|
|
2464
2520
|
kfdSupportEvidence,
|
|
2521
|
+
checkedAt,
|
|
2465
2522
|
});
|
|
2466
2523
|
}
|
|
2467
2524
|
|
|
@@ -68,6 +68,7 @@ const requiredPaths = [
|
|
|
68
68
|
"docs/product-mechanism.md",
|
|
69
69
|
"docs/readme-badges.md",
|
|
70
70
|
"docs/release-passport.md",
|
|
71
|
+
"docs/github-artifact-attestation.md",
|
|
71
72
|
"docs/shifu-gate-profiles.md",
|
|
72
73
|
"docs/auditable-demo.md",
|
|
73
74
|
"docs/release-propagation.md",
|
|
@@ -358,6 +359,9 @@ if (rootPackage.exports?.["./kfd-gate"] !== "./packages/core/kfd-gate.js") {
|
|
|
358
359
|
if (rootPackage.exports?.["./release-passport"] !== "./packages/core/release-passport.js") {
|
|
359
360
|
throw new Error("root package must export @kungfu-tech/buildchain/release-passport");
|
|
360
361
|
}
|
|
362
|
+
if (rootPackage.exports?.["./github-artifact-attestation"] !== "./packages/core/github-artifact-attestation.js") {
|
|
363
|
+
throw new Error("root package must export @kungfu-tech/buildchain/github-artifact-attestation");
|
|
364
|
+
}
|
|
361
365
|
if (rootPackage.exports?.["./release-passport-contract"] !== "./packages/core/release-passport-contract.js") {
|
|
362
366
|
throw new Error("root package must export @kungfu-tech/buildchain/release-passport-contract");
|
|
363
367
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
GITHUB_ARTIFACT_ATTESTATION_WORKFLOW,
|
|
9
|
+
createGitHubArtifactAttestationPolicy,
|
|
10
|
+
githubArtifactAttestationSha256File,
|
|
11
|
+
} from "../packages/core/github-artifact-attestation.js";
|
|
12
|
+
|
|
13
|
+
function env(name, fallback = "") {
|
|
14
|
+
return String(process.env[name] || fallback).trim();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function required(name) {
|
|
18
|
+
const value = env(name);
|
|
19
|
+
if (!value) throw new Error(`${name} is required`);
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const cwd = path.resolve(env("BUILDCHAIN_SOURCE_CWD", "."));
|
|
24
|
+
const subjectRelativePath = required("BUILDCHAIN_GITHUB_ATTESTATION_SUBJECT_PATH").replace(/\\/g, "/");
|
|
25
|
+
const subjectPath = path.resolve(cwd, subjectRelativePath);
|
|
26
|
+
const manifestPath = path.resolve(required("BUILDCHAIN_GITHUB_ATTESTATION_PLATFORM_MANIFEST"));
|
|
27
|
+
const outputPath = path.resolve(required("BUILDCHAIN_GITHUB_ATTESTATION_POLICY_OUTPUT"));
|
|
28
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
29
|
+
const manifestEntry = (manifest.files || []).find((entry) => (
|
|
30
|
+
String(entry.path || "").replace(/\\/g, "/") === subjectRelativePath
|
|
31
|
+
));
|
|
32
|
+
if (!manifestEntry) throw new Error(`platform manifest does not contain ${subjectRelativePath}`);
|
|
33
|
+
const digest = githubArtifactAttestationSha256File(subjectPath);
|
|
34
|
+
const size = fs.statSync(subjectPath).size;
|
|
35
|
+
if (`sha256:${String(manifestEntry.sha256 || "").toLowerCase()}` !== digest || Number(manifestEntry.size) !== size) {
|
|
36
|
+
throw new Error("subject bytes do not match the final platform manifest");
|
|
37
|
+
}
|
|
38
|
+
const manifestDigest = githubArtifactAttestationSha256File(manifestPath);
|
|
39
|
+
const runtimeSha = required("BUILDCHAIN_RUNTIME_SHA").toLowerCase();
|
|
40
|
+
const signerSha = required("BUILDCHAIN_GITHUB_ATTESTATION_SIGNER_SHA").toLowerCase();
|
|
41
|
+
const policy = createGitHubArtifactAttestationPolicy({
|
|
42
|
+
subject: { name: path.basename(subjectPath), path: subjectRelativePath, size, digest },
|
|
43
|
+
caller: {
|
|
44
|
+
repository: required("BUILDCHAIN_SOURCE_REPOSITORY"),
|
|
45
|
+
sourceSha: required("BUILDCHAIN_SOURCE_SHA").toLowerCase(),
|
|
46
|
+
sourceTreeSha: required("BUILDCHAIN_SOURCE_TREE_SHA").toLowerCase(),
|
|
47
|
+
},
|
|
48
|
+
signer: {
|
|
49
|
+
repository: "kungfu-systems/buildchain",
|
|
50
|
+
workflowPath: GITHUB_ARTIFACT_ATTESTATION_WORKFLOW,
|
|
51
|
+
workflowDigest: signerSha,
|
|
52
|
+
},
|
|
53
|
+
build: {
|
|
54
|
+
platform: required("BUILDCHAIN_PLATFORM_ID"),
|
|
55
|
+
platformManifestDigest: manifestDigest,
|
|
56
|
+
runnerReceiptRoot: manifestDigest,
|
|
57
|
+
buildchainRuntimeSha: runtimeSha,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
61
|
+
fs.writeFileSync(outputPath, `${JSON.stringify(policy, null, 2)}\n`);
|
|
62
|
+
process.stdout.write(`github-artifact-attestation-policy=${outputPath}\n`);
|