@kungfu-tech/buildchain 4.0.0 → 4.0.1-alpha.2
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/AGENTS.md +13 -5
- package/actions/promote-buildchain-ref/README.md +11 -6
- package/architecture/internal-capabilities.json +11 -2
- package/architecture/maintainability-policy.json +149 -17
- package/architecture/v4-floating-consumer-policy.json +75 -0
- package/architecture/v4-floating-consumer-policy.md +32 -0
- package/architecture/v4-runtime-ref-resume-authority.json +64 -0
- package/architecture/v4-stage-capsule-qualification.json +2 -2
- package/bin/internal/trust-release-release-handlers.mjs +5 -0
- package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +53 -0
- package/contracts/fixtures/v4-runtime-ref-resume-authority-v1/scenario.json +15 -0
- package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +105 -0
- package/contracts/v4-runtime-ref-resume-authority-v1.schema.json +235 -0
- package/dist/site/buildchain-contract.json +101 -31
- package/dist/site/buildchain-site.json +73 -27
- package/dist/site/capability-registry.json +3 -3
- package/dist/site/controller-registry.json +62 -6
- package/dist/site/kfd-claims.json +68 -13
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +6 -6
- package/dist/site/node-api-registry.json +799 -127
- package/dist/site/page-registry.json +63 -17
- package/dist/site/public-surface-audit.json +49 -17
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +2 -0
- package/dist/site/site-manifest.json +10 -10
- package/dist/site/workflow-registry.json +46 -18
- package/docs/MAP.md +2 -0
- package/docs/dev-delivery-warrant.md +19 -1
- package/docs/lifecycle-protocol.md +41 -0
- package/docs/node-api-reference.md +207 -134
- package/docs/reusable-build-surface.md +41 -0
- package/docs/runtime-train-validation.md +10 -0
- package/docs/v4-runtime-ref-resume-authority.md +69 -0
- package/docs/versioning.md +1 -0
- package/package.json +6 -4
- package/packages/core/artifact-signing.js +61 -0
- package/packages/core/buildchain-config.js +66 -1
- package/packages/core/dev-delivery-proof.js +37 -3
- package/packages/core/dev-delivery-warrant.js +3 -3
- package/packages/core/index.js +2 -0
- package/packages/core/publication-authority.js +5 -5
- package/packages/core/release-candidate.js +79 -19
- package/packages/core/release-passport.js +239 -33
- package/packages/core/v4-floating-consumer-evidence.js +324 -0
- package/packages/core/v4-floating-consumer-policy.js +446 -0
- package/packages/core/v4-floating-consumer-release-passport.js +126 -0
- package/packages/core/v4-runtime-ref-resume-authority.js +625 -0
- package/packages/core/v4-runtime-selector-persistence.js +228 -0
- package/packages/core/workflow-yaml-contract.js +48 -0
- package/scripts/audit-publication-control-plane.mjs +31 -31
- package/scripts/check-inventory.mjs +1 -1
- package/scripts/check-v4-floating-consumer-policy-contract.mjs +198 -0
- package/scripts/check-v4-public-dogfood-contract.mjs +6 -11
- package/scripts/dev-delivery-source-proof-reuse.mjs +631 -0
- package/scripts/dev-pr-auto-merge.mjs +37 -48
- package/scripts/dev-pr-delivery-warrant.mjs +205 -2
- package/scripts/dev-pr-prequeue-guard.mjs +399 -0
- package/scripts/ensure-github-release.mjs +3 -3
- package/scripts/generate-channel-build-workflow.mjs +3 -0
- package/scripts/generate-channel-promotion-workflow.mjs +112 -12
- package/scripts/generate-release-candidate-passport.mjs +41 -33
- package/scripts/init-repo.mjs +5 -1
- package/scripts/inspect-artifact-signing-requests.mjs +6 -0
- package/scripts/npm-publish-transaction.mjs +101 -89
- package/scripts/resume-from-candidate-run.mjs +11 -14
- package/scripts/seal-artifact-signing-requests.mjs +6 -0
- package/scripts/site-capability-metadata.mjs +2 -0
- package/scripts/v4-consumer-policy.mjs +231 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
|
+
import { verifyV4FloatingConsumerPolicyReceipt } from "./v4-floating-consumer-policy.js";
|
|
2
3
|
import { normalizeControllerReceiptReferences, validateControllerReceiptReference } from "./controller-evidence.js";
|
|
3
4
|
|
|
4
5
|
export const RELEASE_CANDIDATE_PASSPORT_CONTRACT = "kungfu-buildchain-release-candidate-passport";
|
|
@@ -171,6 +172,64 @@ function normalizeGateProfileEvidence(gateAggregate = undefined) {
|
|
|
171
172
|
};
|
|
172
173
|
}
|
|
173
174
|
|
|
175
|
+
function isV4BuildchainRuntime(buildchain = {}) {
|
|
176
|
+
return [buildchain.ref, buildchain.workflowShellRef]
|
|
177
|
+
.some((value) => /^v4(?:-alpha)?$/u.test(String(value || "")))
|
|
178
|
+
|| /^4\./u.test(String(buildchain.version || ""));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function normalizeConsumerPolicyEvidence(value, expected = {}) {
|
|
182
|
+
if (value === undefined || value === null || value === "") return undefined;
|
|
183
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
184
|
+
throw new Error("consumerPolicyReceipt must be an object");
|
|
185
|
+
}
|
|
186
|
+
const receipt = value.receipt || value;
|
|
187
|
+
const verification = verifyV4FloatingConsumerPolicyReceipt({
|
|
188
|
+
receipt,
|
|
189
|
+
receiptRoot: value.receiptRoot || "",
|
|
190
|
+
repository: expected.repository,
|
|
191
|
+
sourceSha: expected.sourceSha,
|
|
192
|
+
resolvedRuntimeSha: expected.runtimeSha,
|
|
193
|
+
});
|
|
194
|
+
if (!verification.ok) {
|
|
195
|
+
throw new Error(`consumer policy receipt invalid: ${verification.failures.map((failure) => failure.code).join(", ")}`);
|
|
196
|
+
}
|
|
197
|
+
return { receiptRoot: verification.receiptRoot, receipt };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function validateConsumerPolicyEvidence(passport, check) {
|
|
201
|
+
if (isV4BuildchainRuntime(passport.buildchain)) {
|
|
202
|
+
check(Boolean(passport.consumerPolicy), "Buildchain v4 release candidate requires consumer policy evidence");
|
|
203
|
+
}
|
|
204
|
+
if (!passport.consumerPolicy) return;
|
|
205
|
+
const verification = verifyV4FloatingConsumerPolicyReceipt({
|
|
206
|
+
receipt: passport.consumerPolicy.receipt,
|
|
207
|
+
receiptRoot: passport.consumerPolicy.receiptRoot,
|
|
208
|
+
repository: passport.repository,
|
|
209
|
+
sourceSha: passport.source?.headSha,
|
|
210
|
+
resolvedRuntimeSha: passport.buildchain?.sha,
|
|
211
|
+
});
|
|
212
|
+
for (const failure of verification.failures) {
|
|
213
|
+
check(false, `consumer policy evidence invalid: ${failure.code}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function validateEvidenceBoundCandidateHash(passport, check) {
|
|
218
|
+
if (!passport.familyEvidence && !passport.consumerPolicy) return;
|
|
219
|
+
const expectedCandidateHash = sha256Json({
|
|
220
|
+
repository: passport.repository,
|
|
221
|
+
target: passport.target,
|
|
222
|
+
source: passport.source,
|
|
223
|
+
platformMatrix: passport.platformMatrix,
|
|
224
|
+
buildchain: passport.buildchain,
|
|
225
|
+
...(passport.gateProfileEvidence ? { gateProfileEvidence: passport.gateProfileEvidence } : {}),
|
|
226
|
+
...(passport.familyEvidence ? { familyEvidence: passport.familyEvidence } : {}),
|
|
227
|
+
...(passport.consumerPolicy ? { consumerPolicy: passport.consumerPolicy } : {}),
|
|
228
|
+
...(passport.controllerReceipts ? { controllerReceipts: passport.controllerReceipts } : {}),
|
|
229
|
+
});
|
|
230
|
+
check(passport.candidateHash === expectedCandidateHash, "candidate hash mismatch");
|
|
231
|
+
}
|
|
232
|
+
|
|
174
233
|
function normalizeFamilyReleaseEvidence(value = undefined) {
|
|
175
234
|
if (value === undefined || value === null || value === "") return undefined;
|
|
176
235
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -320,6 +379,7 @@ export function createReleaseCandidatePassport({
|
|
|
320
379
|
buildchain = {},
|
|
321
380
|
gateAggregate = undefined,
|
|
322
381
|
familyEvidence = undefined,
|
|
382
|
+
consumerPolicyReceipt = undefined,
|
|
323
383
|
controllerReceipts = [],
|
|
324
384
|
controllerReceiptReferences = [],
|
|
325
385
|
workflow = {},
|
|
@@ -335,6 +395,20 @@ export function createReleaseCandidatePassport({
|
|
|
335
395
|
const resolvedVersion = version || normalizedSummary.publishSource?.consumerVersion || inferVersionFromReleaseManifest(normalizedSummary);
|
|
336
396
|
const gateProfileEvidence = normalizeGateProfileEvidence(gateAggregate);
|
|
337
397
|
const normalizedFamilyEvidence = normalizeFamilyReleaseEvidence(familyEvidence);
|
|
398
|
+
const resolvedBuildchain = {
|
|
399
|
+
ref: optionalString(buildchain.ref || normalizedSummary.runtime?.ref),
|
|
400
|
+
sha: optionalString(buildchain.sha || normalizedSummary.runtime?.sha),
|
|
401
|
+
version: optionalString(buildchain.version),
|
|
402
|
+
workflowShellRef: optionalString(buildchain.workflowShellRef || normalizedSummary.runtime?.workflowShellRef),
|
|
403
|
+
};
|
|
404
|
+
const normalizedConsumerPolicy = normalizeConsumerPolicyEvidence(consumerPolicyReceipt, {
|
|
405
|
+
repository: repository || normalizedSummary.git?.repository || "",
|
|
406
|
+
sourceSha,
|
|
407
|
+
runtimeSha: resolvedBuildchain.sha,
|
|
408
|
+
});
|
|
409
|
+
if (isV4BuildchainRuntime(resolvedBuildchain) && !normalizedConsumerPolicy) {
|
|
410
|
+
throw new Error("Buildchain v4 release candidate passport requires a valid floating consumer policy receipt");
|
|
411
|
+
}
|
|
338
412
|
const controllerReceiptEvidence = normalizeControllerReceiptReferences({
|
|
339
413
|
receipts: controllerReceipts,
|
|
340
414
|
references: controllerReceiptReferences,
|
|
@@ -366,12 +440,7 @@ export function createReleaseCandidatePassport({
|
|
|
366
440
|
builtSourceSha: nonEmptyString(mergeRefSha || sourceSha, "builtSourceSha"),
|
|
367
441
|
builtSourceTreeSha: resolvedTreeHash,
|
|
368
442
|
},
|
|
369
|
-
buildchain:
|
|
370
|
-
ref: optionalString(buildchain.ref || normalizedSummary.runtime?.ref),
|
|
371
|
-
sha: optionalString(buildchain.sha || normalizedSummary.runtime?.sha),
|
|
372
|
-
version: optionalString(buildchain.version),
|
|
373
|
-
workflowShellRef: optionalString(buildchain.workflowShellRef || normalizedSummary.runtime?.workflowShellRef),
|
|
374
|
-
},
|
|
443
|
+
buildchain: resolvedBuildchain,
|
|
375
444
|
workflow: {
|
|
376
445
|
name: optionalString(workflow.name),
|
|
377
446
|
runId: optionalString(workflow.runId || normalizedSummary.git?.runId),
|
|
@@ -386,6 +455,7 @@ export function createReleaseCandidatePassport({
|
|
|
386
455
|
},
|
|
387
456
|
...(gateProfileEvidence ? { gateProfileEvidence } : {}),
|
|
388
457
|
...(normalizedFamilyEvidence ? { familyEvidence: normalizedFamilyEvidence } : {}),
|
|
458
|
+
...(normalizedConsumerPolicy ? { consumerPolicy: normalizedConsumerPolicy } : {}),
|
|
389
459
|
...(controllerReceiptEvidence.length > 0 ? { controllerReceipts: controllerReceiptEvidence } : {}),
|
|
390
460
|
};
|
|
391
461
|
candidate.candidateHash = sha256Json({
|
|
@@ -396,6 +466,7 @@ export function createReleaseCandidatePassport({
|
|
|
396
466
|
buildchain: candidate.buildchain,
|
|
397
467
|
...(candidate.gateProfileEvidence ? { gateProfileEvidence: candidate.gateProfileEvidence } : {}),
|
|
398
468
|
...(candidate.familyEvidence ? { familyEvidence: candidate.familyEvidence } : {}),
|
|
469
|
+
...(candidate.consumerPolicy ? { consumerPolicy: candidate.consumerPolicy } : {}),
|
|
399
470
|
...(candidate.controllerReceipts ? { controllerReceipts: candidate.controllerReceipts } : {}),
|
|
400
471
|
});
|
|
401
472
|
return candidate;
|
|
@@ -463,6 +534,7 @@ export function validateReleaseCandidatePassport({
|
|
|
463
534
|
check(Boolean(passport.gateProfileEvidence.digest), "gate profile evidence digest is required");
|
|
464
535
|
check(Boolean(passport.gateProfileEvidence.matrixDigest), "gate profile matrix digest is required");
|
|
465
536
|
}
|
|
537
|
+
validateConsumerPolicyEvidence(passport, check);
|
|
466
538
|
if (requireFamilyEvidence || familyEvidenceRoot || familyInitiativeId || familyAssignmentId) {
|
|
467
539
|
check(Boolean(passport.familyEvidence), "family evidence is required");
|
|
468
540
|
}
|
|
@@ -514,18 +586,6 @@ export function validateReleaseCandidatePassport({
|
|
|
514
586
|
controllerIds.add(reference.controllerId);
|
|
515
587
|
}
|
|
516
588
|
}
|
|
517
|
-
|
|
518
|
-
const expectedCandidateHash = sha256Json({
|
|
519
|
-
repository: passport.repository,
|
|
520
|
-
target: passport.target,
|
|
521
|
-
source: passport.source,
|
|
522
|
-
platformMatrix: passport.platformMatrix,
|
|
523
|
-
buildchain: passport.buildchain,
|
|
524
|
-
...(passport.gateProfileEvidence ? { gateProfileEvidence: passport.gateProfileEvidence } : {}),
|
|
525
|
-
familyEvidence: passport.familyEvidence,
|
|
526
|
-
...(passport.controllerReceipts ? { controllerReceipts: passport.controllerReceipts } : {}),
|
|
527
|
-
});
|
|
528
|
-
check(passport.candidateHash === expectedCandidateHash, "candidate hash mismatch");
|
|
529
|
-
}
|
|
589
|
+
validateEvidenceBoundCandidateHash(passport, check);
|
|
530
590
|
return { ok: errors.length === 0, errors };
|
|
531
591
|
}
|
|
@@ -33,6 +33,16 @@ import { normalizeControllerReceiptReferences } from "./controller-evidence.js";
|
|
|
33
33
|
import {
|
|
34
34
|
normalizeGitHubArtifactAttestationPolicy,
|
|
35
35
|
} from "./github-artifact-attestation.js";
|
|
36
|
+
import { verifyV4FloatingConsumerPolicyCertification } from "./v4-floating-consumer-evidence.js";
|
|
37
|
+
import {
|
|
38
|
+
isV4PromotionRouting,
|
|
39
|
+
releasePassportCertificationVerificationOptions,
|
|
40
|
+
requireV4ConsumerPolicyCertification,
|
|
41
|
+
} from "./v4-floating-consumer-release-passport.js";
|
|
42
|
+
import {
|
|
43
|
+
verifyV4RuntimeAuthorizationReceipt,
|
|
44
|
+
verifyV4RuntimeResumeLineage,
|
|
45
|
+
} from "./v4-runtime-ref-resume-authority.js";
|
|
36
46
|
|
|
37
47
|
export { RELEASE_CHECK_REPORT_CONTRACT, RELEASE_PASSPORT_CONTRACT };
|
|
38
48
|
export const ARTIFACT_EVIDENCE_CONTRACT = "kungfu-buildchain-artifact-evidence";
|
|
@@ -390,6 +400,13 @@ function parseJsonInputWithMeta(value, fallback = undefined, { cwd = process.cwd
|
|
|
390
400
|
};
|
|
391
401
|
}
|
|
392
402
|
|
|
403
|
+
function parseV4ConsumerPolicyCertification(input, cwd) {
|
|
404
|
+
return parseJsonInputWithMeta(input, undefined, {
|
|
405
|
+
cwd,
|
|
406
|
+
label: "v4ConsumerPolicyCertificationJson",
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
393
410
|
function mergeAuthoritativePassportBase(passport, basePassport = undefined, { requireKfd = false } = {}) {
|
|
394
411
|
if (!basePassport) {
|
|
395
412
|
if (requireKfd) {
|
|
@@ -1085,6 +1102,123 @@ function normalizePromotionRouting(value = undefined) {
|
|
|
1085
1102
|
return JSON.parse(JSON.stringify(value));
|
|
1086
1103
|
}
|
|
1087
1104
|
|
|
1105
|
+
function normalizePromotionEvidence({
|
|
1106
|
+
release = {},
|
|
1107
|
+
v4ConsumerPolicyCertification: certification,
|
|
1108
|
+
v4ConsumerPolicyCertificationRoot: certificationRoot,
|
|
1109
|
+
repository,
|
|
1110
|
+
sourceSha,
|
|
1111
|
+
cwd,
|
|
1112
|
+
}) {
|
|
1113
|
+
const routing = normalizePromotionRouting(release.promotionRouting);
|
|
1114
|
+
return {
|
|
1115
|
+
routing,
|
|
1116
|
+
consumerPolicy: requireV4ConsumerPolicyCertification({
|
|
1117
|
+
value: certification,
|
|
1118
|
+
repository,
|
|
1119
|
+
sourceSha,
|
|
1120
|
+
routing,
|
|
1121
|
+
cwd,
|
|
1122
|
+
certificationRoot,
|
|
1123
|
+
}),
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
function normalizeV4RuntimeResumeEvidence(value, expected = {}) {
|
|
1128
|
+
if (value === undefined || value === null || value === "") return undefined;
|
|
1129
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1130
|
+
throw new Error("v4RuntimeResumeEvidence must be a JSON object");
|
|
1131
|
+
}
|
|
1132
|
+
const authorizationVerification = verifyV4RuntimeAuthorizationReceipt({
|
|
1133
|
+
receipt: value.authorization,
|
|
1134
|
+
receiptRoot: value.authorizationRoot,
|
|
1135
|
+
repository: expected.repository,
|
|
1136
|
+
sourceSha: expected.sourceSha,
|
|
1137
|
+
runtimeSha: expected.resumeRuntimeSha,
|
|
1138
|
+
consumerPolicyReceiptRoot: expected.consumerPolicyReceiptRoot,
|
|
1139
|
+
});
|
|
1140
|
+
if (!authorizationVerification.ok) {
|
|
1141
|
+
throw new Error(`v4 runtime authorization invalid: ${authorizationVerification.failures.join(", ")}`);
|
|
1142
|
+
}
|
|
1143
|
+
const lineageVerification = verifyV4RuntimeResumeLineage({
|
|
1144
|
+
lineage: value.lineage,
|
|
1145
|
+
lineageRoot: value.lineageRoot,
|
|
1146
|
+
repository: expected.repository,
|
|
1147
|
+
sourceSha: expected.sourceSha,
|
|
1148
|
+
resumeRuntimeSha: expected.resumeRuntimeSha,
|
|
1149
|
+
consumerPolicyReceiptRoot: expected.consumerPolicyReceiptRoot,
|
|
1150
|
+
});
|
|
1151
|
+
if (!lineageVerification.ok) {
|
|
1152
|
+
throw new Error(`v4 runtime resume lineage invalid: ${lineageVerification.failures.join(", ")}`);
|
|
1153
|
+
}
|
|
1154
|
+
if (value.lineage?.authorizationRoot !== value.authorizationRoot) {
|
|
1155
|
+
throw new Error("v4 runtime resume lineage authorization root mismatch");
|
|
1156
|
+
}
|
|
1157
|
+
return {
|
|
1158
|
+
authorizationRoot: value.authorizationRoot,
|
|
1159
|
+
authorization: structuredClone(value.authorization),
|
|
1160
|
+
lineageRoot: value.lineageRoot,
|
|
1161
|
+
lineage: structuredClone(value.lineage),
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
function prepareBuildEvidence({
|
|
1166
|
+
buildSummary,
|
|
1167
|
+
buildFacts,
|
|
1168
|
+
platformArtifactManifests,
|
|
1169
|
+
distTagPromotionEvidence,
|
|
1170
|
+
}) {
|
|
1171
|
+
return {
|
|
1172
|
+
buildSummary: buildSummary
|
|
1173
|
+
? normalizeEvidenceDocument(buildSummary, "buildSummary")
|
|
1174
|
+
: undefined,
|
|
1175
|
+
buildFacts: (buildFacts || [])
|
|
1176
|
+
.map((value, index) =>
|
|
1177
|
+
normalizeEvidenceDocument(value, `buildFacts[${index}]`),
|
|
1178
|
+
)
|
|
1179
|
+
.filter(Boolean),
|
|
1180
|
+
platformArtifactManifests: (platformArtifactManifests || [])
|
|
1181
|
+
.map((manifest, index) =>
|
|
1182
|
+
normalizePlatformArtifactManifest(manifest, index),
|
|
1183
|
+
)
|
|
1184
|
+
.filter(Boolean),
|
|
1185
|
+
distTagPromotionEvidence: distTagPromotionEvidence
|
|
1186
|
+
? normalizeEvidenceDocument(
|
|
1187
|
+
distTagPromotionEvidence,
|
|
1188
|
+
"distTagPromotionEvidence",
|
|
1189
|
+
)
|
|
1190
|
+
: undefined,
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
function preparePromotionBuild(options) {
|
|
1195
|
+
return {
|
|
1196
|
+
promotion: normalizePromotionEvidence(options),
|
|
1197
|
+
buildEvidence: prepareBuildEvidence(options),
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
function normalizeReleaseSourceFields(release) {
|
|
1202
|
+
return {
|
|
1203
|
+
builtSourceSha: releaseField(release, "builtSourceSha", "built_source_sha"),
|
|
1204
|
+
builtSourceTreeSha: releaseField(
|
|
1205
|
+
release,
|
|
1206
|
+
"builtSourceTreeSha",
|
|
1207
|
+
"built_source_tree_sha",
|
|
1208
|
+
),
|
|
1209
|
+
promotionChannelSha: releaseField(
|
|
1210
|
+
release,
|
|
1211
|
+
"promotionChannelSha",
|
|
1212
|
+
"promotion_channel_sha",
|
|
1213
|
+
),
|
|
1214
|
+
promotionChannelTreeSha: releaseField(
|
|
1215
|
+
release,
|
|
1216
|
+
"promotionChannelTreeSha",
|
|
1217
|
+
"promotion_channel_tree_sha",
|
|
1218
|
+
),
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1088
1222
|
export function createArtifactEvidence({ assets = [], repository = "", tag = "", sourceSha = "", workflow = {}, kfdAdopter = undefined } = {}) {
|
|
1089
1223
|
const normalizedAssets = assets.map((asset, index) => normalizeAsset(asset, index));
|
|
1090
1224
|
return buildReleaseArtifactEvidence({ normalizedAssets, repository, tag, sourceSha, workflow, kfdAdopter });
|
|
@@ -1120,7 +1254,8 @@ function prepareReleaseKfdAdopterArtifacts({ kfdAdopter, assets, repository, tag
|
|
|
1120
1254
|
};
|
|
1121
1255
|
}
|
|
1122
1256
|
|
|
1123
|
-
export function createReleasePassport({
|
|
1257
|
+
export function createReleasePassport(options = {}) {
|
|
1258
|
+
const {
|
|
1124
1259
|
cwd = process.cwd(),
|
|
1125
1260
|
repository = "",
|
|
1126
1261
|
tag = "",
|
|
@@ -1161,13 +1296,16 @@ export function createReleasePassport({
|
|
|
1161
1296
|
kfdSupportEvidencePath = "",
|
|
1162
1297
|
invariantPassports = undefined,
|
|
1163
1298
|
releaseEvidence = [],
|
|
1299
|
+
v4ConsumerPolicyCertification = undefined,
|
|
1300
|
+
v4ConsumerPolicyCertificationRoot = "",
|
|
1301
|
+
v4RuntimeResumeEvidence = undefined,
|
|
1164
1302
|
kfdAgentHubEvidence = undefined,
|
|
1165
1303
|
kfdAgentHubEvidencePath = "",
|
|
1166
1304
|
controllerReceipts = [],
|
|
1167
1305
|
controllerReceiptReferences = [],
|
|
1168
1306
|
githubArtifactAttestations = [],
|
|
1169
1307
|
checkedAt = "",
|
|
1170
|
-
} =
|
|
1308
|
+
} = options;
|
|
1171
1309
|
const normalizedTag = nonEmptyString(tag, "tag");
|
|
1172
1310
|
const { normalized: normalizedKfdAdopter, artifactEvidence } = prepareReleaseKfdAdopterArtifacts({
|
|
1173
1311
|
kfdAdopter, assets, repository, tag: normalizedTag, sourceSha, workflow,
|
|
@@ -1176,17 +1314,13 @@ export function createReleasePassport({
|
|
|
1176
1314
|
const normalizedPackageSet = normalizePackageSet(packageSet, { packageName, packageVersion, publish });
|
|
1177
1315
|
const normalizedTrustedPublishing = normalizeTrustedPublishing(trustedPublishing, { workflow, publish });
|
|
1178
1316
|
const normalizedTransaction = normalizeTransaction(transaction);
|
|
1179
|
-
const
|
|
1180
|
-
const
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
.
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
.filter(Boolean);
|
|
1187
|
-
const normalizedDistTagPromotionEvidence = distTagPromotionEvidence
|
|
1188
|
-
? normalizeEvidenceDocument(distTagPromotionEvidence, "distTagPromotionEvidence")
|
|
1189
|
-
: undefined;
|
|
1317
|
+
const { promotion, buildEvidence } = preparePromotionBuild(options);
|
|
1318
|
+
const normalizedV4RuntimeResume = normalizeV4RuntimeResumeEvidence(v4RuntimeResumeEvidence, {
|
|
1319
|
+
repository,
|
|
1320
|
+
sourceSha,
|
|
1321
|
+
resumeRuntimeSha: promotion.routing?.runtime?.resolvedSha || "",
|
|
1322
|
+
consumerPolicyReceiptRoot: promotion.consumerPolicy?.certification?.receiptRoot || "",
|
|
1323
|
+
});
|
|
1190
1324
|
const normalizedImpact = normalizeImpactLedger(impact, { tag: normalizedTag, line });
|
|
1191
1325
|
const generatedAt = optionalString(checkedAt) || nowIso();
|
|
1192
1326
|
const kfd1Metadata = resolveKfd1Metadata();
|
|
@@ -1205,16 +1339,13 @@ export function createReleasePassport({
|
|
|
1205
1339
|
support: kfdSupportEvidencePath,
|
|
1206
1340
|
},
|
|
1207
1341
|
});
|
|
1208
|
-
const
|
|
1209
|
-
const builtSourceTreeSha = releaseField(release, "builtSourceTreeSha", "built_source_tree_sha");
|
|
1210
|
-
const promotionChannelSha = releaseField(release, "promotionChannelSha", "promotion_channel_sha");
|
|
1211
|
-
const promotionChannelTreeSha = releaseField(release, "promotionChannelTreeSha", "promotion_channel_tree_sha");
|
|
1342
|
+
const sourceFields = normalizeReleaseSourceFields(release);
|
|
1212
1343
|
const treeEquivalent = release.treeEquivalent === true;
|
|
1213
1344
|
const recoveryTreeEquivalent = Boolean(
|
|
1214
1345
|
treeEquivalent &&
|
|
1215
|
-
builtSourceTreeSha &&
|
|
1216
|
-
promotionChannelTreeSha &&
|
|
1217
|
-
builtSourceTreeSha === promotionChannelTreeSha,
|
|
1346
|
+
sourceFields.builtSourceTreeSha &&
|
|
1347
|
+
sourceFields.promotionChannelTreeSha &&
|
|
1348
|
+
sourceFields.builtSourceTreeSha === sourceFields.promotionChannelTreeSha,
|
|
1218
1349
|
);
|
|
1219
1350
|
const normalizedControllerReceipts = normalizeControllerReceiptReferences({
|
|
1220
1351
|
receipts: controllerReceipts,
|
|
@@ -1222,8 +1353,8 @@ export function createReleasePassport({
|
|
|
1222
1353
|
expectedSourceSha: sourceSha,
|
|
1223
1354
|
acceptedSourceShas: acceptedControllerSourceShas({
|
|
1224
1355
|
treeEquivalent,
|
|
1225
|
-
builtSourceSha,
|
|
1226
|
-
promotionChannelSha,
|
|
1356
|
+
builtSourceSha: sourceFields.builtSourceSha,
|
|
1357
|
+
promotionChannelSha: sourceFields.promotionChannelSha,
|
|
1227
1358
|
sourceSha,
|
|
1228
1359
|
recoveryTreeEquivalent,
|
|
1229
1360
|
}),
|
|
@@ -1285,9 +1416,9 @@ export function createReleasePassport({
|
|
|
1285
1416
|
releaseSha,
|
|
1286
1417
|
releaseMaterialSha,
|
|
1287
1418
|
builtSourceSha: optionalString(release.builtSourceSha || release.built_source_sha),
|
|
1288
|
-
builtSourceTreeSha,
|
|
1419
|
+
builtSourceTreeSha: sourceFields.builtSourceTreeSha,
|
|
1289
1420
|
promotionChannelSha: optionalString(release.promotionChannelSha || release.promotion_channel_sha),
|
|
1290
|
-
promotionChannelTreeSha,
|
|
1421
|
+
promotionChannelTreeSha: sourceFields.promotionChannelTreeSha,
|
|
1291
1422
|
treeEquivalent: release.treeEquivalent === undefined ? undefined : Boolean(release.treeEquivalent),
|
|
1292
1423
|
publishToolingSha: optionalString(
|
|
1293
1424
|
release.publishToolingSha ||
|
|
@@ -1325,15 +1456,17 @@ export function createReleasePassport({
|
|
|
1325
1456
|
["versionMaterial", versionMaterial],
|
|
1326
1457
|
["trustedPublishing", normalizedTrustedPublishing],
|
|
1327
1458
|
["transaction", normalizedTransaction],
|
|
1328
|
-
["promotionRouting",
|
|
1329
|
-
["buildSummary",
|
|
1330
|
-
["buildFacts",
|
|
1331
|
-
["platformArtifactManifests",
|
|
1332
|
-
["distTagPromotion",
|
|
1459
|
+
["promotionRouting", promotion.routing],
|
|
1460
|
+
["buildSummary", buildEvidence.buildSummary],
|
|
1461
|
+
["buildFacts", buildEvidence.buildFacts],
|
|
1462
|
+
["platformArtifactManifests", buildEvidence.platformArtifactManifests],
|
|
1463
|
+
["distTagPromotion", buildEvidence.distTagPromotionEvidence],
|
|
1333
1464
|
...kfdParts.sectionEntries,
|
|
1334
1465
|
["kfdAgentHub", normalizedKfdAgentHub],
|
|
1335
1466
|
["invariantPassports", invariantPassports],
|
|
1336
1467
|
["releaseEvidence", releaseEvidence],
|
|
1468
|
+
["v4ConsumerPolicy", promotion.consumerPolicy],
|
|
1469
|
+
["v4RuntimeResume", normalizedV4RuntimeResume],
|
|
1337
1470
|
["controllerReceipts", normalizedControllerReceipts],
|
|
1338
1471
|
["githubArtifactAttestations", normalizedGitHubArtifactAttestations],
|
|
1339
1472
|
]),
|
|
@@ -1360,21 +1493,21 @@ export function createReleasePassport({
|
|
|
1360
1493
|
artifactEvidence: artifactEvidencePath,
|
|
1361
1494
|
publishEvidence: publishEvidencePath,
|
|
1362
1495
|
transactionState: transactionStatePath,
|
|
1363
|
-
buildSummary:
|
|
1364
|
-
buildFacts:
|
|
1496
|
+
buildSummary: buildEvidence.buildSummary?.path || "",
|
|
1497
|
+
buildFacts: buildEvidence.buildFacts.map((fact) => ({
|
|
1365
1498
|
path: fact.path || "",
|
|
1366
1499
|
sha256: fact.sha256 || "",
|
|
1367
1500
|
contract: fact.fields?.contract || "",
|
|
1368
1501
|
id: fact.fields?.id || "",
|
|
1369
1502
|
digest: fact.fields?.digest || "",
|
|
1370
1503
|
})),
|
|
1371
|
-
platformArtifactManifests:
|
|
1504
|
+
platformArtifactManifests: buildEvidence.platformArtifactManifests.map((manifest) => ({
|
|
1372
1505
|
path: manifest.path,
|
|
1373
1506
|
sha256: manifest.sha256,
|
|
1374
1507
|
platform: manifest.platform,
|
|
1375
1508
|
artifactName: manifest.artifactName,
|
|
1376
1509
|
})),
|
|
1377
|
-
distTagPromotionEvidence:
|
|
1510
|
+
distTagPromotionEvidence: buildEvidence.distTagPromotionEvidence?.path || "",
|
|
1378
1511
|
...kfdParts.evidence,
|
|
1379
1512
|
kfdAgentHub: normalizedKfdAgentHub ? optionalString(kfdAgentHubEvidencePath || "kfd-agent-hub-evidence.json") : "",
|
|
1380
1513
|
invariantPassports: invariantPassports ? "invariantPassports" : "",
|
|
@@ -1456,6 +1589,9 @@ export function collectGitHubReleasePassport({
|
|
|
1456
1589
|
invariantPassportJsons = [],
|
|
1457
1590
|
invariantPassportCommand = "",
|
|
1458
1591
|
releaseEvidenceJsons = [],
|
|
1592
|
+
v4ConsumerPolicyCertificationJson = "",
|
|
1593
|
+
v4ConsumerPolicyCertificationRoot = "",
|
|
1594
|
+
v4RuntimeResumeEvidenceJson = "",
|
|
1459
1595
|
kfdAgentHubEvidenceJson = "",
|
|
1460
1596
|
controllerReceiptReferences = [],
|
|
1461
1597
|
githubArtifactAttestationPolicyJsons = [],
|
|
@@ -1531,6 +1667,12 @@ export function collectGitHubReleasePassport({
|
|
|
1531
1667
|
}),
|
|
1532
1668
|
)
|
|
1533
1669
|
.filter((meta) => meta.value);
|
|
1670
|
+
const v4ConsumerPolicyCertificationMeta =
|
|
1671
|
+
parseV4ConsumerPolicyCertification(v4ConsumerPolicyCertificationJson, cwd);
|
|
1672
|
+
const v4RuntimeResumeEvidence = parseJsonInput(v4RuntimeResumeEvidenceJson, undefined, {
|
|
1673
|
+
cwd,
|
|
1674
|
+
label: "v4RuntimeResumeEvidenceJson",
|
|
1675
|
+
});
|
|
1534
1676
|
const kfdAgentHubEvidenceMeta = parseJsonInputWithMeta(
|
|
1535
1677
|
kfdAgentHubEvidenceJson,
|
|
1536
1678
|
undefined,
|
|
@@ -1656,6 +1798,9 @@ export function collectGitHubReleasePassport({
|
|
|
1656
1798
|
kfdSupportEvidencePath: kfdSupport ? "kfd-support.json" : "",
|
|
1657
1799
|
invariantPassports,
|
|
1658
1800
|
releaseEvidence: releaseEvidenceAttachments.map(({ reference }) => reference),
|
|
1801
|
+
v4ConsumerPolicyCertification: v4ConsumerPolicyCertificationMeta.value,
|
|
1802
|
+
v4ConsumerPolicyCertificationRoot,
|
|
1803
|
+
v4RuntimeResumeEvidence,
|
|
1659
1804
|
kfdAgentHubEvidence: kfdAgentHubEvidenceMeta.value
|
|
1660
1805
|
? { ...kfdAgentHubEvidenceMeta, path: "kfd-agent-hub-evidence.json" }
|
|
1661
1806
|
: undefined,
|
|
@@ -2125,6 +2270,65 @@ function validateReleaseEvidenceAttachments({
|
|
|
2125
2270
|
return { evidenceArtifacts, releaseEvidence };
|
|
2126
2271
|
}
|
|
2127
2272
|
|
|
2273
|
+
function validateV4ConsumerPolicyPassportSection({ passport, issues }) {
|
|
2274
|
+
const routing = passport?.promotionRouting;
|
|
2275
|
+
const evidence = passport?.v4ConsumerPolicy;
|
|
2276
|
+
if (isV4PromotionRouting(routing) && !evidence) {
|
|
2277
|
+
issues.push(issue(
|
|
2278
|
+
"error",
|
|
2279
|
+
"v4ConsumerPolicy.missing",
|
|
2280
|
+
"Buildchain v4 Release Passport requires an external floating consumer policy certification",
|
|
2281
|
+
));
|
|
2282
|
+
return;
|
|
2283
|
+
}
|
|
2284
|
+
if (!evidence) return;
|
|
2285
|
+
const verification = verifyV4FloatingConsumerPolicyCertification(
|
|
2286
|
+
releasePassportCertificationVerificationOptions({
|
|
2287
|
+
evidence,
|
|
2288
|
+
passport,
|
|
2289
|
+
routing,
|
|
2290
|
+
}),
|
|
2291
|
+
);
|
|
2292
|
+
for (const failure of verification.failures) {
|
|
2293
|
+
issues.push(issue("error", `v4ConsumerPolicy.${failure.code}`, failure.message));
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
function validateV4RuntimeResumePassportSection({ passport, issues }) {
|
|
2298
|
+
const evidence = passport?.v4RuntimeResume;
|
|
2299
|
+
if (!evidence) return;
|
|
2300
|
+
const consumerPolicyReceiptRoot = passport?.v4ConsumerPolicy?.certification?.receiptRoot || "";
|
|
2301
|
+
const authorization = verifyV4RuntimeAuthorizationReceipt({
|
|
2302
|
+
receipt: evidence.authorization,
|
|
2303
|
+
receiptRoot: evidence.authorizationRoot,
|
|
2304
|
+
repository: passport?.product?.repository || "",
|
|
2305
|
+
sourceSha: passport?.release?.sourceSha || "",
|
|
2306
|
+
runtimeSha: passport?.promotionRouting?.runtime?.resolvedSha || "",
|
|
2307
|
+
consumerPolicyReceiptRoot,
|
|
2308
|
+
});
|
|
2309
|
+
for (const failure of authorization.failures) {
|
|
2310
|
+
issues.push(issue("error", `v4RuntimeResume.authorization.${failure}`, failure));
|
|
2311
|
+
}
|
|
2312
|
+
const lineage = verifyV4RuntimeResumeLineage({
|
|
2313
|
+
lineage: evidence.lineage,
|
|
2314
|
+
lineageRoot: evidence.lineageRoot,
|
|
2315
|
+
repository: passport?.product?.repository || "",
|
|
2316
|
+
sourceSha: passport?.release?.sourceSha || "",
|
|
2317
|
+
resumeRuntimeSha: passport?.promotionRouting?.runtime?.resolvedSha || "",
|
|
2318
|
+
consumerPolicyReceiptRoot,
|
|
2319
|
+
});
|
|
2320
|
+
for (const failure of lineage.failures) {
|
|
2321
|
+
issues.push(issue("error", `v4RuntimeResume.lineage.${failure}`, failure));
|
|
2322
|
+
}
|
|
2323
|
+
if (evidence.lineage?.authorizationRoot !== evidence.authorizationRoot) {
|
|
2324
|
+
issues.push(issue(
|
|
2325
|
+
"error",
|
|
2326
|
+
"v4RuntimeResume.authorization-root-mismatch",
|
|
2327
|
+
"runtime resume lineage must bind the embedded authorization receipt root",
|
|
2328
|
+
));
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2128
2332
|
function validatePublishEvidenceSection({ passport, publishEvidence, normalizedPublishEvidence, issues }) {
|
|
2129
2333
|
const publishEvidenceSupplied =
|
|
2130
2334
|
Boolean(passport?.evidence?.publishEvidence) ||
|
|
@@ -2597,6 +2801,8 @@ export function createReleaseCheckReport({
|
|
|
2597
2801
|
releaseEvidenceDocuments,
|
|
2598
2802
|
issues,
|
|
2599
2803
|
});
|
|
2804
|
+
validateV4ConsumerPolicyPassportSection({ passport, issues });
|
|
2805
|
+
validateV4RuntimeResumePassportSection({ passport, issues });
|
|
2600
2806
|
validatePublishEvidenceSection({ passport, publishEvidence, normalizedPublishEvidence, issues });
|
|
2601
2807
|
const evidenceIndex = indexEvidenceArtifacts(evidenceArtifacts);
|
|
2602
2808
|
validateReleaseArtifacts({ artifacts, evidenceIndex, issues });
|