@kungfu-tech/buildchain 3.0.6-alpha.3 → 3.0.6-alpha.5
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/contracts/release-candidate-recovery-v1.schema.json +3 -2
- package/dist/site/buildchain-contract.json +6 -6
- package/dist/site/buildchain-site.json +9 -12
- package/dist/site/kfd-claims.json +2 -2
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +2 -2
- package/dist/site/node-api-registry.json +37 -7
- package/dist/site/page-registry.json +4 -4
- package/dist/site/public-surface-audit.json +1 -1
- package/dist/site/publication-registry.json +3 -6
- package/dist/site/site-manifest.json +5 -8
- package/docs/node-api-reference.md +8 -7
- package/docs/release-candidate.md +9 -0
- package/package.json +1 -1
- package/packages/core/release-candidate-recovery.js +78 -13
- package/scripts/check-inventory.mjs +3 -1
- package/scripts/publication-candidate-sealer.mjs +104 -0
- package/scripts/release-candidate-resolver.mjs +28 -0
- package/scripts/resume-from-candidate-run.mjs +49 -20
|
@@ -7604,7 +7604,7 @@
|
|
|
7604
7604
|
"pageRegistryPath": "dist/site/page-registry.json",
|
|
7605
7605
|
"cliRegistryDigest": "2e7804e1fa8ef08bbe59fcf58908e47daef4dc145675fc9a989862671e40f993",
|
|
7606
7606
|
"workflowRegistryDigest": "1ab8b8d32f2ceb8dffad69d1f877f6ad6b37355dbe62c6ef5c0e6b9f4f197acb",
|
|
7607
|
-
"pageRegistryDigest": "
|
|
7607
|
+
"pageRegistryDigest": "300d36c3126d981d2b749827b550f7428e0da8981bf87d8b189b6832bbda7816"
|
|
7608
7608
|
},
|
|
7609
7609
|
"comparison": {
|
|
7610
7610
|
"missingCliRegistry": [],
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"contract": "kungfu-buildchain-publication-release-registry",
|
|
4
|
-
"generatedAt": "
|
|
5
|
-
"publishedAt": "2026-08-06T03:18:26.054Z",
|
|
4
|
+
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
6
5
|
"reproducible": true,
|
|
7
|
-
"timestampPolicy": "
|
|
6
|
+
"timestampPolicy": "source-date-epoch",
|
|
8
7
|
"deterministicInputs": [
|
|
9
8
|
"README.md",
|
|
10
9
|
"docs/*.md",
|
|
@@ -13,13 +12,11 @@
|
|
|
13
12
|
"packages/core/README.md",
|
|
14
13
|
"package.json#exports",
|
|
15
14
|
"tests/buildchain-inventory.json",
|
|
16
|
-
"sourceRevision",
|
|
17
15
|
"sourceDateEpoch",
|
|
18
16
|
"package content",
|
|
19
17
|
"declared Buildchain surface manifest contract"
|
|
20
18
|
],
|
|
21
19
|
"sourceDateEpoch": "0",
|
|
22
|
-
"sourceRevision": "2e685b037b979732297210d74b480d5e061a7aa4",
|
|
23
20
|
"timestampPolicyDetails": {
|
|
24
21
|
"contract": "kungfu-buildchain-surface-timestamp-policy",
|
|
25
22
|
"timestampFields": [
|
|
@@ -32,7 +29,7 @@
|
|
|
32
29
|
},
|
|
33
30
|
"package": {
|
|
34
31
|
"name": "@kungfu-tech/buildchain",
|
|
35
|
-
"version": "3.0.6-alpha.
|
|
32
|
+
"version": "3.0.6-alpha.5",
|
|
36
33
|
"versionSource": "package.json#version"
|
|
37
34
|
},
|
|
38
35
|
"sourceKind": "package-site-bundle",
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"contract": "kungfu-buildchain-site-manifest",
|
|
4
|
-
"generatedAt": "
|
|
5
|
-
"publishedAt": "2026-08-06T03:18:26.054Z",
|
|
4
|
+
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
6
5
|
"reproducible": true,
|
|
7
|
-
"timestampPolicy": "
|
|
6
|
+
"timestampPolicy": "source-date-epoch",
|
|
8
7
|
"deterministicInputs": [
|
|
9
8
|
"README.md",
|
|
10
9
|
"docs/*.md",
|
|
@@ -13,13 +12,11 @@
|
|
|
13
12
|
"packages/core/README.md",
|
|
14
13
|
"package.json#exports",
|
|
15
14
|
"tests/buildchain-inventory.json",
|
|
16
|
-
"sourceRevision",
|
|
17
15
|
"sourceDateEpoch",
|
|
18
16
|
"package content",
|
|
19
17
|
"declared Buildchain surface manifest contract"
|
|
20
18
|
],
|
|
21
19
|
"sourceDateEpoch": "0",
|
|
22
|
-
"sourceRevision": "2e685b037b979732297210d74b480d5e061a7aa4",
|
|
23
20
|
"timestampPolicyDetails": {
|
|
24
21
|
"contract": "kungfu-buildchain-surface-timestamp-policy",
|
|
25
22
|
"timestampFields": [
|
|
@@ -37,7 +34,7 @@
|
|
|
37
34
|
},
|
|
38
35
|
"package": {
|
|
39
36
|
"name": "@kungfu-tech/buildchain",
|
|
40
|
-
"version": "3.0.6-alpha.
|
|
37
|
+
"version": "3.0.6-alpha.5",
|
|
41
38
|
"versionSource": "package.json#version"
|
|
42
39
|
},
|
|
43
40
|
"entrypoint": "buildchain-site.json",
|
|
@@ -125,7 +122,7 @@
|
|
|
125
122
|
"path": "docs/release-candidate.md",
|
|
126
123
|
"plane": "verify",
|
|
127
124
|
"exists": true,
|
|
128
|
-
"digest": "sha256:
|
|
125
|
+
"digest": "sha256:9cb06463e0cbb06180ae4e9ed3258a9e7466237916ffc766c548ef5faa730651"
|
|
129
126
|
},
|
|
130
127
|
{
|
|
131
128
|
"id": "release-propagation",
|
|
@@ -237,7 +234,7 @@
|
|
|
237
234
|
"path": "docs/node-api-reference.md",
|
|
238
235
|
"plane": "use",
|
|
239
236
|
"exists": true,
|
|
240
|
-
"digest": "sha256:
|
|
237
|
+
"digest": "sha256:f8ae86b9182811d9f980b3ae1f1a42d2c84cd6c133003c996946848f938099a8"
|
|
241
238
|
},
|
|
242
239
|
{
|
|
243
240
|
"id": "build-facts",
|
|
@@ -447,7 +447,7 @@ Target: `./packages/core/index.js`. Public symbols: 562.
|
|
|
447
447
|
| `readReleaseTransaction` | function: function readReleaseTransaction(filePath) | filePath | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { readReleaseTransaction } from "@kungfu-tech/buildchain";` | `packages/core/publish-transaction.js:367` |
|
|
448
448
|
| `recordReleaseActivationPhase` | function: function recordReleaseActivationPhase(transaction, phaseId, { receiptRoots = [], failure = "" } = {}) | transaction, phaseId, { receiptRoots = [], failure = "" } = {} | unknown | May throw an Error on rejected input or failed operations; follow the linked source contract. | may-write-or-invoke-external-actions | `import { recordReleaseActivationPhase } from "@kungfu-tech/buildchain";` | `packages/core/release-activation-transaction.js:217` |
|
|
449
449
|
| `recordReleasePropagationStage` | function: function recordReleasePropagationStage({ work, expectedWorkRoot, receipt } = {}) | { work, expectedWorkRoot, receipt } = {} | unknown | May throw an Error on rejected input or failed operations; follow the linked source contract. | may-write-or-invoke-external-actions | `import { recordReleasePropagationStage } from "@kungfu-tech/buildchain";` | `packages/core/release-propagation-work-transitions.js:45` |
|
|
450
|
-
| `recoveryFailure` | function: function recoveryFailure(error) | error | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { recoveryFailure } from "@kungfu-tech/buildchain";` | `packages/core/release-candidate-recovery.js:
|
|
450
|
+
| `recoveryFailure` | function: function recoveryFailure(error) | error | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { recoveryFailure } from "@kungfu-tech/buildchain";` | `packages/core/release-candidate-recovery.js:441` |
|
|
451
451
|
| `redactBuildchainLogAttributes` | function: function redactBuildchainLogAttributes(attributes = {}) | attributes = {} | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { redactBuildchainLogAttributes } from "@kungfu-tech/buildchain";` | `packages/core/logging.js:33` |
|
|
452
452
|
| `redactDiagnosticsValue` | function: function redactDiagnosticsValue(key, value, pattern = DEFAULT_SECRET_KEY_PATTERN) | key, value, pattern = DEFAULT_SECRET_KEY_PATTERN | unknown | May throw an Error on rejected input or failed operations; follow the linked source contract. | none-detected-by-static-source-scan | `import { redactDiagnosticsValue } from "@kungfu-tech/buildchain";` | `packages/core/diagnostics.js:953` |
|
|
453
453
|
| `redactIssueText` | function: function redactIssueText(value) | value | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { redactIssueText } from "@kungfu-tech/buildchain";` | `packages/core/issue-reporting.js:51` |
|
|
@@ -571,7 +571,7 @@ Target: `./packages/core/index.js`. Public symbols: 562.
|
|
|
571
571
|
| `verifyPublicationQualificationReceipt` | function: function verifyPublicationQualificationReceipt({ receipt, capability, gateAggregate, expected = {}, usedNonces = [], now = new Date(), } = {}) | { receipt, capability, gateAggregate, expected = {}, usedNonces = [], now = new Date(), } = {} | unknown | May throw an Error on rejected input or failed operations; follow the linked source contract. | none-detected-by-static-source-scan | `import { verifyPublicationQualificationReceipt } from "@kungfu-tech/buildchain";` | `packages/core/publication-authority.js:1096` |
|
|
572
572
|
| `verifyPublicationReproducibility` | function: function verifyPublicationReproducibility({ cwd = process.cwd(), sourceSha = "", output = DEFAULT_OUTPUT, promote = false, keepWorkspaces = false, pullToolchain = true, packageName = "", allowUnpinnedToolchain = false, overlayPaths = [DEFAULT_REGISTRY_INPUT_DIR, DEFAULT_REGISTRY_HYDRATION], } = {}) | { cwd = process.cwd(), sourceSha = "", output = DEFAULT_OUTPUT, promote = false, keepWorkspaces = false, pullToolchain = true, packageName = "", allowUnpinnedToolchain = false, overlayPaths = [DEFAULT_REGISTRY_INPUT_DIR, DEFAULT_REGISTRY_HYDRATION], } = {} | unknown | May throw an Error on rejected input or failed operations; follow the linked source contract. | local-filesystem-write | `import { verifyPublicationReproducibility } from "@kungfu-tech/buildchain";` | `packages/core/publication-reproducibility.js:869` |
|
|
573
573
|
| `verifyPublicationSealedBundle` | function: function verifyPublicationSealedBundle({ bundleRoot, manifest } = {}) | { bundleRoot, manifest } = {} | unknown | May throw an Error on rejected input or failed operations; follow the linked source contract. | none-detected-by-static-source-scan | `import { verifyPublicationSealedBundle } from "@kungfu-tech/buildchain";` | `packages/core/publication-sealed-bundle.js:124` |
|
|
574
|
-
| `verifyReleaseCandidateRecovery` | function: function verifyReleaseCandidateRecovery({ candidateRepository, targetRepository, expectedRunId, expectedWorkflowFile, expectedWorkflowName, channel, targetRef, targetSha, targetTree, expectedSourceTree = "", expectedCandidateRoot = "", expectedRuntimeSha, expectedTransactionId = "", existingTransaction = undefined, run, workflow, pullRequest, ancestry, passport, buildSummary, controllerReceipts = [], platformManifests = [], productPayloadManifests = [], artifacts = [], currentToolingSha, recoveryRunId = "", createdAt = new Date().toISOString(), } = {}) | { candidateRepository, targetRepository, expectedRunId, expectedWorkflowFile, expectedWorkflowName, channel, targetRef, targetSha, targetTree, expectedSourceTree = "", expectedCandidateRoot = "", expectedRuntimeSha, expectedTransactionId = "", existingTransaction = undefined, run, workflow, pullRequest, ancestry, passport, buildSummary, controllerReceipts = [], platformManifests = [], productPayloadManifests = [], artifacts = [], currentToolingSha, recoveryRunId = "", createdAt = new Date().toISOString(), } = {} | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { verifyReleaseCandidateRecovery } from "@kungfu-tech/buildchain";` | `packages/core/release-candidate-recovery.js:
|
|
574
|
+
| `verifyReleaseCandidateRecovery` | function: function verifyReleaseCandidateRecovery({ candidateRepository, targetRepository, expectedRunId, expectedWorkflowFile, expectedWorkflowName, channel, targetRef, targetSha, targetTree, expectedSourceTree = "", expectedCandidateRoot = "", expectedRuntimeSha, expectedTransactionId = "", existingTransaction = undefined, run, workflow, pullRequest, ancestry, passport, buildSummary, controllerReceipts = [], platformManifests = [], platformManifestEvidence = [], productPayloadManifests = [], artifacts = [], publicationVersion = "", currentToolingSha, recoveryRunId = "", createdAt = new Date().toISOString(), } = {}) | { candidateRepository, targetRepository, expectedRunId, expectedWorkflowFile, expectedWorkflowName, channel, targetRef, targetSha, targetTree, expectedSourceTree = "", expectedCandidateRoot = "", expectedRuntimeSha, expectedTransactionId = "", existingTransaction = undefined, run, workflow, pullRequest, ancestry, passport, buildSummary, controllerReceipts = [], platformManifests = [], platformManifestEvidence = [], productPayloadManifests = [], artifacts = [], publicationVersion = "", currentToolingSha, recoveryRunId = "", createdAt = new Date().toISOString(), } = {} | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { verifyReleaseCandidateRecovery } from "@kungfu-tech/buildchain";` | `packages/core/release-candidate-recovery.js:318` |
|
|
575
575
|
| `verifyReleasePassport` | function: async function verifyReleasePassport({ passportLocation, artifactEvidenceLocation = "", publishEvidenceLocation = "", impactLocation = "", agentIndexLocation = "", productMechanismLocation = "", kfdAgentHubEvidenceLocation = "", kfdSupportEvidenceLocation = "", checkedAt = nowIso(), } = {}) | { passportLocation, artifactEvidenceLocation = "", publishEvidenceLocation = "", impactLocation = "", agentIndexLocation = "", productMechanismLocation = "", kfdAgentHubEvidenceLocation = "", kfdSupportEvidenceLocation = "", checkedAt = nowIso(), } = {} | Promise<unknown> | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { verifyReleasePassport } from "@kungfu-tech/buildchain";` | `packages/core/release-passport.js:2743` |
|
|
576
576
|
| `verifyReleasePropagationWork` | function: function verifyReleasePropagationWork(input) | input | unknown | May throw an Error on rejected input or failed operations; follow the linked source contract. | none-detected-by-static-source-scan | `import { verifyReleasePropagationWork } from "@kungfu-tech/buildchain";` | `packages/core/release-propagation-work.js:486` |
|
|
577
577
|
| `WEB_SURFACE_PRODUCTION_DECISION_CONTRACT` | constant: const WEB_SURFACE_PRODUCTION_DECISION_CONTRACT | none | value | Import does not declare a throw contract. | none-on-import | `import { WEB_SURFACE_PRODUCTION_DECISION_CONTRACT } from "@kungfu-tech/buildchain";` | `packages/core/web-surface-publication-candidate.js:7` |
|
|
@@ -1062,7 +1062,7 @@ Target: `./packages/core/index.js`. Public symbols: 562.
|
|
|
1062
1062
|
| `readReleaseTransaction` | function: function readReleaseTransaction(filePath) | filePath | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { readReleaseTransaction } from "@kungfu-tech/buildchain/core";` | `packages/core/publish-transaction.js:367` |
|
|
1063
1063
|
| `recordReleaseActivationPhase` | function: function recordReleaseActivationPhase(transaction, phaseId, { receiptRoots = [], failure = "" } = {}) | transaction, phaseId, { receiptRoots = [], failure = "" } = {} | unknown | May throw an Error on rejected input or failed operations; follow the linked source contract. | may-write-or-invoke-external-actions | `import { recordReleaseActivationPhase } from "@kungfu-tech/buildchain/core";` | `packages/core/release-activation-transaction.js:217` |
|
|
1064
1064
|
| `recordReleasePropagationStage` | function: function recordReleasePropagationStage({ work, expectedWorkRoot, receipt } = {}) | { work, expectedWorkRoot, receipt } = {} | unknown | May throw an Error on rejected input or failed operations; follow the linked source contract. | may-write-or-invoke-external-actions | `import { recordReleasePropagationStage } from "@kungfu-tech/buildchain/core";` | `packages/core/release-propagation-work-transitions.js:45` |
|
|
1065
|
-
| `recoveryFailure` | function: function recoveryFailure(error) | error | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { recoveryFailure } from "@kungfu-tech/buildchain/core";` | `packages/core/release-candidate-recovery.js:
|
|
1065
|
+
| `recoveryFailure` | function: function recoveryFailure(error) | error | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { recoveryFailure } from "@kungfu-tech/buildchain/core";` | `packages/core/release-candidate-recovery.js:441` |
|
|
1066
1066
|
| `redactBuildchainLogAttributes` | function: function redactBuildchainLogAttributes(attributes = {}) | attributes = {} | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { redactBuildchainLogAttributes } from "@kungfu-tech/buildchain/core";` | `packages/core/logging.js:33` |
|
|
1067
1067
|
| `redactDiagnosticsValue` | function: function redactDiagnosticsValue(key, value, pattern = DEFAULT_SECRET_KEY_PATTERN) | key, value, pattern = DEFAULT_SECRET_KEY_PATTERN | unknown | May throw an Error on rejected input or failed operations; follow the linked source contract. | none-detected-by-static-source-scan | `import { redactDiagnosticsValue } from "@kungfu-tech/buildchain/core";` | `packages/core/diagnostics.js:953` |
|
|
1068
1068
|
| `redactIssueText` | function: function redactIssueText(value) | value | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { redactIssueText } from "@kungfu-tech/buildchain/core";` | `packages/core/issue-reporting.js:51` |
|
|
@@ -1186,7 +1186,7 @@ Target: `./packages/core/index.js`. Public symbols: 562.
|
|
|
1186
1186
|
| `verifyPublicationQualificationReceipt` | function: function verifyPublicationQualificationReceipt({ receipt, capability, gateAggregate, expected = {}, usedNonces = [], now = new Date(), } = {}) | { receipt, capability, gateAggregate, expected = {}, usedNonces = [], now = new Date(), } = {} | unknown | May throw an Error on rejected input or failed operations; follow the linked source contract. | none-detected-by-static-source-scan | `import { verifyPublicationQualificationReceipt } from "@kungfu-tech/buildchain/core";` | `packages/core/publication-authority.js:1096` |
|
|
1187
1187
|
| `verifyPublicationReproducibility` | function: function verifyPublicationReproducibility({ cwd = process.cwd(), sourceSha = "", output = DEFAULT_OUTPUT, promote = false, keepWorkspaces = false, pullToolchain = true, packageName = "", allowUnpinnedToolchain = false, overlayPaths = [DEFAULT_REGISTRY_INPUT_DIR, DEFAULT_REGISTRY_HYDRATION], } = {}) | { cwd = process.cwd(), sourceSha = "", output = DEFAULT_OUTPUT, promote = false, keepWorkspaces = false, pullToolchain = true, packageName = "", allowUnpinnedToolchain = false, overlayPaths = [DEFAULT_REGISTRY_INPUT_DIR, DEFAULT_REGISTRY_HYDRATION], } = {} | unknown | May throw an Error on rejected input or failed operations; follow the linked source contract. | local-filesystem-write | `import { verifyPublicationReproducibility } from "@kungfu-tech/buildchain/core";` | `packages/core/publication-reproducibility.js:869` |
|
|
1188
1188
|
| `verifyPublicationSealedBundle` | function: function verifyPublicationSealedBundle({ bundleRoot, manifest } = {}) | { bundleRoot, manifest } = {} | unknown | May throw an Error on rejected input or failed operations; follow the linked source contract. | none-detected-by-static-source-scan | `import { verifyPublicationSealedBundle } from "@kungfu-tech/buildchain/core";` | `packages/core/publication-sealed-bundle.js:124` |
|
|
1189
|
-
| `verifyReleaseCandidateRecovery` | function: function verifyReleaseCandidateRecovery({ candidateRepository, targetRepository, expectedRunId, expectedWorkflowFile, expectedWorkflowName, channel, targetRef, targetSha, targetTree, expectedSourceTree = "", expectedCandidateRoot = "", expectedRuntimeSha, expectedTransactionId = "", existingTransaction = undefined, run, workflow, pullRequest, ancestry, passport, buildSummary, controllerReceipts = [], platformManifests = [], productPayloadManifests = [], artifacts = [], currentToolingSha, recoveryRunId = "", createdAt = new Date().toISOString(), } = {}) | { candidateRepository, targetRepository, expectedRunId, expectedWorkflowFile, expectedWorkflowName, channel, targetRef, targetSha, targetTree, expectedSourceTree = "", expectedCandidateRoot = "", expectedRuntimeSha, expectedTransactionId = "", existingTransaction = undefined, run, workflow, pullRequest, ancestry, passport, buildSummary, controllerReceipts = [], platformManifests = [], productPayloadManifests = [], artifacts = [], currentToolingSha, recoveryRunId = "", createdAt = new Date().toISOString(), } = {} | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { verifyReleaseCandidateRecovery } from "@kungfu-tech/buildchain/core";` | `packages/core/release-candidate-recovery.js:
|
|
1189
|
+
| `verifyReleaseCandidateRecovery` | function: function verifyReleaseCandidateRecovery({ candidateRepository, targetRepository, expectedRunId, expectedWorkflowFile, expectedWorkflowName, channel, targetRef, targetSha, targetTree, expectedSourceTree = "", expectedCandidateRoot = "", expectedRuntimeSha, expectedTransactionId = "", existingTransaction = undefined, run, workflow, pullRequest, ancestry, passport, buildSummary, controllerReceipts = [], platformManifests = [], platformManifestEvidence = [], productPayloadManifests = [], artifacts = [], publicationVersion = "", currentToolingSha, recoveryRunId = "", createdAt = new Date().toISOString(), } = {}) | { candidateRepository, targetRepository, expectedRunId, expectedWorkflowFile, expectedWorkflowName, channel, targetRef, targetSha, targetTree, expectedSourceTree = "", expectedCandidateRoot = "", expectedRuntimeSha, expectedTransactionId = "", existingTransaction = undefined, run, workflow, pullRequest, ancestry, passport, buildSummary, controllerReceipts = [], platformManifests = [], platformManifestEvidence = [], productPayloadManifests = [], artifacts = [], publicationVersion = "", currentToolingSha, recoveryRunId = "", createdAt = new Date().toISOString(), } = {} | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { verifyReleaseCandidateRecovery } from "@kungfu-tech/buildchain/core";` | `packages/core/release-candidate-recovery.js:318` |
|
|
1190
1190
|
| `verifyReleasePassport` | function: async function verifyReleasePassport({ passportLocation, artifactEvidenceLocation = "", publishEvidenceLocation = "", impactLocation = "", agentIndexLocation = "", productMechanismLocation = "", kfdAgentHubEvidenceLocation = "", kfdSupportEvidenceLocation = "", checkedAt = nowIso(), } = {}) | { passportLocation, artifactEvidenceLocation = "", publishEvidenceLocation = "", impactLocation = "", agentIndexLocation = "", productMechanismLocation = "", kfdAgentHubEvidenceLocation = "", kfdSupportEvidenceLocation = "", checkedAt = nowIso(), } = {} | Promise<unknown> | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { verifyReleasePassport } from "@kungfu-tech/buildchain/core";` | `packages/core/release-passport.js:2743` |
|
|
1191
1191
|
| `verifyReleasePropagationWork` | function: function verifyReleasePropagationWork(input) | input | unknown | May throw an Error on rejected input or failed operations; follow the linked source contract. | none-detected-by-static-source-scan | `import { verifyReleasePropagationWork } from "@kungfu-tech/buildchain/core";` | `packages/core/release-propagation-work.js:486` |
|
|
1192
1192
|
| `WEB_SURFACE_PRODUCTION_DECISION_CONTRACT` | constant: const WEB_SURFACE_PRODUCTION_DECISION_CONTRACT | none | value | Import does not declare a throw contract. | none-on-import | `import { WEB_SURFACE_PRODUCTION_DECISION_CONTRACT } from "@kungfu-tech/buildchain/core";` | `packages/core/web-surface-publication-candidate.js:7` |
|
|
@@ -1906,14 +1906,15 @@ Target: `./packages/core/release-candidate.js`. Public symbols: 5.
|
|
|
1906
1906
|
|
|
1907
1907
|
## `@kungfu-tech/buildchain/release-candidate-recovery`
|
|
1908
1908
|
|
|
1909
|
-
Target: `./packages/core/release-candidate-recovery.js`. Public symbols:
|
|
1909
|
+
Target: `./packages/core/release-candidate-recovery.js`. Public symbols: 5.
|
|
1910
1910
|
|
|
1911
1911
|
| Symbol | Kind and signature | Parameters | Return | Errors | Side effects | Example | Source |
|
|
1912
1912
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
1913
|
-
| `recoveryFailure` | function: function recoveryFailure(error) | error | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { recoveryFailure } from "@kungfu-tech/buildchain/release-candidate-recovery";` | `packages/core/release-candidate-recovery.js:
|
|
1913
|
+
| `recoveryFailure` | function: function recoveryFailure(error) | error | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { recoveryFailure } from "@kungfu-tech/buildchain/release-candidate-recovery";` | `packages/core/release-candidate-recovery.js:441` |
|
|
1914
1914
|
| `RELEASE_CANDIDATE_RECOVERY_CONTRACT` | constant: const RELEASE_CANDIDATE_RECOVERY_CONTRACT | none | value | Import does not declare a throw contract. | none-on-import | `import { RELEASE_CANDIDATE_RECOVERY_CONTRACT } from "@kungfu-tech/buildchain/release-candidate-recovery";` | `packages/core/release-candidate-recovery.js:11` |
|
|
1915
1915
|
| `ReleaseCandidateRecoveryError` | class: class ReleaseCandidateRecoveryError | none | ReleaseCandidateRecoveryError | Construction and method errors follow the linked source implementation. | class-dependent | `import { ReleaseCandidateRecoveryError } from "@kungfu-tech/buildchain/release-candidate-recovery";` | `packages/core/release-candidate-recovery.js:18` |
|
|
1916
|
-
| `
|
|
1916
|
+
| `validateReleaseCandidateRecoveryReceipt` | function: function validateReleaseCandidateRecoveryReceipt({ receipt, passport, repository = "", targetChannel = "", targetRef = "", targetSha = "", targetTree = "", version = "", } = {}) | { receipt, passport, repository = "", targetChannel = "", targetRef = "", targetSha = "", targetTree = "", version = "", } = {} | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { validateReleaseCandidateRecoveryReceipt } from "@kungfu-tech/buildchain/release-candidate-recovery";` | `packages/core/release-candidate-recovery.js:96` |
|
|
1917
|
+
| `verifyReleaseCandidateRecovery` | function: function verifyReleaseCandidateRecovery({ candidateRepository, targetRepository, expectedRunId, expectedWorkflowFile, expectedWorkflowName, channel, targetRef, targetSha, targetTree, expectedSourceTree = "", expectedCandidateRoot = "", expectedRuntimeSha, expectedTransactionId = "", existingTransaction = undefined, run, workflow, pullRequest, ancestry, passport, buildSummary, controllerReceipts = [], platformManifests = [], platformManifestEvidence = [], productPayloadManifests = [], artifacts = [], publicationVersion = "", currentToolingSha, recoveryRunId = "", createdAt = new Date().toISOString(), } = {}) | { candidateRepository, targetRepository, expectedRunId, expectedWorkflowFile, expectedWorkflowName, channel, targetRef, targetSha, targetTree, expectedSourceTree = "", expectedCandidateRoot = "", expectedRuntimeSha, expectedTransactionId = "", existingTransaction = undefined, run, workflow, pullRequest, ancestry, passport, buildSummary, controllerReceipts = [], platformManifests = [], platformManifestEvidence = [], productPayloadManifests = [], artifacts = [], publicationVersion = "", currentToolingSha, recoveryRunId = "", createdAt = new Date().toISOString(), } = {} | unknown | Errors from called operations may propagate; no narrower throw contract is declared in source. | none-detected-by-static-source-scan | `import { verifyReleaseCandidateRecovery } from "@kungfu-tech/buildchain/release-candidate-recovery";` | `packages/core/release-candidate-recovery.js:318` |
|
|
1917
1918
|
|
|
1918
1919
|
## `@kungfu-tech/buildchain/stable-candidate-ledger`
|
|
1919
1920
|
|
|
@@ -195,6 +195,15 @@ the skipped stages, current tooling SHA, transaction identity/state, and an
|
|
|
195
195
|
exact receipt root. The receipt is uploaded as an Actions artifact and staged
|
|
196
196
|
with immutable GitHub Release Passport assets.
|
|
197
197
|
|
|
198
|
+
The original candidate Passport is never rewritten when a reusable fixture or
|
|
199
|
+
consumer build records a product version different from the sealed publication
|
|
200
|
+
package. In that case the promote action validates the Passport without
|
|
201
|
+
discarding its original target, then requires the immutable recovery receipt to
|
|
202
|
+
bind the original candidate root and source/tree to the exact version read from
|
|
203
|
+
the sealed payload. Without that receipt, the existing direct Passport version
|
|
204
|
+
check remains mandatory; receipt, candidate-root, target, or version drift fails
|
|
205
|
+
before publication side effects.
|
|
206
|
+
|
|
198
207
|
Missing or expired artifacts, archive or payload digest drift, tree/root,
|
|
199
208
|
repository/workflow/channel/target mismatch, untrusted run/PR provenance,
|
|
200
209
|
incomplete controller evidence, and transaction conflict fail closed with an
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "3.0.6-alpha.
|
|
3
|
+
"version": "3.0.6-alpha.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Buildchain Release Passport, release governance, CLI toolkit, and site facts.",
|
|
6
6
|
"repository": "https://github.com/kungfu-systems/buildchain",
|
|
@@ -93,13 +93,59 @@ function normalizedRef(value) {
|
|
|
93
93
|
return String(value || "").replace(/^refs\/heads\//, "").trim();
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
function
|
|
97
|
-
|
|
96
|
+
export function validateReleaseCandidateRecoveryReceipt({
|
|
97
|
+
receipt,
|
|
98
|
+
passport,
|
|
99
|
+
repository = "",
|
|
100
|
+
targetChannel = "",
|
|
101
|
+
targetRef = "",
|
|
102
|
+
targetSha = "",
|
|
103
|
+
targetTree = "",
|
|
104
|
+
version = "",
|
|
105
|
+
} = {}) {
|
|
106
|
+
const receiptObject = Boolean(receipt && typeof receipt === "object" && !Array.isArray(receipt));
|
|
107
|
+
const passportObject = Boolean(passport && typeof passport === "object" && !Array.isArray(passport));
|
|
108
|
+
const rootedReceipt = Object.assign({}, receipt);
|
|
109
|
+
delete rootedReceipt.root;
|
|
110
|
+
const candidateHash = String(passport?.candidateHash || "");
|
|
111
|
+
const candidateRoot = `sha256:${candidateHash}`;
|
|
112
|
+
const checks = [
|
|
113
|
+
[receiptObject, "receipt must be an object"],
|
|
114
|
+
[passportObject, "passport must be an object"],
|
|
115
|
+
[receipt?.contract === RELEASE_CANDIDATE_RECOVERY_CONTRACT, `contract must be ${RELEASE_CANDIDATE_RECOVERY_CONTRACT}`],
|
|
116
|
+
[Number(receipt?.schemaVersion) === 1, "schemaVersion must be 1"],
|
|
117
|
+
[receipt?.action === "reused", "action must be reused"],
|
|
118
|
+
[receipt?.payloadBytes === "unchanged", "payloadBytes must be unchanged"],
|
|
119
|
+
[JSON.stringify(receipt?.skippedBuildStages) === JSON.stringify(["install", "build", "verify", "platform-matrix"]), "skippedBuildStages must record the complete skipped product build matrix"],
|
|
120
|
+
[receipt?.root === `sha256:${sha256Json(rootedReceipt)}`, "receipt root mismatch"],
|
|
121
|
+
[!repository || receipt?.repository === repository, `repository mismatch: expected ${repository}, got ${receipt?.repository || "<empty>"}`],
|
|
122
|
+
[!targetChannel || receipt?.target?.channel === targetChannel, `target channel mismatch: expected ${targetChannel}, got ${receipt?.target?.channel || "<empty>"}`],
|
|
123
|
+
[!targetRef || normalizedRef(receipt?.target?.ref) === normalizedRef(targetRef), `target ref mismatch: expected ${normalizedRef(targetRef)}, got ${normalizedRef(receipt?.target?.ref) || "<empty>"}`],
|
|
124
|
+
[!targetSha || receipt?.target?.sha === targetSha, `target SHA mismatch: expected ${targetSha}, got ${receipt?.target?.sha || "<empty>"}`],
|
|
125
|
+
[!targetTree || receipt?.target?.tree === targetTree, `target tree mismatch: expected ${targetTree}, got ${receipt?.target?.tree || "<empty>"}`],
|
|
126
|
+
[!version || receipt?.target?.version === version, `publication version mismatch: expected ${version}, got ${receipt?.target?.version || "<empty>"}`],
|
|
127
|
+
[passportObject && /^[0-9a-f]{64}$/.test(candidateHash), "passport candidateHash must be a sha256 digest"],
|
|
128
|
+
[passportObject && receipt?.recovered?.candidateRoot === candidateRoot, `candidate root mismatch: expected ${candidateRoot}, got ${receipt?.recovered?.candidateRoot || "<empty>"}`],
|
|
129
|
+
[passportObject && receipt?.originalCandidate?.sourceSha === passport?.source?.headSha, "original candidate source SHA mismatch"],
|
|
130
|
+
[passportObject && receipt?.originalCandidate?.tree === passport?.source?.treeHash, "original candidate tree mismatch"],
|
|
131
|
+
];
|
|
132
|
+
const errors = checks.filter(([ok]) => !ok).map(([, message]) => message);
|
|
133
|
+
return {
|
|
134
|
+
ok: errors.length === 0,
|
|
135
|
+
errors,
|
|
136
|
+
publicationVersion: String(receipt?.target?.version || ""),
|
|
137
|
+
candidateRoot: String(receipt?.recovered?.candidateRoot || ""),
|
|
138
|
+
};
|
|
98
139
|
}
|
|
99
140
|
|
|
100
|
-
function
|
|
141
|
+
function sameArtifactFile(left, right) {
|
|
142
|
+
return Boolean(left && right && left.size === right.size && left.sha256 === right.sha256);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function validatePlatformPayloads(passport, artifacts, platformManifests, platformManifestEvidence) {
|
|
101
146
|
const byName = new Map(artifacts.map((artifact) => [artifact.name, artifact]));
|
|
102
147
|
const manifestsByArtifact = new Map((platformManifests || []).map((manifest) => [manifest.artifactName, manifest]));
|
|
148
|
+
const evidenceByArtifact = new Map((platformManifestEvidence || []).map((evidence) => [evidence.artifactName, evidence]));
|
|
103
149
|
if (manifestsByArtifact.size !== (passport.platformMatrix || []).length) {
|
|
104
150
|
fail("platform-matrix-mismatch", "platform manifest count differs from the Passport platform matrix", "Restore every exact platform manifest from the candidate run.");
|
|
105
151
|
}
|
|
@@ -122,10 +168,26 @@ function validatePlatformPayloads(passport, artifacts, platformManifests) {
|
|
|
122
168
|
size: Number(file.size ?? file.bytes),
|
|
123
169
|
sha256: `sha256:${String(file.sha256 || "").replace(/^sha256:/, "")}`,
|
|
124
170
|
})).sort((left, right) => left.path.localeCompare(right.path));
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
171
|
+
const platformId = required(platform.platformId, `${platform.artifactName}.platformId`);
|
|
172
|
+
const buildchainEvidencePrefix = `.buildchain/artifacts/${platformId}/`;
|
|
173
|
+
const manifestByPath = new Map(manifestFiles.map((file) => [file.path, file]));
|
|
174
|
+
const evidenceFiles = new Map((evidenceByArtifact.get(platform.artifactName)?.files || []).map((file) => [file.path, file]));
|
|
175
|
+
const payloadPaths = new Set(payload.files.map((file) => file.path));
|
|
176
|
+
for (const manifestFile of manifestFiles) {
|
|
177
|
+
if (!payloadPaths.has(manifestFile.path)) {
|
|
178
|
+
fail("artifact-manifest-mismatch", `platform payload omits a manifest-declared file: ${platform.artifactName}/${manifestFile.path}`, "Do not publish; preserve the mismatched manifest and payload evidence.");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
for (const payloadFile of payload.files) {
|
|
182
|
+
const manifestFile = manifestByPath.get(payloadFile.path);
|
|
183
|
+
if (sameArtifactFile(payloadFile, manifestFile)) continue;
|
|
184
|
+
const evidencePath = payloadFile.path.startsWith(buildchainEvidencePrefix)
|
|
185
|
+
? payloadFile.path.slice(buildchainEvidencePrefix.length)
|
|
186
|
+
: "";
|
|
187
|
+
const evidenceFile = evidencePath ? evidenceFiles.get(evidencePath) : undefined;
|
|
188
|
+
if (!sameArtifactFile(payloadFile, evidenceFile)) {
|
|
189
|
+
fail("artifact-manifest-mismatch", `platform payload contains unbound bytes or evidence drift: ${platform.artifactName}/${payloadFile.path}`, "Do not publish; preserve the platform payload, manifest, and independently uploaded evidence.");
|
|
190
|
+
}
|
|
129
191
|
}
|
|
130
192
|
if (passportFiles.length > 0 && JSON.stringify(manifestFiles) !== JSON.stringify(passportFiles)) {
|
|
131
193
|
fail("artifact-manifest-mismatch", `platform manifest differs from Passport inventory: ${platform.artifactName}`, "Do not publish; preserve the mismatched manifest and Passport evidence.");
|
|
@@ -236,17 +298,17 @@ function validateCandidateIdentity({
|
|
|
236
298
|
return { sha, tree, runtimeSha, toolingSha };
|
|
237
299
|
}
|
|
238
300
|
|
|
239
|
-
function validateRecoveryTransaction({ existingTransaction, expectedTransactionId, repository, passport, sha, targetRef, candidateRoot }) {
|
|
301
|
+
function validateRecoveryTransaction({ existingTransaction, expectedTransactionId, repository, version, passport, sha, targetRef, candidateRoot }) {
|
|
240
302
|
const actualTransactionId = String(existingTransaction?.id || "");
|
|
241
303
|
if (expectedTransactionId && !actualTransactionId) fail("transaction-identity-conflict", `expected transaction ${expectedTransactionId} does not exist`, "Remove the stale transaction identity only if no durable transaction was ever sealed; otherwise preserve evidence and enter repair_required.");
|
|
242
304
|
if (expectedTransactionId && expectedTransactionId !== actualTransactionId) fail("transaction-identity-conflict", `existing transaction ${actualTransactionId} conflicts with expected ${expectedTransactionId}`, "Enter repair_required and inspect the durable transaction before any retry.");
|
|
243
305
|
if (existingTransaction) {
|
|
244
|
-
const expectedIdentity = releaseTransactionId({ repository, version
|
|
306
|
+
const expectedIdentity = releaseTransactionId({ repository, version, sourceSha: sha, targetRef: normalizedRef(targetRef) });
|
|
245
307
|
assertEqual(existingTransaction.id, expectedIdentity, "transaction-identity-conflict", "durable transaction identity", "Enter repair_required; the durable transaction does not belong to this exact publication target.");
|
|
246
308
|
assertEqual(existingTransaction.repository, repository, "transaction-identity-conflict", "durable transaction repository", "Enter repair_required; never cross repository transaction boundaries.");
|
|
247
309
|
assertEqual(normalizedRef(existingTransaction.target_ref), normalizedRef(targetRef), "transaction-identity-conflict", "durable transaction target ref", "Enter repair_required; never retarget a sealed transaction.");
|
|
248
310
|
assertEqual(existingTransaction.source_sha, sha, "transaction-identity-conflict", "durable transaction source SHA", "Resume with the transaction's exact promotion SHA or enter repair_required.");
|
|
249
|
-
assertEqual(existingTransaction.version,
|
|
311
|
+
assertEqual(existingTransaction.version, version, "transaction-identity-conflict", "durable transaction version", "Enter repair_required; never change a sealed publication version.");
|
|
250
312
|
assertEqual(existingTransaction.channel, passport.target.channel, "transaction-identity-conflict", "durable transaction channel", "Enter repair_required; never move a transaction between channels.");
|
|
251
313
|
}
|
|
252
314
|
if (existingTransaction?.candidateRoot && existingTransaction.candidateRoot !== candidateRoot) fail("transaction-identity-conflict", "existing transaction is sealed to a different candidate root", "Enter repair_required; never reuse the conflicting transaction.");
|
|
@@ -276,8 +338,10 @@ export function verifyReleaseCandidateRecovery({
|
|
|
276
338
|
buildSummary,
|
|
277
339
|
controllerReceipts = [],
|
|
278
340
|
platformManifests = [],
|
|
341
|
+
platformManifestEvidence = [],
|
|
279
342
|
productPayloadManifests = [],
|
|
280
343
|
artifacts = [],
|
|
344
|
+
publicationVersion = "",
|
|
281
345
|
currentToolingSha,
|
|
282
346
|
recoveryRunId = "",
|
|
283
347
|
createdAt = new Date().toISOString(),
|
|
@@ -296,7 +360,7 @@ export function verifyReleaseCandidateRecovery({
|
|
|
296
360
|
if (new Set(recoveredArtifacts.map((artifact) => artifact.name)).size !== recoveredArtifacts.length) {
|
|
297
361
|
fail("artifact-count-mismatch", "candidate artifact names are not unique", "Inspect the candidate run and remove ambiguity by creating a new candidate.");
|
|
298
362
|
}
|
|
299
|
-
validatePlatformPayloads(passport, recoveredArtifacts, platformManifests);
|
|
363
|
+
validatePlatformPayloads(passport, recoveredArtifacts, platformManifests, platformManifestEvidence);
|
|
300
364
|
|
|
301
365
|
const references = new Map((passport.controllerReceipts || []).map((reference) => [reference.controllerId, reference]));
|
|
302
366
|
for (const receipt of controllerReceipts) {
|
|
@@ -331,8 +395,9 @@ export function verifyReleaseCandidateRecovery({
|
|
|
331
395
|
runtimeSha,
|
|
332
396
|
});
|
|
333
397
|
|
|
398
|
+
const version = required(publicationVersion || passport.target?.version, "publicationVersion");
|
|
334
399
|
const actualTransactionId = validateRecoveryTransaction({
|
|
335
|
-
existingTransaction, expectedTransactionId, repository, passport, sha, targetRef, candidateRoot,
|
|
400
|
+
existingTransaction, expectedTransactionId, repository, version, passport, sha, targetRef, candidateRoot,
|
|
336
401
|
});
|
|
337
402
|
|
|
338
403
|
const receipt = {
|
|
@@ -351,7 +416,7 @@ export function verifyReleaseCandidateRecovery({
|
|
|
351
416
|
mergeSha: String(pullRequest.mergeSha || ""),
|
|
352
417
|
tree: passport.source.treeHash,
|
|
353
418
|
},
|
|
354
|
-
target: { channel: passport.target.channel, ref: normalizedRef(targetRef), sha, tree },
|
|
419
|
+
target: { channel: passport.target.channel, ref: normalizedRef(targetRef), sha, tree, version },
|
|
355
420
|
recovered: {
|
|
356
421
|
candidateRoot,
|
|
357
422
|
buildSummaryRoot: `sha256:${passport.diagnostics.buildSummaryHash}`,
|
|
@@ -992,7 +992,7 @@ for (const requiredSnippet of [
|
|
|
992
992
|
"resume-buildchain-runtime-sha:",
|
|
993
993
|
"github-release: true",
|
|
994
994
|
"release-passport-buildchain-self-kfd: true",
|
|
995
|
-
|
|
995
|
+
'artifact-patterns: "buildchain-package-*"',
|
|
996
996
|
"release-passport-impact-json: .buildchain/release-impact.json",
|
|
997
997
|
]) {
|
|
998
998
|
if (!buildchainRefPromotionWorkflow.includes(requiredSnippet)) {
|
|
@@ -1051,6 +1051,8 @@ for (const requiredSnippet of [
|
|
|
1051
1051
|
"publish-sealed-bundle-root: ${{ steps.rc.outputs.publish-sealed-bundle-root }}",
|
|
1052
1052
|
"BUILDCHAIN_EXPECTED_TRANSACTION_ID: ${{ inputs.resume-transaction-id }}",
|
|
1053
1053
|
"if: ${{ inputs.resume-candidate-run-id == '' }}",
|
|
1054
|
+
"Bridge Buildchain self-runtime dependencies",
|
|
1055
|
+
"ln -s .buildchain/runtime/node_modules node_modules",
|
|
1054
1056
|
]) {
|
|
1055
1057
|
if (!releaseCandidatePromoteWorkflow.includes(requiredSnippet)) {
|
|
1056
1058
|
throw new Error(`release candidate promote workflow missing KFD gate pass-through: ${requiredSnippet}`);
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
PUBLICATION_ARTIFACT_CANDIDATE_CONTRACT,
|
|
7
|
+
publicationArtifactCandidateDigest,
|
|
8
|
+
} from "../packages/core/publication-artifact-candidate.js";
|
|
9
|
+
import { createPublicationSealedBundle } from "../packages/core/publication-sealed-bundle.js";
|
|
10
|
+
|
|
11
|
+
function requiredString(value, label) {
|
|
12
|
+
const normalized = String(value || "").trim();
|
|
13
|
+
if (!normalized) throw new Error(`${label} must be a non-empty string`);
|
|
14
|
+
return normalized;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function normalizeSha256(value, label) {
|
|
18
|
+
const digest = requiredString(value, label).replace(/^sha256:/, "").toLowerCase();
|
|
19
|
+
if (!/^[0-9a-f]{64}$/.test(digest)) throw new Error(`${label} must be a sha256 digest`);
|
|
20
|
+
return digest;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function assertGitSha(value, label) {
|
|
24
|
+
const sha = requiredString(value, label);
|
|
25
|
+
if (!/^[0-9a-f]{40}$/i.test(sha)) throw new Error(`${label} must be a 40-character Git SHA`);
|
|
26
|
+
return sha.toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function collectCandidateFiles(root) {
|
|
30
|
+
const resolvedRoot = path.resolve(requiredString(root, "bundleRoot"));
|
|
31
|
+
const files = [];
|
|
32
|
+
const stack = fs.existsSync(resolvedRoot) ? [resolvedRoot] : [];
|
|
33
|
+
while (stack.length) {
|
|
34
|
+
const current = stack.pop();
|
|
35
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
36
|
+
const absolutePath = path.join(current, entry.name);
|
|
37
|
+
if (entry.isDirectory()) {
|
|
38
|
+
stack.push(absolutePath);
|
|
39
|
+
} else if (entry.isFile()) {
|
|
40
|
+
files.push({
|
|
41
|
+
path: path.relative(resolvedRoot, absolutePath).split(path.sep).join("/"),
|
|
42
|
+
absolutePath,
|
|
43
|
+
size: fs.statSync(absolutePath).size,
|
|
44
|
+
sha256: crypto.createHash("sha256").update(fs.readFileSync(absolutePath)).digest("hex"),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { resolvedRoot, files: files.sort((left, right) => left.path.localeCompare(right.path)) };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function createResolvedPublicationSealedBundle({
|
|
53
|
+
bundleRoot,
|
|
54
|
+
repository,
|
|
55
|
+
sourceSha,
|
|
56
|
+
sourceTreeSha,
|
|
57
|
+
runtimeSha,
|
|
58
|
+
releaseCandidateRoot,
|
|
59
|
+
npmArtifacts = [],
|
|
60
|
+
releaseAssetPaths = [],
|
|
61
|
+
} = {}) {
|
|
62
|
+
if (npmArtifacts.length === 0) return undefined;
|
|
63
|
+
const { resolvedRoot, files } = collectCandidateFiles(bundleRoot);
|
|
64
|
+
const byAbsolutePath = new Map(files.map((file) => [path.resolve(file.absolutePath), file]));
|
|
65
|
+
const resolvedNpmArtifacts = npmArtifacts.map((artifact, index) => {
|
|
66
|
+
const file = byAbsolutePath.get(path.resolve(requiredString(artifact.path, `npmArtifacts[${index}].path`)));
|
|
67
|
+
if (!file) throw new Error(`npmArtifacts[${index}].path is outside the sealed payload root`);
|
|
68
|
+
return { file, metadata: artifact };
|
|
69
|
+
});
|
|
70
|
+
const main = resolvedNpmArtifacts.find((entry) => entry.metadata.role === "main")
|
|
71
|
+
|| (resolvedNpmArtifacts.length === 1 ? resolvedNpmArtifacts[0] : undefined);
|
|
72
|
+
if (!main) throw new Error("candidate npm payload set has no unique main package tarball");
|
|
73
|
+
const selectedReleaseAssets = (releaseAssetPaths.length > 0
|
|
74
|
+
? releaseAssetPaths
|
|
75
|
+
: resolvedNpmArtifacts.map((entry) => entry.file.absolutePath))
|
|
76
|
+
.map((assetPath, index) => {
|
|
77
|
+
const file = byAbsolutePath.get(path.resolve(assetPath));
|
|
78
|
+
if (!file) throw new Error(`releaseAssetPaths[${index}] is outside the sealed payload root`);
|
|
79
|
+
return file;
|
|
80
|
+
});
|
|
81
|
+
const candidatePayload = {
|
|
82
|
+
schemaVersion: 1,
|
|
83
|
+
contract: PUBLICATION_ARTIFACT_CANDIDATE_CONTRACT,
|
|
84
|
+
repository: requiredString(repository, "repository"),
|
|
85
|
+
sourceSha: assertGitSha(sourceSha, "sourceSha"),
|
|
86
|
+
sourceTreeSha: assertGitSha(sourceTreeSha, "sourceTreeSha"),
|
|
87
|
+
runtimeSha: assertGitSha(runtimeSha, "runtimeSha"),
|
|
88
|
+
releaseCandidateRoot: `sha256:${normalizeSha256(releaseCandidateRoot, "releaseCandidateRoot")}`,
|
|
89
|
+
files: files.map(({ path: filePath, size, sha256 }) => ({ path: filePath, size, sha256 })),
|
|
90
|
+
};
|
|
91
|
+
const candidate = {
|
|
92
|
+
...candidatePayload,
|
|
93
|
+
candidateDigest: publicationArtifactCandidateDigest(candidatePayload),
|
|
94
|
+
};
|
|
95
|
+
const manifest = createPublicationSealedBundle({
|
|
96
|
+
candidate,
|
|
97
|
+
packageName: main.metadata.name,
|
|
98
|
+
packageVersion: main.metadata.ref,
|
|
99
|
+
npmTarballPath: main.file.path,
|
|
100
|
+
npmIntegrity: main.metadata.integrity,
|
|
101
|
+
releaseAssetPaths: selectedReleaseAssets.map((file) => file.path),
|
|
102
|
+
});
|
|
103
|
+
return { root: resolvedRoot, manifest, npmArtifacts: resolvedNpmArtifacts, files };
|
|
104
|
+
}
|
|
@@ -5,6 +5,7 @@ import os from "node:os";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { execFileSync } from "node:child_process";
|
|
7
7
|
import { pathToFileURL } from "node:url";
|
|
8
|
+
import { createResolvedPublicationSealedBundle } from "./publication-candidate-sealer.mjs";
|
|
8
9
|
import { writeGitHubOutputs } from "./build-contract-core.mjs";
|
|
9
10
|
|
|
10
11
|
const DEFAULT_WORKFLOW_FILE = "build-surface-fixture.yml";
|
|
@@ -421,6 +422,7 @@ export async function resolveReleaseCandidateArtifacts({
|
|
|
421
422
|
requiredArtifactCount = 0,
|
|
422
423
|
publishArtifactKind = "npm",
|
|
423
424
|
publishPackageMain = "",
|
|
425
|
+
runtimeSha = "",
|
|
424
426
|
outputDir = ".buildchain/release-candidate",
|
|
425
427
|
fetchImpl = globalThis.fetch,
|
|
426
428
|
download = true,
|
|
@@ -668,6 +670,27 @@ export async function resolveReleaseCandidateArtifacts({
|
|
|
668
670
|
});
|
|
669
671
|
const requiredArtifactsPath = path.join(resolvedOutput, "publish-required-artifacts.json");
|
|
670
672
|
fs.writeFileSync(requiredArtifactsPath, `${JSON.stringify(generatedRequiredArtifacts, null, 2)}\n`);
|
|
673
|
+
const sealedBundle = publishArtifactKind === "npm"
|
|
674
|
+
? createResolvedPublicationSealedBundle({
|
|
675
|
+
bundleRoot: payloadDir,
|
|
676
|
+
repository: repoInfo.fullName,
|
|
677
|
+
sourceSha: passport.source?.headSha,
|
|
678
|
+
sourceTreeSha: passport.source?.treeHash,
|
|
679
|
+
runtimeSha: runtimeSha || sha,
|
|
680
|
+
releaseCandidateRoot: passport.candidateHash,
|
|
681
|
+
npmArtifacts: npmTarballPaths.map((tarballPath) => ({
|
|
682
|
+
path: tarballPath,
|
|
683
|
+
...readNpmPackageArtifact({ tarballPath, mainPackage: publishPackageMain }),
|
|
684
|
+
})),
|
|
685
|
+
releaseAssetPaths,
|
|
686
|
+
})
|
|
687
|
+
: undefined;
|
|
688
|
+
const sealedBundleManifestPath = sealedBundle
|
|
689
|
+
? path.join(resolvedOutput, "sealed-bundle.json")
|
|
690
|
+
: "";
|
|
691
|
+
if (sealedBundleManifestPath) {
|
|
692
|
+
fs.writeFileSync(sealedBundleManifestPath, `${JSON.stringify(sealedBundle.manifest, null, 2)}\n`);
|
|
693
|
+
}
|
|
671
694
|
return {
|
|
672
695
|
...result,
|
|
673
696
|
paths: {
|
|
@@ -679,6 +702,8 @@ export async function resolveReleaseCandidateArtifacts({
|
|
|
679
702
|
npmTarballs: npmTarballPaths.map(outputPath),
|
|
680
703
|
releaseAssets: releaseAssetPaths.map(outputPath),
|
|
681
704
|
publishRequiredArtifacts: outputPath(requiredArtifactsPath),
|
|
705
|
+
sealedBundleRoot: sealedBundle ? outputPath(sealedBundle.root) : "",
|
|
706
|
+
sealedBundleManifest: sealedBundleManifestPath ? outputPath(sealedBundleManifestPath) : "",
|
|
682
707
|
},
|
|
683
708
|
version: passport.target?.version || "",
|
|
684
709
|
candidateHash: passport.candidateHash || "",
|
|
@@ -705,6 +730,7 @@ export async function resolveReleaseCandidateArtifactsCli() {
|
|
|
705
730
|
requiredArtifactCount: env("BUILDCHAIN_REQUIRED_ARTIFACT_COUNT", "0"),
|
|
706
731
|
publishArtifactKind: env("BUILDCHAIN_PUBLISH_ARTIFACT_KIND", "npm"),
|
|
707
732
|
publishPackageMain: env("BUILDCHAIN_PUBLISH_PACKAGE_MAIN"),
|
|
733
|
+
runtimeSha: env("BUILDCHAIN_CURRENT_RUNTIME_SHA", env("BUILDCHAIN_RUNTIME_SHA", env("BUILDCHAIN_TARGET_SHA"))),
|
|
708
734
|
outputDir: env("BUILDCHAIN_RC_OUTPUT_DIR", ".buildchain/release-candidate"),
|
|
709
735
|
download: releaseCandidateDownloadEnabled(env("BUILDCHAIN_RC_DOWNLOAD", "true")),
|
|
710
736
|
waitSeconds: env("BUILDCHAIN_RC_WAIT_SECONDS", "600"),
|
|
@@ -735,6 +761,8 @@ export async function resolveReleaseCandidateArtifactsCli() {
|
|
|
735
761
|
).join("\n"),
|
|
736
762
|
"publish-required-artifacts-json": JSON.stringify(result.publishRequiredArtifacts || []),
|
|
737
763
|
"publish-required-artifacts-path": result.paths?.publishRequiredArtifacts || "",
|
|
764
|
+
"publish-sealed-bundle-root": result.paths?.sealedBundleRoot || "",
|
|
765
|
+
"publish-sealed-bundle-manifest": result.paths?.sealedBundleManifest || "",
|
|
738
766
|
"release-candidate-run-id": result.run?.id || "",
|
|
739
767
|
"release-candidate-run-url": result.run?.url || "",
|
|
740
768
|
"release-candidate-pr": result.pullRequest?.number ? String(result.pullRequest.number) : "",
|