@kungfu-tech/buildchain 2.14.7-alpha.0 → 2.14.8-alpha.0
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/dist/site/buildchain-contract.json +6 -6
- package/dist/site/buildchain-site.json +7 -7
- package/dist/site/kfd-claims.json +2 -2
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +1 -1
- package/dist/site/page-registry.json +2 -2
- package/dist/site/public-surface-audit.json +1 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +5 -5
- package/docs/release-candidate.md +20 -0
- package/package.json +1 -1
- package/scripts/check-inventory.mjs +3 -0
- package/scripts/generate-channel-promotion-workflow.mjs +58 -34
- package/scripts/promotion-identity-resolver.mjs +127 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"product": {
|
|
5
5
|
"name": "Buildchain",
|
|
6
6
|
"package": "@kungfu-tech/buildchain",
|
|
7
|
-
"version": "2.14.
|
|
7
|
+
"version": "2.14.8-alpha.0",
|
|
8
8
|
"repository": "https://github.com/kungfu-systems/buildchain"
|
|
9
9
|
},
|
|
10
10
|
"majorLine": "v2",
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
"GitHub Release passport and evidence publication is delegated to promote-buildchain-ref after the semver release transaction completes"
|
|
171
171
|
],
|
|
172
172
|
"breakingDigest": "sha256:1f476fb9a4854b362c4b24cef89a3c7675d7d63baf3bc828d7b8ae7d2787cead",
|
|
173
|
-
"auditDigest": "sha256:
|
|
173
|
+
"auditDigest": "sha256:37e44f90dd433606cd01cf4c0b2911a7d2b4f694898c1629dcd905e974ad0c53"
|
|
174
174
|
},
|
|
175
175
|
{
|
|
176
176
|
"contractVersion": 1,
|
|
@@ -212,7 +212,7 @@
|
|
|
212
212
|
"promotion reuses PR-stage release-candidate artifacts and does not run the heavy native build matrix"
|
|
213
213
|
],
|
|
214
214
|
"breakingDigest": "sha256:aa30f22e3af0a89841310bdbdc900844dd95a66974db173fa140a71bbd7e82c0",
|
|
215
|
-
"auditDigest": "sha256:
|
|
215
|
+
"auditDigest": "sha256:9f4d8d0d8837ef2d06352113c1262f2611e6460faea95d969142385e964306e2"
|
|
216
216
|
},
|
|
217
217
|
{
|
|
218
218
|
"contractVersion": 1,
|
|
@@ -582,7 +582,7 @@
|
|
|
582
582
|
"manual entries carry source file digests so downstream sites and agents can detect stale hand-written documentation"
|
|
583
583
|
],
|
|
584
584
|
"breakingDigest": "sha256:7d0d2819e3a3e72989d9c57b5efe9d0bc0a79bc0f2c82a0c7b9d6c5a211a91f2",
|
|
585
|
-
"auditDigest": "sha256:
|
|
585
|
+
"auditDigest": "sha256:af5c2479bf66f24b75a8625ccbbc60830e83fc59d516a27ae3c6c84525489792"
|
|
586
586
|
},
|
|
587
587
|
{
|
|
588
588
|
"contractVersion": 1,
|
|
@@ -2700,7 +2700,7 @@
|
|
|
2700
2700
|
"missing receipts are non-qualifying and must not be represented as a successful controller run"
|
|
2701
2701
|
],
|
|
2702
2702
|
"breakingDigest": "sha256:015d8de793adbd4c539416be7d7512e18cc92097e629b0203fa06d2c183e98bd",
|
|
2703
|
-
"auditDigest": "sha256:
|
|
2703
|
+
"auditDigest": "sha256:9f4d8d0d8837ef2d06352113c1262f2611e6460faea95d969142385e964306e2"
|
|
2704
2704
|
},
|
|
2705
2705
|
{
|
|
2706
2706
|
"contractVersion": 1,
|
|
@@ -2844,5 +2844,5 @@
|
|
|
2844
2844
|
}
|
|
2845
2845
|
],
|
|
2846
2846
|
"compatibilityDigest": "sha256:edc3653e5cb34efd97065a6941db16140bbf375a565bbcf329d9d795bb07676f",
|
|
2847
|
-
"contractDigest": "sha256:
|
|
2847
|
+
"contractDigest": "sha256:b82f5788b3deb02712569d80859a0c38eee8023a84f9e6d252f6e87862c1bfe1"
|
|
2848
2848
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"contract": "kungfu-buildchain-site-bundle",
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"publishedAt": "2026-07-
|
|
4
|
+
"generatedAt": "2026-07-21T14:45:28.549Z",
|
|
5
|
+
"publishedAt": "2026-07-21T14:45:28.549Z",
|
|
6
6
|
"reproducible": true,
|
|
7
7
|
"timestampPolicy": "ci-injected",
|
|
8
8
|
"deterministicInputs": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"declared Buildchain surface manifest contract"
|
|
20
20
|
],
|
|
21
21
|
"sourceDateEpoch": "0",
|
|
22
|
-
"sourceRevision": "
|
|
22
|
+
"sourceRevision": "0d9c71d965ec0825af481f5789c5a138c7dad5dc",
|
|
23
23
|
"timestampPolicyDetails": {
|
|
24
24
|
"contract": "kungfu-buildchain-surface-timestamp-policy",
|
|
25
25
|
"timestampFields": [
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"package": {
|
|
39
39
|
"name": "@kungfu-tech/buildchain",
|
|
40
|
-
"version": "2.14.
|
|
40
|
+
"version": "2.14.8-alpha.0",
|
|
41
41
|
"versionSource": "package.json#version"
|
|
42
42
|
},
|
|
43
43
|
"source": {
|
|
@@ -1330,7 +1330,7 @@
|
|
|
1330
1330
|
],
|
|
1331
1331
|
"maturity": "stable",
|
|
1332
1332
|
"sourcePath": "docs/release-candidate.md",
|
|
1333
|
-
"digest": "sha256:
|
|
1333
|
+
"digest": "sha256:5528710ec63dbdf86a34e69a7ea86dbb4194d72522951fc67108f5cfc58fdd99",
|
|
1334
1334
|
"headings": [
|
|
1335
1335
|
{
|
|
1336
1336
|
"level": 1,
|
|
@@ -1338,7 +1338,7 @@
|
|
|
1338
1338
|
"anchor": "release-candidate-passport"
|
|
1339
1339
|
}
|
|
1340
1340
|
],
|
|
1341
|
-
"markdown": "# Release Candidate Passport\n\nThe release-candidate passport is the pre-promotion evidence contract produced\nafter a reusable build matrix succeeds and before any publish-gate side effects\nrun. It is different from the release passport:\n\n- `release-candidate-passport.json` proves which source SHA, channel, runtime,\n workflow run, and platform artifacts were verified before promotion.\n- `buildchain.release.json` is generated after publish finalization and remains\n the durable audit entrypoint for the published release.\n\nEnable it on the reusable build workflow:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n artifact-name: libnode\n release-candidate: true\n publish-channel: alpha\n publish-source-ref: publish-gate/alpha/v22/v22.22/22.22.3-kf.3-alpha.7\n```\n\nWhen the platform matrix and aggregate summaries complete, Buildchain uploads:\n\n```text\n<artifact-name>-release-candidate-<publish-source-sha>\n```\n\nThe passport contract is `kungfu-buildchain-release-candidate-passport`. It\ncontains:\n\n- repository and pull request context;\n- target channel, target ref, and product version or a non-publish\n `source-<shortSha>` candidate label;\n- source head SHA, merge ref SHA, and the Git `HEAD^{tree}` SHA for PR merge\n equivalence after the channel PR lands;\n- Buildchain runtime ref/SHA and workflow shell ref;\n- workflow run id/attempt/url;\n- normalized platform matrix and artifact summaries;\n- the hash of the aggregate `build-summary.json`.\n\nPromotion workflows that should not rebuild artifacts can enable:\n\n```yaml\n- uses: kungfu-systems/buildchain/actions/promote-buildchain-ref@v2\n with:\n token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}\n sha: ${{ needs.build.outputs.publish-source-sha }}\n target-ref: alpha/v22/v22.22\n promote-only-release-candidate: \"true\"\n release-candidate-passport-path: .buildchain/artifacts/release-candidate-passport.json\n release-candidate-build-summary-path: .buildchain/artifacts/build-summary.json\n```\n\nWith `promote-only-release-candidate: \"true\"`, promotion fails before\nversion-state, publish transaction, tag, or branch side effects when the\npassport does not match the repository, channel, source identity, platform\nmatrix, or build-summary hash. Source identity accepts the exact PR source SHA,\nthe PR merge ref SHA, or an exact Git tree match with the promoted channel HEAD;\nthis keeps post-merge channel commits strict without forcing a rebuild. The\nBuildchain-owned promotion workflow resolves the matching same-repository\nmerged channel PR and downloads its PR-stage RC passport automatically before\npromotion starts. The consumer wrapper defaults to a PR-stage workflow file\nnamed `build.yml` with display name `Build`, and filters the RC passport and\nbuild summary by the configured `artifact-name` before promotion. It also\ndownloads payload artifacts from the same PR-stage run, validates the required\npayload count, passes downloaded platform manifests into the release passport,\nand either forwards an explicit `publish-required-artifacts-json` value or\ngenerates one before calling `promote-buildchain-ref`. Before that call, the\nwrapper creates or updates `publish-gate/{alpha,release,major}` to the\npromotion channel commit and passes that ref, target SHA, and `locked=true` to\nthe promote action with `require-publish-source-lock: \"true\"`. Consumers using\nfloating `@v2` therefore get publish-side source-lock drift protection without\ncopying resolver or promote YAML. The default npm path\ngenerates that requirement list from the downloaded `.tgz` payloads themselves:\nBuildchain reads `package/package.json` inside each tarball for the real scoped\npackage name and version, computes npm-style `sha512-...` integrity over the\ntarball bytes, marks `publish-package-main` as `role: main`, and marks every\nother package as `role: platform`. Consumer workflows therefore stay\ndeclarative and do not need their own artifact download or publish-evidence\ngeneration scripts.\n\nBecause a channel merge can trigger promotion before its PR-stage matrix has\nfinished uploading evidence, the resolver waits up to ten minutes for the exact\nmerged PR's successful workflow run and paired artifacts. Polling remains bound\nto the PR/head identity; timeout or a sibling run still fails closed.\n\nBy default, the wrapper forwards GitHub Release publication to the underlying\n`promote-buildchain-ref` semver model. Once the release transaction is complete,\nthe action creates or updates the public GitHub Release, applies\nprerelease/latest metadata from the semver tag, and uploads the publish evidence\nfile together with the generated release passport assets. This keeps\nnpm/registry publication, Buildchain release passport persistence, and\n`release.published` propagation in one declarative reusable workflow. Consumers\nthat do not publish GitHub Releases can opt out with `github-release: false`.\nFor anchored/manual package releases, the public GitHub Release tag defaults to\n`v<publishedVersion>` while the internal transaction exact tag remains recorded\nin the release passport.\n\nProducts that publish KFD release trust evidence can keep that path declarative\ntoo. Pass KFD-1 self contract witnesses, KFD-2 public claim files, and KFD-3\npre-build/artifact evidence into the wrapper:\n\n```yaml\njobs:\n promote:\n uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v2\n with:\n buildchain-channel: auto\n buildchain-alpha-contract-lock-path: .buildchain/alpha-contract-lock.json\n buildchain-stable-contract-lock-path: .buildchain/contract-lock.json\n channel: alpha\n artifact-name: libnode\n release-passport-kfd-1-witness-jsons: .buildchain/kfd/kfd-1/standard-contract.witness.json\n release-passport-kfd-2-claim-jsons: .buildchain/kfd/kfd-2/release-claims.json\n release-passport-kfd-3-prebuild-witness-jsons: .buildchain/kfd/kfd-3/collaboration-interface.prebuild.json\n release-passport-kfd-3-artifact-verify-command: kungfu agent verify --json\n```\n\nBuildchain forwards those declarations into `promote-buildchain-ref`, verifies\nKFD-1 source/artifact contract surfaces, audits KFD-2 public release claims, and\ncompares KFD-3 declared shipped public surfaces with artifact-exposed public\nsurfaces. The release passport records the results under `kfd-1`, `kfd-2`, and\nthe KFD-provided `kfd-3` section.\n\nManaged consumers may also ask the promotion wrapper to assemble sealed\npublication evidence from the exact release candidate instead of producing\nshort-lived admission JSON in repository-specific workflow code:\n\n```yaml\n publication-auto-admission: true\n publication-auto-no-gate: true\n publication-publisher-workflow-path: .github/workflows/buildchain-ref-promotion.yml\n publication-product: Example Product\n publication-target: npm:@example/product\n publication-package-name: \"@example/product\"\n```\n\n`publication-auto-no-gate` is an explicit consumer decision, not a default. A\nconsumer with a Shifu Gate registry supplies `publication-gate-aggregate-json`\ninstead. Buildchain still requires caller-owned RC evidence, an exact authority\nruntime and source SHA, a repository-local publisher workflow, matching npm\ntarget/package identity, and a qualifying control-plane audit.\n\nA consumer that owns additional product qualification semantics can opt in to\nthe sealed handoff without teaching Buildchain those semantics:\n\n```yaml\n publication-consumer-predicate-id: kungfu.release-admission/v1\n publication-consumer-qualification-command: node scripts/qualify-release.mjs\n```\n\nThe command reads `BUILDCHAIN_PUBLICATION_CAPABILITY_PATH` and\n`BUILDCHAIN_PUBLICATION_GATE_AGGREGATE_PATH`, evaluates the complete aggregate,\nand writes a decision JSON document to\n`BUILDCHAIN_PUBLICATION_QUALIFICATION_RESULT_PATH`. It also receives\n`BUILDCHAIN_PUBLICATION_PREDICATE_ID` and\n`BUILDCHAIN_PUBLICATION_PREDICATE_DIGEST`. The separate qualification job has\nno write or OIDC permission and does not inherit publication secrets. A\nsuccessful deterministic receipt is rechecked by the provider action\nimmediately before mutation. Omitting both inputs preserves the existing\nconsumer contract; supplying only one fails closed."
|
|
1341
|
+
"markdown": "# Release Candidate Passport\n\nThe release-candidate passport is the pre-promotion evidence contract produced\nafter a reusable build matrix succeeds and before any publish-gate side effects\nrun. It is different from the release passport:\n\n- `release-candidate-passport.json` proves which source SHA, channel, runtime,\n workflow run, and platform artifacts were verified before promotion.\n- `buildchain.release.json` is generated after publish finalization and remains\n the durable audit entrypoint for the published release.\n\nEnable it on the reusable build workflow:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n artifact-name: libnode\n release-candidate: true\n publish-channel: alpha\n publish-source-ref: publish-gate/alpha/v22/v22.22/22.22.3-kf.3-alpha.7\n```\n\nWhen the platform matrix and aggregate summaries complete, Buildchain uploads:\n\n```text\n<artifact-name>-release-candidate-<publish-source-sha>\n```\n\nThe passport contract is `kungfu-buildchain-release-candidate-passport`. It\ncontains:\n\n- repository and pull request context;\n- target channel, target ref, and product version or a non-publish\n `source-<shortSha>` candidate label;\n- source head SHA, merge ref SHA, and the Git `HEAD^{tree}` SHA for PR merge\n equivalence after the channel PR lands;\n- Buildchain runtime ref/SHA and workflow shell ref;\n- workflow run id/attempt/url;\n- normalized platform matrix and artifact summaries;\n- the hash of the aggregate `build-summary.json`.\n\nPromotion workflows that should not rebuild artifacts can enable:\n\n```yaml\n- uses: kungfu-systems/buildchain/actions/promote-buildchain-ref@v2\n with:\n token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}\n sha: ${{ needs.build.outputs.publish-source-sha }}\n target-ref: alpha/v22/v22.22\n promote-only-release-candidate: \"true\"\n release-candidate-passport-path: .buildchain/artifacts/release-candidate-passport.json\n release-candidate-build-summary-path: .buildchain/artifacts/build-summary.json\n```\n\nWith `promote-only-release-candidate: \"true\"`, promotion fails before\nversion-state, publish transaction, tag, or branch side effects when the\npassport does not match the repository, channel, source identity, platform\nmatrix, or build-summary hash. Source identity accepts the exact PR source SHA,\nthe PR merge ref SHA, or an exact Git tree match with the promoted channel HEAD;\nthis keeps post-merge channel commits strict without forcing a rebuild. The\nBuildchain-owned promotion workflow resolves the matching same-repository\nmerged channel PR and downloads its PR-stage RC passport automatically before\npromotion starts. The consumer wrapper defaults to a PR-stage workflow file\nnamed `build.yml` with display name `Build`, and filters the RC passport and\nbuild summary by the configured `artifact-name` before promotion. It also\ndownloads payload artifacts from the same PR-stage run, validates the required\npayload count, passes downloaded platform manifests into the release passport,\nand either forwards an explicit `publish-required-artifacts-json` value or\ngenerates one before calling `promote-buildchain-ref`. Before that call, the\nwrapper creates or updates `publish-gate/{alpha,release,major}` to the\npromotion channel commit and passes that ref, target SHA, and `locked=true` to\nthe promote action with `require-publish-source-lock: \"true\"`. Consumers using\nfloating `@v2` therefore get publish-side source-lock drift protection without\ncopying resolver or promote YAML. The default npm path\ngenerates that requirement list from the downloaded `.tgz` payloads themselves:\nBuildchain reads `package/package.json` inside each tarball for the real scoped\npackage name and version, computes npm-style `sha512-...` integrity over the\ntarball bytes, marks `publish-package-main` as `role: main`, and marks every\nother package as `role: platform`. Consumer workflows therefore stay\ndeclarative and do not need their own artifact download or publish-evidence\ngeneration scripts.\n\nBecause a channel merge can trigger promotion before its PR-stage matrix has\nfinished uploading evidence, the resolver waits up to ten minutes for the exact\nmerged PR's successful workflow run and paired artifacts. Polling remains bound\nto the PR/head identity; timeout or a sibling run still fails closed.\n\nThe public promotion router preserves the requested `vN` or `vN-alpha` ref as\naudit metadata, but binds the router to GitHub's selected reusable-workflow SHA\nand resolves each remaining floating shell/runtime ref exactly once. Every\nlater checkout and delegated promotion receives those immutable SHAs, so a\nchannel tag moving during the run cannot mix two Buildchain revisions.\n\nIf a promote-only run failed after its consumer commit was already merged and\nits PR-stage artifacts were built, rerun the complete failed workflow after the\nfixed Buildchain channel is published:\n\n```bash\ngh run rerun <run-id> --repo <owner>/<consumer>\n```\n\nUse a complete rerun, not `--failed`, so GitHub resolves the reusable workflow\nagain. The release-candidate resolver reuses the existing candidate artifacts,\nand the durable publication transaction makes the recovery idempotent; do not\nopen a replacement consumer PR or rebuild the native matrix solely for this\nrouter failure.\n\nBy default, the wrapper forwards GitHub Release publication to the underlying\n`promote-buildchain-ref` semver model. Once the release transaction is complete,\nthe action creates or updates the public GitHub Release, applies\nprerelease/latest metadata from the semver tag, and uploads the publish evidence\nfile together with the generated release passport assets. This keeps\nnpm/registry publication, Buildchain release passport persistence, and\n`release.published` propagation in one declarative reusable workflow. Consumers\nthat do not publish GitHub Releases can opt out with `github-release: false`.\nFor anchored/manual package releases, the public GitHub Release tag defaults to\n`v<publishedVersion>` while the internal transaction exact tag remains recorded\nin the release passport.\n\nProducts that publish KFD release trust evidence can keep that path declarative\ntoo. Pass KFD-1 self contract witnesses, KFD-2 public claim files, and KFD-3\npre-build/artifact evidence into the wrapper:\n\n```yaml\njobs:\n promote:\n uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v2\n with:\n buildchain-channel: auto\n buildchain-alpha-contract-lock-path: .buildchain/alpha-contract-lock.json\n buildchain-stable-contract-lock-path: .buildchain/contract-lock.json\n channel: alpha\n artifact-name: libnode\n release-passport-kfd-1-witness-jsons: .buildchain/kfd/kfd-1/standard-contract.witness.json\n release-passport-kfd-2-claim-jsons: .buildchain/kfd/kfd-2/release-claims.json\n release-passport-kfd-3-prebuild-witness-jsons: .buildchain/kfd/kfd-3/collaboration-interface.prebuild.json\n release-passport-kfd-3-artifact-verify-command: kungfu agent verify --json\n```\n\nBuildchain forwards those declarations into `promote-buildchain-ref`, verifies\nKFD-1 source/artifact contract surfaces, audits KFD-2 public release claims, and\ncompares KFD-3 declared shipped public surfaces with artifact-exposed public\nsurfaces. The release passport records the results under `kfd-1`, `kfd-2`, and\nthe KFD-provided `kfd-3` section.\n\nManaged consumers may also ask the promotion wrapper to assemble sealed\npublication evidence from the exact release candidate instead of producing\nshort-lived admission JSON in repository-specific workflow code:\n\n```yaml\n publication-auto-admission: true\n publication-auto-no-gate: true\n publication-publisher-workflow-path: .github/workflows/buildchain-ref-promotion.yml\n publication-product: Example Product\n publication-target: npm:@example/product\n publication-package-name: \"@example/product\"\n```\n\n`publication-auto-no-gate` is an explicit consumer decision, not a default. A\nconsumer with a Shifu Gate registry supplies `publication-gate-aggregate-json`\ninstead. Buildchain still requires caller-owned RC evidence, an exact authority\nruntime and source SHA, a repository-local publisher workflow, matching npm\ntarget/package identity, and a qualifying control-plane audit.\n\nA consumer that owns additional product qualification semantics can opt in to\nthe sealed handoff without teaching Buildchain those semantics:\n\n```yaml\n publication-consumer-predicate-id: kungfu.release-admission/v1\n publication-consumer-qualification-command: node scripts/qualify-release.mjs\n```\n\nThe command reads `BUILDCHAIN_PUBLICATION_CAPABILITY_PATH` and\n`BUILDCHAIN_PUBLICATION_GATE_AGGREGATE_PATH`, evaluates the complete aggregate,\nand writes a decision JSON document to\n`BUILDCHAIN_PUBLICATION_QUALIFICATION_RESULT_PATH`. It also receives\n`BUILDCHAIN_PUBLICATION_PREDICATE_ID` and\n`BUILDCHAIN_PUBLICATION_PREDICATE_DIGEST`. The separate qualification job has\nno write or OIDC permission and does not inherit publication secrets. A\nsuccessful deterministic receipt is rechecked by the provider action\nimmediately before mutation. Omitting both inputs preserves the existing\nconsumer contract; supplying only one fails closed."
|
|
1342
1342
|
},
|
|
1343
1343
|
{
|
|
1344
1344
|
"id": "manual:release-flow",
|
|
@@ -2569,7 +2569,7 @@
|
|
|
2569
2569
|
"path": "docs/release-candidate.md",
|
|
2570
2570
|
"plane": "verify",
|
|
2571
2571
|
"exists": true,
|
|
2572
|
-
"digest": "sha256:
|
|
2572
|
+
"digest": "sha256:5528710ec63dbdf86a34e69a7ea86dbb4194d72522951fc67108f5cfc58fdd99"
|
|
2573
2573
|
},
|
|
2574
2574
|
{
|
|
2575
2575
|
"id": "release-propagation",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"contract": "kungfu-buildchain-public-surface-reverse-audit",
|
|
22
22
|
"path": "dist/site/public-surface-audit.json",
|
|
23
23
|
"status": "passed",
|
|
24
|
-
"sha256": "
|
|
24
|
+
"sha256": "74cb3ec8e051754901cbdcffd06fb7fa7579c4a1ba27488e5e5340abf5497952",
|
|
25
25
|
"summary": {
|
|
26
26
|
"cliCommandCount": 84,
|
|
27
27
|
"workflowCount": 51,
|
|
@@ -228,7 +228,7 @@
|
|
|
228
228
|
"contract": "kungfu-buildchain-public-surface-reverse-audit",
|
|
229
229
|
"path": "dist/site/public-surface-audit.json",
|
|
230
230
|
"status": "passed",
|
|
231
|
-
"sha256": "
|
|
231
|
+
"sha256": "74cb3ec8e051754901cbdcffd06fb7fa7579c4a1ba27488e5e5340abf5497952",
|
|
232
232
|
"summary": {
|
|
233
233
|
"cliCommandCount": 84,
|
|
234
234
|
"workflowCount": 51,
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"title": "Release Candidate Passport",
|
|
80
80
|
"path": "docs/release-candidate.md",
|
|
81
81
|
"plane": "verify",
|
|
82
|
-
"digest": "sha256:
|
|
82
|
+
"digest": "sha256:5528710ec63dbdf86a34e69a7ea86dbb4194d72522951fc67108f5cfc58fdd99",
|
|
83
83
|
"capabilityGroup": "reusable-build",
|
|
84
84
|
"audience": [
|
|
85
85
|
"release-operator",
|
|
@@ -1264,7 +1264,7 @@
|
|
|
1264
1264
|
],
|
|
1265
1265
|
"maturity": "stable",
|
|
1266
1266
|
"sourcePath": "docs/release-candidate.md",
|
|
1267
|
-
"digest": "sha256:
|
|
1267
|
+
"digest": "sha256:5528710ec63dbdf86a34e69a7ea86dbb4194d72522951fc67108f5cfc58fdd99",
|
|
1268
1268
|
"headings": [
|
|
1269
1269
|
{
|
|
1270
1270
|
"level": 1,
|
|
@@ -1272,7 +1272,7 @@
|
|
|
1272
1272
|
"anchor": "release-candidate-passport"
|
|
1273
1273
|
}
|
|
1274
1274
|
],
|
|
1275
|
-
"markdown": "# Release Candidate Passport\n\nThe release-candidate passport is the pre-promotion evidence contract produced\nafter a reusable build matrix succeeds and before any publish-gate side effects\nrun. It is different from the release passport:\n\n- `release-candidate-passport.json` proves which source SHA, channel, runtime,\n workflow run, and platform artifacts were verified before promotion.\n- `buildchain.release.json` is generated after publish finalization and remains\n the durable audit entrypoint for the published release.\n\nEnable it on the reusable build workflow:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n artifact-name: libnode\n release-candidate: true\n publish-channel: alpha\n publish-source-ref: publish-gate/alpha/v22/v22.22/22.22.3-kf.3-alpha.7\n```\n\nWhen the platform matrix and aggregate summaries complete, Buildchain uploads:\n\n```text\n<artifact-name>-release-candidate-<publish-source-sha>\n```\n\nThe passport contract is `kungfu-buildchain-release-candidate-passport`. It\ncontains:\n\n- repository and pull request context;\n- target channel, target ref, and product version or a non-publish\n `source-<shortSha>` candidate label;\n- source head SHA, merge ref SHA, and the Git `HEAD^{tree}` SHA for PR merge\n equivalence after the channel PR lands;\n- Buildchain runtime ref/SHA and workflow shell ref;\n- workflow run id/attempt/url;\n- normalized platform matrix and artifact summaries;\n- the hash of the aggregate `build-summary.json`.\n\nPromotion workflows that should not rebuild artifacts can enable:\n\n```yaml\n- uses: kungfu-systems/buildchain/actions/promote-buildchain-ref@v2\n with:\n token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}\n sha: ${{ needs.build.outputs.publish-source-sha }}\n target-ref: alpha/v22/v22.22\n promote-only-release-candidate: \"true\"\n release-candidate-passport-path: .buildchain/artifacts/release-candidate-passport.json\n release-candidate-build-summary-path: .buildchain/artifacts/build-summary.json\n```\n\nWith `promote-only-release-candidate: \"true\"`, promotion fails before\nversion-state, publish transaction, tag, or branch side effects when the\npassport does not match the repository, channel, source identity, platform\nmatrix, or build-summary hash. Source identity accepts the exact PR source SHA,\nthe PR merge ref SHA, or an exact Git tree match with the promoted channel HEAD;\nthis keeps post-merge channel commits strict without forcing a rebuild. The\nBuildchain-owned promotion workflow resolves the matching same-repository\nmerged channel PR and downloads its PR-stage RC passport automatically before\npromotion starts. The consumer wrapper defaults to a PR-stage workflow file\nnamed `build.yml` with display name `Build`, and filters the RC passport and\nbuild summary by the configured `artifact-name` before promotion. It also\ndownloads payload artifacts from the same PR-stage run, validates the required\npayload count, passes downloaded platform manifests into the release passport,\nand either forwards an explicit `publish-required-artifacts-json` value or\ngenerates one before calling `promote-buildchain-ref`. Before that call, the\nwrapper creates or updates `publish-gate/{alpha,release,major}` to the\npromotion channel commit and passes that ref, target SHA, and `locked=true` to\nthe promote action with `require-publish-source-lock: \"true\"`. Consumers using\nfloating `@v2` therefore get publish-side source-lock drift protection without\ncopying resolver or promote YAML. The default npm path\ngenerates that requirement list from the downloaded `.tgz` payloads themselves:\nBuildchain reads `package/package.json` inside each tarball for the real scoped\npackage name and version, computes npm-style `sha512-...` integrity over the\ntarball bytes, marks `publish-package-main` as `role: main`, and marks every\nother package as `role: platform`. Consumer workflows therefore stay\ndeclarative and do not need their own artifact download or publish-evidence\ngeneration scripts.\n\nBecause a channel merge can trigger promotion before its PR-stage matrix has\nfinished uploading evidence, the resolver waits up to ten minutes for the exact\nmerged PR's successful workflow run and paired artifacts. Polling remains bound\nto the PR/head identity; timeout or a sibling run still fails closed.\n\nBy default, the wrapper forwards GitHub Release publication to the underlying\n`promote-buildchain-ref` semver model. Once the release transaction is complete,\nthe action creates or updates the public GitHub Release, applies\nprerelease/latest metadata from the semver tag, and uploads the publish evidence\nfile together with the generated release passport assets. This keeps\nnpm/registry publication, Buildchain release passport persistence, and\n`release.published` propagation in one declarative reusable workflow. Consumers\nthat do not publish GitHub Releases can opt out with `github-release: false`.\nFor anchored/manual package releases, the public GitHub Release tag defaults to\n`v<publishedVersion>` while the internal transaction exact tag remains recorded\nin the release passport.\n\nProducts that publish KFD release trust evidence can keep that path declarative\ntoo. Pass KFD-1 self contract witnesses, KFD-2 public claim files, and KFD-3\npre-build/artifact evidence into the wrapper:\n\n```yaml\njobs:\n promote:\n uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v2\n with:\n buildchain-channel: auto\n buildchain-alpha-contract-lock-path: .buildchain/alpha-contract-lock.json\n buildchain-stable-contract-lock-path: .buildchain/contract-lock.json\n channel: alpha\n artifact-name: libnode\n release-passport-kfd-1-witness-jsons: .buildchain/kfd/kfd-1/standard-contract.witness.json\n release-passport-kfd-2-claim-jsons: .buildchain/kfd/kfd-2/release-claims.json\n release-passport-kfd-3-prebuild-witness-jsons: .buildchain/kfd/kfd-3/collaboration-interface.prebuild.json\n release-passport-kfd-3-artifact-verify-command: kungfu agent verify --json\n```\n\nBuildchain forwards those declarations into `promote-buildchain-ref`, verifies\nKFD-1 source/artifact contract surfaces, audits KFD-2 public release claims, and\ncompares KFD-3 declared shipped public surfaces with artifact-exposed public\nsurfaces. The release passport records the results under `kfd-1`, `kfd-2`, and\nthe KFD-provided `kfd-3` section.\n\nManaged consumers may also ask the promotion wrapper to assemble sealed\npublication evidence from the exact release candidate instead of producing\nshort-lived admission JSON in repository-specific workflow code:\n\n```yaml\n publication-auto-admission: true\n publication-auto-no-gate: true\n publication-publisher-workflow-path: .github/workflows/buildchain-ref-promotion.yml\n publication-product: Example Product\n publication-target: npm:@example/product\n publication-package-name: \"@example/product\"\n```\n\n`publication-auto-no-gate` is an explicit consumer decision, not a default. A\nconsumer with a Shifu Gate registry supplies `publication-gate-aggregate-json`\ninstead. Buildchain still requires caller-owned RC evidence, an exact authority\nruntime and source SHA, a repository-local publisher workflow, matching npm\ntarget/package identity, and a qualifying control-plane audit.\n\nA consumer that owns additional product qualification semantics can opt in to\nthe sealed handoff without teaching Buildchain those semantics:\n\n```yaml\n publication-consumer-predicate-id: kungfu.release-admission/v1\n publication-consumer-qualification-command: node scripts/qualify-release.mjs\n```\n\nThe command reads `BUILDCHAIN_PUBLICATION_CAPABILITY_PATH` and\n`BUILDCHAIN_PUBLICATION_GATE_AGGREGATE_PATH`, evaluates the complete aggregate,\nand writes a decision JSON document to\n`BUILDCHAIN_PUBLICATION_QUALIFICATION_RESULT_PATH`. It also receives\n`BUILDCHAIN_PUBLICATION_PREDICATE_ID` and\n`BUILDCHAIN_PUBLICATION_PREDICATE_DIGEST`. The separate qualification job has\nno write or OIDC permission and does not inherit publication secrets. A\nsuccessful deterministic receipt is rechecked by the provider action\nimmediately before mutation. Omitting both inputs preserves the existing\nconsumer contract; supplying only one fails closed."
|
|
1275
|
+
"markdown": "# Release Candidate Passport\n\nThe release-candidate passport is the pre-promotion evidence contract produced\nafter a reusable build matrix succeeds and before any publish-gate side effects\nrun. It is different from the release passport:\n\n- `release-candidate-passport.json` proves which source SHA, channel, runtime,\n workflow run, and platform artifacts were verified before promotion.\n- `buildchain.release.json` is generated after publish finalization and remains\n the durable audit entrypoint for the published release.\n\nEnable it on the reusable build workflow:\n\n```yaml\njobs:\n build:\n uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2\n with:\n artifact-name: libnode\n release-candidate: true\n publish-channel: alpha\n publish-source-ref: publish-gate/alpha/v22/v22.22/22.22.3-kf.3-alpha.7\n```\n\nWhen the platform matrix and aggregate summaries complete, Buildchain uploads:\n\n```text\n<artifact-name>-release-candidate-<publish-source-sha>\n```\n\nThe passport contract is `kungfu-buildchain-release-candidate-passport`. It\ncontains:\n\n- repository and pull request context;\n- target channel, target ref, and product version or a non-publish\n `source-<shortSha>` candidate label;\n- source head SHA, merge ref SHA, and the Git `HEAD^{tree}` SHA for PR merge\n equivalence after the channel PR lands;\n- Buildchain runtime ref/SHA and workflow shell ref;\n- workflow run id/attempt/url;\n- normalized platform matrix and artifact summaries;\n- the hash of the aggregate `build-summary.json`.\n\nPromotion workflows that should not rebuild artifacts can enable:\n\n```yaml\n- uses: kungfu-systems/buildchain/actions/promote-buildchain-ref@v2\n with:\n token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}\n sha: ${{ needs.build.outputs.publish-source-sha }}\n target-ref: alpha/v22/v22.22\n promote-only-release-candidate: \"true\"\n release-candidate-passport-path: .buildchain/artifacts/release-candidate-passport.json\n release-candidate-build-summary-path: .buildchain/artifacts/build-summary.json\n```\n\nWith `promote-only-release-candidate: \"true\"`, promotion fails before\nversion-state, publish transaction, tag, or branch side effects when the\npassport does not match the repository, channel, source identity, platform\nmatrix, or build-summary hash. Source identity accepts the exact PR source SHA,\nthe PR merge ref SHA, or an exact Git tree match with the promoted channel HEAD;\nthis keeps post-merge channel commits strict without forcing a rebuild. The\nBuildchain-owned promotion workflow resolves the matching same-repository\nmerged channel PR and downloads its PR-stage RC passport automatically before\npromotion starts. The consumer wrapper defaults to a PR-stage workflow file\nnamed `build.yml` with display name `Build`, and filters the RC passport and\nbuild summary by the configured `artifact-name` before promotion. It also\ndownloads payload artifacts from the same PR-stage run, validates the required\npayload count, passes downloaded platform manifests into the release passport,\nand either forwards an explicit `publish-required-artifacts-json` value or\ngenerates one before calling `promote-buildchain-ref`. Before that call, the\nwrapper creates or updates `publish-gate/{alpha,release,major}` to the\npromotion channel commit and passes that ref, target SHA, and `locked=true` to\nthe promote action with `require-publish-source-lock: \"true\"`. Consumers using\nfloating `@v2` therefore get publish-side source-lock drift protection without\ncopying resolver or promote YAML. The default npm path\ngenerates that requirement list from the downloaded `.tgz` payloads themselves:\nBuildchain reads `package/package.json` inside each tarball for the real scoped\npackage name and version, computes npm-style `sha512-...` integrity over the\ntarball bytes, marks `publish-package-main` as `role: main`, and marks every\nother package as `role: platform`. Consumer workflows therefore stay\ndeclarative and do not need their own artifact download or publish-evidence\ngeneration scripts.\n\nBecause a channel merge can trigger promotion before its PR-stage matrix has\nfinished uploading evidence, the resolver waits up to ten minutes for the exact\nmerged PR's successful workflow run and paired artifacts. Polling remains bound\nto the PR/head identity; timeout or a sibling run still fails closed.\n\nThe public promotion router preserves the requested `vN` or `vN-alpha` ref as\naudit metadata, but binds the router to GitHub's selected reusable-workflow SHA\nand resolves each remaining floating shell/runtime ref exactly once. Every\nlater checkout and delegated promotion receives those immutable SHAs, so a\nchannel tag moving during the run cannot mix two Buildchain revisions.\n\nIf a promote-only run failed after its consumer commit was already merged and\nits PR-stage artifacts were built, rerun the complete failed workflow after the\nfixed Buildchain channel is published:\n\n```bash\ngh run rerun <run-id> --repo <owner>/<consumer>\n```\n\nUse a complete rerun, not `--failed`, so GitHub resolves the reusable workflow\nagain. The release-candidate resolver reuses the existing candidate artifacts,\nand the durable publication transaction makes the recovery idempotent; do not\nopen a replacement consumer PR or rebuild the native matrix solely for this\nrouter failure.\n\nBy default, the wrapper forwards GitHub Release publication to the underlying\n`promote-buildchain-ref` semver model. Once the release transaction is complete,\nthe action creates or updates the public GitHub Release, applies\nprerelease/latest metadata from the semver tag, and uploads the publish evidence\nfile together with the generated release passport assets. This keeps\nnpm/registry publication, Buildchain release passport persistence, and\n`release.published` propagation in one declarative reusable workflow. Consumers\nthat do not publish GitHub Releases can opt out with `github-release: false`.\nFor anchored/manual package releases, the public GitHub Release tag defaults to\n`v<publishedVersion>` while the internal transaction exact tag remains recorded\nin the release passport.\n\nProducts that publish KFD release trust evidence can keep that path declarative\ntoo. Pass KFD-1 self contract witnesses, KFD-2 public claim files, and KFD-3\npre-build/artifact evidence into the wrapper:\n\n```yaml\njobs:\n promote:\n uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v2\n with:\n buildchain-channel: auto\n buildchain-alpha-contract-lock-path: .buildchain/alpha-contract-lock.json\n buildchain-stable-contract-lock-path: .buildchain/contract-lock.json\n channel: alpha\n artifact-name: libnode\n release-passport-kfd-1-witness-jsons: .buildchain/kfd/kfd-1/standard-contract.witness.json\n release-passport-kfd-2-claim-jsons: .buildchain/kfd/kfd-2/release-claims.json\n release-passport-kfd-3-prebuild-witness-jsons: .buildchain/kfd/kfd-3/collaboration-interface.prebuild.json\n release-passport-kfd-3-artifact-verify-command: kungfu agent verify --json\n```\n\nBuildchain forwards those declarations into `promote-buildchain-ref`, verifies\nKFD-1 source/artifact contract surfaces, audits KFD-2 public release claims, and\ncompares KFD-3 declared shipped public surfaces with artifact-exposed public\nsurfaces. The release passport records the results under `kfd-1`, `kfd-2`, and\nthe KFD-provided `kfd-3` section.\n\nManaged consumers may also ask the promotion wrapper to assemble sealed\npublication evidence from the exact release candidate instead of producing\nshort-lived admission JSON in repository-specific workflow code:\n\n```yaml\n publication-auto-admission: true\n publication-auto-no-gate: true\n publication-publisher-workflow-path: .github/workflows/buildchain-ref-promotion.yml\n publication-product: Example Product\n publication-target: npm:@example/product\n publication-package-name: \"@example/product\"\n```\n\n`publication-auto-no-gate` is an explicit consumer decision, not a default. A\nconsumer with a Shifu Gate registry supplies `publication-gate-aggregate-json`\ninstead. Buildchain still requires caller-owned RC evidence, an exact authority\nruntime and source SHA, a repository-local publisher workflow, matching npm\ntarget/package identity, and a qualifying control-plane audit.\n\nA consumer that owns additional product qualification semantics can opt in to\nthe sealed handoff without teaching Buildchain those semantics:\n\n```yaml\n publication-consumer-predicate-id: kungfu.release-admission/v1\n publication-consumer-qualification-command: node scripts/qualify-release.mjs\n```\n\nThe command reads `BUILDCHAIN_PUBLICATION_CAPABILITY_PATH` and\n`BUILDCHAIN_PUBLICATION_GATE_AGGREGATE_PATH`, evaluates the complete aggregate,\nand writes a decision JSON document to\n`BUILDCHAIN_PUBLICATION_QUALIFICATION_RESULT_PATH`. It also receives\n`BUILDCHAIN_PUBLICATION_PREDICATE_ID` and\n`BUILDCHAIN_PUBLICATION_PREDICATE_DIGEST`. The separate qualification job has\nno write or OIDC permission and does not inherit publication secrets. A\nsuccessful deterministic receipt is rechecked by the provider action\nimmediately before mutation. Omitting both inputs preserves the existing\nconsumer contract; supplying only one fails closed."
|
|
1276
1276
|
},
|
|
1277
1277
|
{
|
|
1278
1278
|
"id": "manual:release-flow",
|
|
@@ -3690,7 +3690,7 @@
|
|
|
3690
3690
|
"pageRegistryPath": "dist/site/page-registry.json",
|
|
3691
3691
|
"cliRegistryDigest": "90c8650f870c350ca187c612c37f2459dcbfe265aa9b1d5b375ee5c4fb31fab6",
|
|
3692
3692
|
"workflowRegistryDigest": "02950eef2769b1bd2d899099ef23c7b9ae716b18d602e25aef169d788bc6ece0",
|
|
3693
|
-
"pageRegistryDigest": "
|
|
3693
|
+
"pageRegistryDigest": "b5bdca0bc6ed05a2a03b6456785381a333af238c25bd33c64be1b4882fcccd9b"
|
|
3694
3694
|
},
|
|
3695
3695
|
"comparison": {
|
|
3696
3696
|
"missingCliRegistry": [],
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"contract": "kungfu-buildchain-publication-release-registry",
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"publishedAt": "2026-07-
|
|
4
|
+
"generatedAt": "2026-07-21T14:45:28.549Z",
|
|
5
|
+
"publishedAt": "2026-07-21T14:45:28.549Z",
|
|
6
6
|
"reproducible": true,
|
|
7
7
|
"timestampPolicy": "ci-injected",
|
|
8
8
|
"deterministicInputs": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"declared Buildchain surface manifest contract"
|
|
20
20
|
],
|
|
21
21
|
"sourceDateEpoch": "0",
|
|
22
|
-
"sourceRevision": "
|
|
22
|
+
"sourceRevision": "0d9c71d965ec0825af481f5789c5a138c7dad5dc",
|
|
23
23
|
"timestampPolicyDetails": {
|
|
24
24
|
"contract": "kungfu-buildchain-surface-timestamp-policy",
|
|
25
25
|
"timestampFields": [
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"package": {
|
|
34
34
|
"name": "@kungfu-tech/buildchain",
|
|
35
|
-
"version": "2.14.
|
|
35
|
+
"version": "2.14.8-alpha.0",
|
|
36
36
|
"versionSource": "package.json#version"
|
|
37
37
|
},
|
|
38
38
|
"sourceKind": "package-site-bundle",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"contract": "kungfu-buildchain-site-manifest",
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"publishedAt": "2026-07-
|
|
4
|
+
"generatedAt": "2026-07-21T14:45:28.549Z",
|
|
5
|
+
"publishedAt": "2026-07-21T14:45:28.549Z",
|
|
6
6
|
"reproducible": true,
|
|
7
7
|
"timestampPolicy": "ci-injected",
|
|
8
8
|
"deterministicInputs": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"declared Buildchain surface manifest contract"
|
|
20
20
|
],
|
|
21
21
|
"sourceDateEpoch": "0",
|
|
22
|
-
"sourceRevision": "
|
|
22
|
+
"sourceRevision": "0d9c71d965ec0825af481f5789c5a138c7dad5dc",
|
|
23
23
|
"timestampPolicyDetails": {
|
|
24
24
|
"contract": "kungfu-buildchain-surface-timestamp-policy",
|
|
25
25
|
"timestampFields": [
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"package": {
|
|
39
39
|
"name": "@kungfu-tech/buildchain",
|
|
40
|
-
"version": "2.14.
|
|
40
|
+
"version": "2.14.8-alpha.0",
|
|
41
41
|
"versionSource": "package.json#version"
|
|
42
42
|
},
|
|
43
43
|
"entrypoint": "buildchain-site.json",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"path": "docs/release-candidate.md",
|
|
94
94
|
"plane": "verify",
|
|
95
95
|
"exists": true,
|
|
96
|
-
"digest": "sha256:
|
|
96
|
+
"digest": "sha256:5528710ec63dbdf86a34e69a7ea86dbb4194d72522951fc67108f5cfc58fdd99"
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
"id": "release-propagation",
|
|
@@ -87,6 +87,26 @@ finished uploading evidence, the resolver waits up to ten minutes for the exact
|
|
|
87
87
|
merged PR's successful workflow run and paired artifacts. Polling remains bound
|
|
88
88
|
to the PR/head identity; timeout or a sibling run still fails closed.
|
|
89
89
|
|
|
90
|
+
The public promotion router preserves the requested `vN` or `vN-alpha` ref as
|
|
91
|
+
audit metadata, but binds the router to GitHub's selected reusable-workflow SHA
|
|
92
|
+
and resolves each remaining floating shell/runtime ref exactly once. Every
|
|
93
|
+
later checkout and delegated promotion receives those immutable SHAs, so a
|
|
94
|
+
channel tag moving during the run cannot mix two Buildchain revisions.
|
|
95
|
+
|
|
96
|
+
If a promote-only run failed after its consumer commit was already merged and
|
|
97
|
+
its PR-stage artifacts were built, rerun the complete failed workflow after the
|
|
98
|
+
fixed Buildchain channel is published:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
gh run rerun <run-id> --repo <owner>/<consumer>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Use a complete rerun, not `--failed`, so GitHub resolves the reusable workflow
|
|
105
|
+
again. The release-candidate resolver reuses the existing candidate artifacts,
|
|
106
|
+
and the durable publication transaction makes the recovery idempotent; do not
|
|
107
|
+
open a replacement consumer PR or rebuild the native matrix solely for this
|
|
108
|
+
router failure.
|
|
109
|
+
|
|
90
110
|
By default, the wrapper forwards GitHub Release publication to the underlying
|
|
91
111
|
`promote-buildchain-ref` semver model. Once the release transaction is complete,
|
|
92
112
|
the action creates or updates the public GitHub Release, applies
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.8-alpha.0",
|
|
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",
|
|
@@ -51,6 +51,7 @@ const requiredPaths = [
|
|
|
51
51
|
"scripts/buildchain-channel-router.mjs",
|
|
52
52
|
"scripts/generate-channel-build-workflow.mjs",
|
|
53
53
|
"scripts/promotion-channel-router.mjs",
|
|
54
|
+
"scripts/promotion-identity-resolver.mjs",
|
|
54
55
|
"scripts/generate-channel-promotion-workflow.mjs",
|
|
55
56
|
"scripts/build-standalone-binary.mjs",
|
|
56
57
|
"scripts/create-release-bundle.mjs",
|
|
@@ -246,6 +247,8 @@ for (const requiredSnippet of [
|
|
|
246
247
|
".github/workflows/.build.yml:",
|
|
247
248
|
".github/workflows/.gate-profile.yml:",
|
|
248
249
|
'property "workflow_ref" is not defined in object type',
|
|
250
|
+
'property "workflow_repository" is not defined in object type',
|
|
251
|
+
'property "workflow_sha" is not defined in object type',
|
|
249
252
|
]) {
|
|
250
253
|
if (!actionlintConfig.includes(requiredSnippet)) {
|
|
251
254
|
throw new Error(`actionlint config missing scoped job.workflow_ref compatibility rule: ${requiredSnippet}`);
|
|
@@ -97,7 +97,7 @@ function forwardedInputs(inputNames, { includeInternal = true, unsupportedInputs
|
|
|
97
97
|
.filter((name) => !unsupported.has(name))
|
|
98
98
|
.map((name) => {
|
|
99
99
|
const routed = {
|
|
100
|
-
"buildchain-ref": "runtime-
|
|
100
|
+
"buildchain-ref": "runtime-sha",
|
|
101
101
|
"buildchain-contract-lock-path": "contract-lock-path",
|
|
102
102
|
channel: "publication-channel",
|
|
103
103
|
"target-ref": "target-ref",
|
|
@@ -221,7 +221,7 @@ jobs:
|
|
|
221
221
|
publication-channel: \${{ steps.route.outputs.publication-channel }}
|
|
222
222
|
target-ref: \${{ steps.route.outputs.target-ref }}
|
|
223
223
|
router-ref: \${{ steps.router.outputs.ref }}
|
|
224
|
-
router-sha: \${{ steps.
|
|
224
|
+
router-sha: \${{ steps.router.outputs.sha }}
|
|
225
225
|
shell-ref: \${{ steps.route.outputs.shell-ref }}
|
|
226
226
|
shell-sha: \${{ steps.identities.outputs.shell-sha }}
|
|
227
227
|
runtime-ref: \${{ steps.route.outputs.runtime-ref }}
|
|
@@ -237,25 +237,35 @@ jobs:
|
|
|
237
237
|
shell: bash
|
|
238
238
|
env:
|
|
239
239
|
BUILDCHAIN_ROUTER_WORKFLOW_REF: \${{ job.workflow_ref }}
|
|
240
|
+
BUILDCHAIN_ROUTER_WORKFLOW_REPOSITORY: \${{ job.workflow_repository }}
|
|
241
|
+
BUILDCHAIN_ROUTER_WORKFLOW_SHA: \${{ job.workflow_sha }}
|
|
240
242
|
run: |
|
|
241
243
|
set -euo pipefail
|
|
242
244
|
workflow_ref="\${BUILDCHAIN_ROUTER_WORKFLOW_REF}"
|
|
243
|
-
repository="\${
|
|
245
|
+
repository="\${BUILDCHAIN_ROUTER_WORKFLOW_REPOSITORY}"
|
|
244
246
|
ref="\${workflow_ref##*@}"
|
|
245
247
|
ref="\${ref#refs/heads/}"
|
|
246
248
|
ref="\${ref#refs/tags/}"
|
|
247
|
-
|
|
249
|
+
parsed_repository="\${workflow_ref%%/.github/workflows/*}"
|
|
250
|
+
if [[ -z "\${repository}" || "\${repository}" != "\${parsed_repository}" || -z "\${ref}" ]]; then
|
|
248
251
|
echo "::error::Unable to resolve promotion router source from job.workflow_ref=\${workflow_ref}"
|
|
249
252
|
exit 1
|
|
250
253
|
fi
|
|
251
|
-
|
|
252
|
-
|
|
254
|
+
if [[ ! "\${BUILDCHAIN_ROUTER_WORKFLOW_SHA}" =~ ^[0-9a-fA-F]{40}$ ]]; then
|
|
255
|
+
echo "::error::job.workflow_sha is not an exact commit SHA"
|
|
256
|
+
exit 1
|
|
257
|
+
fi
|
|
258
|
+
{
|
|
259
|
+
echo "repository=\${repository}"
|
|
260
|
+
echo "ref=\${ref}"
|
|
261
|
+
echo "sha=\${BUILDCHAIN_ROUTER_WORKFLOW_SHA,,}"
|
|
262
|
+
} >> "\${GITHUB_OUTPUT}"
|
|
253
263
|
|
|
254
264
|
- name: Checkout promotion router
|
|
255
265
|
uses: actions/checkout@v7.0.0
|
|
256
266
|
with:
|
|
257
267
|
repository: \${{ steps.router.outputs.repository }}
|
|
258
|
-
ref: \${{ steps.router.outputs.
|
|
268
|
+
ref: \${{ steps.router.outputs.sha }}
|
|
259
269
|
path: .buildchain/router
|
|
260
270
|
persist-credentials: false
|
|
261
271
|
|
|
@@ -289,11 +299,43 @@ jobs:
|
|
|
289
299
|
throw new Error(\`promotion runtime override requires write, maintain, or admin permission; actor has \${level}\`);
|
|
290
300
|
}
|
|
291
301
|
|
|
302
|
+
- name: Resolve immutable promotion identities
|
|
303
|
+
id: identities
|
|
304
|
+
shell: bash
|
|
305
|
+
env:
|
|
306
|
+
GITHUB_TOKEN: \${{ github.token }}
|
|
307
|
+
CHANNEL: \${{ steps.route.outputs.channel }}
|
|
308
|
+
SELECTED_SHELL_REF: \${{ steps.route.outputs.shell-ref }}
|
|
309
|
+
ALPHA_SHELL_REF: ${alphaRoute.logicalRef}
|
|
310
|
+
ALPHA_SHELL_CALL_REF: ${alphaRoute.callRef}
|
|
311
|
+
STABLE_SHELL_REF: ${stableRoute.logicalRef}
|
|
312
|
+
STABLE_SHELL_CALL_REF: ${stableRoute.callRef}
|
|
313
|
+
run: |
|
|
314
|
+
set -euo pipefail
|
|
315
|
+
if [[ "\${CHANNEL}" = "alpha" ]]; then
|
|
316
|
+
expected_ref="\${ALPHA_SHELL_REF}"
|
|
317
|
+
call_ref="\${ALPHA_SHELL_CALL_REF}"
|
|
318
|
+
else
|
|
319
|
+
expected_ref="\${STABLE_SHELL_REF}"
|
|
320
|
+
call_ref="\${STABLE_SHELL_CALL_REF}"
|
|
321
|
+
fi
|
|
322
|
+
if [[ "\${SELECTED_SHELL_REF}" != "\${expected_ref}" ]]; then
|
|
323
|
+
echo "::error::Selected promotion shell ref \${SELECTED_SHELL_REF} does not match configured \${expected_ref}"
|
|
324
|
+
exit 1
|
|
325
|
+
fi
|
|
326
|
+
node .buildchain/router/scripts/promotion-identity-resolver.mjs \\
|
|
327
|
+
--repository "\${{ steps.router.outputs.repository }}" \\
|
|
328
|
+
--router-ref "\${{ steps.router.outputs.ref }}" \\
|
|
329
|
+
--router-sha "\${{ steps.router.outputs.sha }}" \\
|
|
330
|
+
--shell-ref "\${SELECTED_SHELL_REF}" \\
|
|
331
|
+
--shell-call-ref "\${call_ref}" \\
|
|
332
|
+
--runtime-ref "\${{ steps.route.outputs.runtime-ref }}"
|
|
333
|
+
|
|
292
334
|
- name: Checkout selected promotion workflow shell
|
|
293
335
|
uses: actions/checkout@v7.0.0
|
|
294
336
|
with:
|
|
295
337
|
repository: \${{ steps.router.outputs.repository }}
|
|
296
|
-
ref: \${{ steps.
|
|
338
|
+
ref: \${{ steps.identities.outputs.shell-sha }}
|
|
297
339
|
path: .buildchain/shell
|
|
298
340
|
persist-credentials: false
|
|
299
341
|
|
|
@@ -301,7 +343,7 @@ jobs:
|
|
|
301
343
|
uses: actions/checkout@v7.0.0
|
|
302
344
|
with:
|
|
303
345
|
repository: \${{ steps.router.outputs.repository }}
|
|
304
|
-
ref: \${{ steps.
|
|
346
|
+
ref: \${{ steps.identities.outputs.runtime-sha }}
|
|
305
347
|
path: .buildchain/runtime
|
|
306
348
|
persist-credentials: false
|
|
307
349
|
|
|
@@ -334,44 +376,26 @@ jobs:
|
|
|
334
376
|
echo "path=\${path}" >> "\${GITHUB_OUTPUT}"
|
|
335
377
|
echo "digest=\${digest}" >> "\${GITHUB_OUTPUT}"
|
|
336
378
|
|
|
337
|
-
- name:
|
|
338
|
-
id: identities
|
|
379
|
+
- name: Verify immutable promotion checkouts
|
|
339
380
|
shell: bash
|
|
340
381
|
env:
|
|
341
382
|
CHANNEL: \${{ steps.route.outputs.channel }}
|
|
342
|
-
SELECTED_SHELL_REF: \${{ steps.route.outputs.shell-ref }}
|
|
343
|
-
ALPHA_SHELL_REF: ${alphaRoute.logicalRef}
|
|
344
|
-
ALPHA_SHELL_CALL_REF: ${alphaRoute.callRef}
|
|
345
383
|
ALPHA_SHELL_WORKFLOW_PATH: ${alphaRoute.workflowPath}
|
|
346
|
-
STABLE_SHELL_REF: ${stableRoute.logicalRef}
|
|
347
|
-
STABLE_SHELL_CALL_REF: ${stableRoute.callRef}
|
|
348
384
|
STABLE_SHELL_WORKFLOW_PATH: ${stableRoute.workflowPath}
|
|
385
|
+
ROUTER_SHA: \${{ steps.router.outputs.sha }}
|
|
386
|
+
SHELL_SHA: \${{ steps.identities.outputs.shell-sha }}
|
|
387
|
+
RUNTIME_SHA: \${{ steps.identities.outputs.runtime-sha }}
|
|
349
388
|
run: |
|
|
350
389
|
set -euo pipefail
|
|
351
390
|
if [[ "\${CHANNEL}" = "alpha" ]]; then
|
|
352
|
-
expected_ref="\${ALPHA_SHELL_REF}"
|
|
353
|
-
call_ref="\${ALPHA_SHELL_CALL_REF}"
|
|
354
391
|
workflow_path="\${ALPHA_SHELL_WORKFLOW_PATH}"
|
|
355
392
|
else
|
|
356
|
-
expected_ref="\${STABLE_SHELL_REF}"
|
|
357
|
-
call_ref="\${STABLE_SHELL_CALL_REF}"
|
|
358
393
|
workflow_path="\${STABLE_SHELL_WORKFLOW_PATH}"
|
|
359
394
|
fi
|
|
360
|
-
if [[ "\${SELECTED_SHELL_REF}" != "\${expected_ref}" ]]; then
|
|
361
|
-
echo "::error::Selected promotion shell ref \${SELECTED_SHELL_REF} does not match configured \${expected_ref}"
|
|
362
|
-
exit 1
|
|
363
|
-
fi
|
|
364
395
|
test -f ".buildchain/shell/\${workflow_path}"
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
exit 1
|
|
369
|
-
fi
|
|
370
|
-
{
|
|
371
|
-
echo "router-sha=$(git -C .buildchain/router rev-parse HEAD)"
|
|
372
|
-
echo "shell-sha=\${shell_sha}"
|
|
373
|
-
echo "runtime-sha=$(git -C .buildchain/runtime rev-parse HEAD)"
|
|
374
|
-
} >> "\${GITHUB_OUTPUT}"
|
|
396
|
+
[[ "$(git -C .buildchain/router rev-parse HEAD)" = "\${ROUTER_SHA}" ]] || { echo "::error::Promotion router checkout moved"; exit 1; }
|
|
397
|
+
[[ "$(git -C .buildchain/shell rev-parse HEAD)" = "\${SHELL_SHA}" ]] || { echo "::error::Promotion shell checkout moved"; exit 1; }
|
|
398
|
+
[[ "$(git -C .buildchain/runtime rev-parse HEAD)" = "\${RUNTIME_SHA}" ]] || { echo "::error::Promotion runtime checkout moved"; exit 1; }
|
|
375
399
|
|
|
376
400
|
alpha:
|
|
377
401
|
name: Promote with alpha workflow shell
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const SHA_PATTERN = /^[0-9a-f]{40}$/i;
|
|
8
|
+
|
|
9
|
+
function normalized(value) {
|
|
10
|
+
return String(value ?? "").trim();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function requireSha(value, label) {
|
|
14
|
+
const sha = normalized(value).toLowerCase();
|
|
15
|
+
if (!SHA_PATTERN.test(sha)) throw new Error(`${label} must be an exact 40-character SHA`);
|
|
16
|
+
return sha;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function resolvePromotionIdentities({
|
|
20
|
+
routerRef,
|
|
21
|
+
routerSha,
|
|
22
|
+
shellRef,
|
|
23
|
+
shellCallRef,
|
|
24
|
+
runtimeRef,
|
|
25
|
+
resolveRef,
|
|
26
|
+
} = {}) {
|
|
27
|
+
const requested = {
|
|
28
|
+
router: normalized(routerRef),
|
|
29
|
+
shell: normalized(shellRef),
|
|
30
|
+
shellCall: normalized(shellCallRef),
|
|
31
|
+
runtime: normalized(runtimeRef),
|
|
32
|
+
};
|
|
33
|
+
if (Object.values(requested).some((value) => !value)) {
|
|
34
|
+
throw new Error("router, shell, shell call, and runtime refs are required");
|
|
35
|
+
}
|
|
36
|
+
if (typeof resolveRef !== "function") throw new Error("resolveRef must be a function");
|
|
37
|
+
|
|
38
|
+
const immutableRouterSha = requireSha(routerSha, "router SHA");
|
|
39
|
+
const resolved = new Map([[requested.router, immutableRouterSha]]);
|
|
40
|
+
const resolveOnce = async (ref) => {
|
|
41
|
+
if (SHA_PATTERN.test(ref)) return ref.toLowerCase();
|
|
42
|
+
if (!resolved.has(ref)) {
|
|
43
|
+
resolved.set(ref, requireSha(await resolveRef(ref), `resolved SHA for ${ref}`));
|
|
44
|
+
}
|
|
45
|
+
return resolved.get(ref);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
routerRef: requested.router,
|
|
50
|
+
routerSha: immutableRouterSha,
|
|
51
|
+
shellRef: requested.shell,
|
|
52
|
+
shellSha: await resolveOnce(requested.shellCall),
|
|
53
|
+
runtimeRef: requested.runtime,
|
|
54
|
+
runtimeSha: await resolveOnce(requested.runtime),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function parseArgs(argv) {
|
|
59
|
+
const values = {};
|
|
60
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
61
|
+
const token = argv[index];
|
|
62
|
+
if (!token.startsWith("--")) throw new Error(`unexpected argument: ${token}`);
|
|
63
|
+
const value = argv[index + 1];
|
|
64
|
+
if (value === undefined || value.startsWith("--")) throw new Error(`missing value for ${token}`);
|
|
65
|
+
values[token.slice(2)] = value;
|
|
66
|
+
index += 1;
|
|
67
|
+
}
|
|
68
|
+
return values;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function repositoryParts(value) {
|
|
72
|
+
const match = normalized(value).match(/^([^/]+)\/([^/]+)$/);
|
|
73
|
+
if (!match) throw new Error("repository must use owner/name form");
|
|
74
|
+
return { owner: match[1], repo: match[2] };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function githubCommitResolver({ repository, token }) {
|
|
78
|
+
const { owner, repo } = repositoryParts(repository);
|
|
79
|
+
return async (ref) => {
|
|
80
|
+
const response = await fetch(
|
|
81
|
+
`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/commits/${encodeURIComponent(ref)}`,
|
|
82
|
+
{
|
|
83
|
+
headers: {
|
|
84
|
+
Accept: "application/vnd.github+json",
|
|
85
|
+
Authorization: `Bearer ${token}`,
|
|
86
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
87
|
+
"User-Agent": "kungfu-buildchain-promotion-router",
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
if (!response.ok) throw new Error(`unable to resolve ${ref}: GitHub API returned ${response.status}`);
|
|
92
|
+
return (await response.json()).sha;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function writeOutputs(file, identities) {
|
|
97
|
+
const outputs = {
|
|
98
|
+
"router-sha": identities.routerSha,
|
|
99
|
+
"shell-sha": identities.shellSha,
|
|
100
|
+
"runtime-sha": identities.runtimeSha,
|
|
101
|
+
};
|
|
102
|
+
fs.appendFileSync(file, Object.entries(outputs).map(([key, value]) => `${key}=${value}\n`).join(""));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function main() {
|
|
106
|
+
const args = parseArgs(process.argv.slice(2));
|
|
107
|
+
const token = normalized(process.env.GITHUB_TOKEN);
|
|
108
|
+
if (!token) throw new Error("GITHUB_TOKEN is required to resolve floating promotion refs");
|
|
109
|
+
const identities = await resolvePromotionIdentities({
|
|
110
|
+
routerRef: args["router-ref"],
|
|
111
|
+
routerSha: args["router-sha"],
|
|
112
|
+
shellRef: args["shell-ref"],
|
|
113
|
+
shellCallRef: args["shell-call-ref"],
|
|
114
|
+
runtimeRef: args["runtime-ref"],
|
|
115
|
+
resolveRef: await githubCommitResolver({ repository: args.repository, token }),
|
|
116
|
+
});
|
|
117
|
+
if (process.env.GITHUB_OUTPUT) writeOutputs(process.env.GITHUB_OUTPUT, identities);
|
|
118
|
+
process.stdout.write(`${JSON.stringify(identities, null, 2)}\n`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
122
|
+
if (isMain) {
|
|
123
|
+
main().catch((error) => {
|
|
124
|
+
console.error(`promotion-identity-resolver: ${error.message}`);
|
|
125
|
+
process.exitCode = 1;
|
|
126
|
+
});
|
|
127
|
+
}
|