@kungfu-tech/buildchain 2.12.1 → 2.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -5
- package/dist/site/buildchain-contract.json +6 -6
- package/dist/site/buildchain-site.json +73 -21
- package/dist/site/capability-registry.json +2 -2
- package/dist/site/kfd-claims.json +59 -10
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +3 -3
- package/dist/site/node-api-registry.json +1 -1
- package/dist/site/page-registry.json +62 -11
- package/dist/site/public-surface-audit.json +40 -7
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +7 -7
- package/dist/site/workflow-registry.json +35 -3
- package/docs/MAP.md +2 -1
- package/docs/lifecycle-protocol.md +16 -0
- package/docs/release-governance.md +7 -0
- package/docs/shifu-gate-profiles.md +163 -0
- package/docs/stable-candidate-patrol.md +23 -3
- package/package.json +1 -1
- package/packages/core/release-candidate.js +42 -0
- package/scripts/build-contract-core.mjs +21 -4
- package/scripts/check-inventory.mjs +4 -0
- package/scripts/gate-profile-core.mjs +413 -0
- package/scripts/generate-release-candidate-passport.mjs +4 -0
- package/scripts/shifu-gate-profile.mjs +351 -0
- package/scripts/stable-candidate-patrol.mjs +4 -0
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
"status": "passed",
|
|
5
5
|
"summary": {
|
|
6
6
|
"cliCommandCount": 76,
|
|
7
|
-
"workflowCount":
|
|
7
|
+
"workflowCount": 43,
|
|
8
8
|
"actionCount": 4,
|
|
9
|
-
"sitePageCount":
|
|
9
|
+
"sitePageCount": 47,
|
|
10
10
|
"docCommandRefCount": 241,
|
|
11
11
|
"failureCount": 0
|
|
12
12
|
},
|
|
@@ -425,6 +425,7 @@
|
|
|
425
425
|
"checkout-cache-reference-repository-template",
|
|
426
426
|
"checkout-cache-timeout-seconds",
|
|
427
427
|
"expected-artifacts-json",
|
|
428
|
+
"gate-profile-aggregate-json",
|
|
428
429
|
"install-command",
|
|
429
430
|
"linux-container-image",
|
|
430
431
|
"linux-container-preset",
|
|
@@ -456,7 +457,7 @@
|
|
|
456
457
|
"verify-command",
|
|
457
458
|
"working-directory"
|
|
458
459
|
],
|
|
459
|
-
"inputCount":
|
|
460
|
+
"inputCount": 58
|
|
460
461
|
},
|
|
461
462
|
{
|
|
462
463
|
"id": ".bump-minor-version",
|
|
@@ -464,6 +465,32 @@
|
|
|
464
465
|
"inputs": [],
|
|
465
466
|
"inputCount": 0
|
|
466
467
|
},
|
|
468
|
+
{
|
|
469
|
+
"id": ".gate-profile",
|
|
470
|
+
"path": ".github/workflows/.gate-profile.yml",
|
|
471
|
+
"inputs": [
|
|
472
|
+
"artifact-name",
|
|
473
|
+
"artifact-retention-days",
|
|
474
|
+
"buildchain-ref",
|
|
475
|
+
"buildchain-repository",
|
|
476
|
+
"gate-command-json",
|
|
477
|
+
"gate-environment-json",
|
|
478
|
+
"gate-plan-command-json",
|
|
479
|
+
"gate-profile",
|
|
480
|
+
"gate-registry",
|
|
481
|
+
"include-advisory",
|
|
482
|
+
"node-version",
|
|
483
|
+
"platforms-json",
|
|
484
|
+
"require-trusted-event",
|
|
485
|
+
"runner-preset",
|
|
486
|
+
"shifu-cache-profile-digest",
|
|
487
|
+
"shifu-cache-profile-ref",
|
|
488
|
+
"source-ref",
|
|
489
|
+
"untrusted-policy",
|
|
490
|
+
"working-directory"
|
|
491
|
+
],
|
|
492
|
+
"inputCount": 19
|
|
493
|
+
},
|
|
467
494
|
{
|
|
468
495
|
"id": ".release-docker",
|
|
469
496
|
"path": ".github/workflows/.release-docker.yml",
|
|
@@ -730,6 +757,7 @@
|
|
|
730
757
|
"checkout-cache-reference-repository-template",
|
|
731
758
|
"checkout-cache-timeout-seconds",
|
|
732
759
|
"expected-artifacts-json",
|
|
760
|
+
"gate-profile-aggregate-json",
|
|
733
761
|
"install-command",
|
|
734
762
|
"linux-container-image",
|
|
735
763
|
"linux-container-preset",
|
|
@@ -761,7 +789,7 @@
|
|
|
761
789
|
"verify-command",
|
|
762
790
|
"working-directory"
|
|
763
791
|
],
|
|
764
|
-
"inputCount":
|
|
792
|
+
"inputCount": 61
|
|
765
793
|
},
|
|
766
794
|
{
|
|
767
795
|
"id": "buildchain-alpha-self-dogfood",
|
|
@@ -864,12 +892,13 @@
|
|
|
864
892
|
"inputs": [
|
|
865
893
|
"buildchain-ref",
|
|
866
894
|
"buildchain-repository",
|
|
895
|
+
"mode",
|
|
867
896
|
"node-version",
|
|
868
897
|
"require-version-state",
|
|
869
898
|
"upload-artifacts",
|
|
870
899
|
"working-directory"
|
|
871
900
|
],
|
|
872
|
-
"inputCount":
|
|
901
|
+
"inputCount": 7
|
|
873
902
|
},
|
|
874
903
|
{
|
|
875
904
|
"id": "dev-pr-auto-merge",
|
|
@@ -1452,6 +1481,10 @@
|
|
|
1452
1481
|
"id": "manual:runtime-train-validation",
|
|
1453
1482
|
"category": "manual"
|
|
1454
1483
|
},
|
|
1484
|
+
{
|
|
1485
|
+
"id": "manual:shifu-gate-profiles",
|
|
1486
|
+
"category": "manual"
|
|
1487
|
+
},
|
|
1455
1488
|
{
|
|
1456
1489
|
"id": "manual:site-bundle-contract",
|
|
1457
1490
|
"category": "manual"
|
|
@@ -2690,8 +2723,8 @@
|
|
|
2690
2723
|
"workflowRegistryPath": "dist/site/workflow-registry.json",
|
|
2691
2724
|
"pageRegistryPath": "dist/site/page-registry.json",
|
|
2692
2725
|
"cliRegistryDigest": "ab57fec294efb9dde1612787bfd0d532b5bb95298b5da14a2b51a5e71b9c7dcc",
|
|
2693
|
-
"workflowRegistryDigest": "
|
|
2694
|
-
"pageRegistryDigest": "
|
|
2726
|
+
"workflowRegistryDigest": "ccc8ab882a30d8c180c109aa484c619d34a5e889c571757f11e555f113c4d338",
|
|
2727
|
+
"pageRegistryDigest": "394ef34331be95e72b6c2c875ce138ea0b3344e539793291dc8126d168a2c1f9"
|
|
2695
2728
|
},
|
|
2696
2729
|
"comparison": {
|
|
2697
2730
|
"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-13T12:43:26.201Z",
|
|
5
|
+
"publishedAt": "2026-07-13T12:43:26.201Z",
|
|
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": "d61a8cfd6d147aadc73306dbc97bf2ce6b825895",
|
|
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.12.
|
|
35
|
+
"version": "2.12.2",
|
|
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-13T12:43:26.201Z",
|
|
5
|
+
"publishedAt": "2026-07-13T12:43:26.201Z",
|
|
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": "d61a8cfd6d147aadc73306dbc97bf2ce6b825895",
|
|
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.12.
|
|
40
|
+
"version": "2.12.2",
|
|
41
41
|
"versionSource": "package.json#version"
|
|
42
42
|
},
|
|
43
43
|
"entrypoint": "buildchain-site.json",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"path": "docs/MAP.md",
|
|
54
54
|
"plane": "use",
|
|
55
55
|
"exists": true,
|
|
56
|
-
"digest": "sha256:
|
|
56
|
+
"digest": "sha256:5df6d6110d7c33da4625a2e1ebb6b778eef9c4420379cff342229bc0b02962e1"
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
59
|
"id": "install",
|
|
@@ -197,7 +197,7 @@
|
|
|
197
197
|
"path": "docs/lifecycle-protocol.md",
|
|
198
198
|
"plane": "use",
|
|
199
199
|
"exists": true,
|
|
200
|
-
"digest": "sha256:
|
|
200
|
+
"digest": "sha256:3a03359ec58db04f334303256b02020f6039616e8802c19dfc00e76bbb05bf18"
|
|
201
201
|
},
|
|
202
202
|
{
|
|
203
203
|
"id": "reusable-build-surface",
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
"path": "docs/release-governance.md",
|
|
222
222
|
"plane": "why",
|
|
223
223
|
"exists": true,
|
|
224
|
-
"digest": "sha256:
|
|
224
|
+
"digest": "sha256:3ae2bbc2df394726f350e58be1a71dd0379b99b19cd6ccf15fbe3b843f3890b6"
|
|
225
225
|
},
|
|
226
226
|
{
|
|
227
227
|
"id": "release-flow",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"checkout-cache-reference-repository-template",
|
|
35
35
|
"checkout-cache-timeout-seconds",
|
|
36
36
|
"expected-artifacts-json",
|
|
37
|
+
"gate-profile-aggregate-json",
|
|
37
38
|
"install-command",
|
|
38
39
|
"linux-container-image",
|
|
39
40
|
"linux-container-preset",
|
|
@@ -65,7 +66,7 @@
|
|
|
65
66
|
"verify-command",
|
|
66
67
|
"working-directory"
|
|
67
68
|
],
|
|
68
|
-
"inputCount":
|
|
69
|
+
"inputCount": 58,
|
|
69
70
|
"surface": "reusable-build",
|
|
70
71
|
"capabilityGroup": "api-cli-reference",
|
|
71
72
|
"status": "active"
|
|
@@ -79,6 +80,35 @@
|
|
|
79
80
|
"capabilityGroup": "api-cli-reference",
|
|
80
81
|
"status": "active"
|
|
81
82
|
},
|
|
83
|
+
{
|
|
84
|
+
"id": ".gate-profile",
|
|
85
|
+
"path": ".github/workflows/.gate-profile.yml",
|
|
86
|
+
"inputs": [
|
|
87
|
+
"artifact-name",
|
|
88
|
+
"artifact-retention-days",
|
|
89
|
+
"buildchain-ref",
|
|
90
|
+
"buildchain-repository",
|
|
91
|
+
"gate-command-json",
|
|
92
|
+
"gate-environment-json",
|
|
93
|
+
"gate-plan-command-json",
|
|
94
|
+
"gate-profile",
|
|
95
|
+
"gate-registry",
|
|
96
|
+
"include-advisory",
|
|
97
|
+
"node-version",
|
|
98
|
+
"platforms-json",
|
|
99
|
+
"require-trusted-event",
|
|
100
|
+
"runner-preset",
|
|
101
|
+
"shifu-cache-profile-digest",
|
|
102
|
+
"shifu-cache-profile-ref",
|
|
103
|
+
"source-ref",
|
|
104
|
+
"untrusted-policy",
|
|
105
|
+
"working-directory"
|
|
106
|
+
],
|
|
107
|
+
"inputCount": 19,
|
|
108
|
+
"surface": "reusable-workflow",
|
|
109
|
+
"capabilityGroup": "api-cli-reference",
|
|
110
|
+
"status": "active"
|
|
111
|
+
},
|
|
82
112
|
{
|
|
83
113
|
"id": ".release-docker",
|
|
84
114
|
"path": ".github/workflows/.release-docker.yml",
|
|
@@ -384,6 +414,7 @@
|
|
|
384
414
|
"checkout-cache-reference-repository-template",
|
|
385
415
|
"checkout-cache-timeout-seconds",
|
|
386
416
|
"expected-artifacts-json",
|
|
417
|
+
"gate-profile-aggregate-json",
|
|
387
418
|
"install-command",
|
|
388
419
|
"linux-container-image",
|
|
389
420
|
"linux-container-preset",
|
|
@@ -415,7 +446,7 @@
|
|
|
415
446
|
"verify-command",
|
|
416
447
|
"working-directory"
|
|
417
448
|
],
|
|
418
|
-
"inputCount":
|
|
449
|
+
"inputCount": 61,
|
|
419
450
|
"surface": "channel-build-router",
|
|
420
451
|
"capabilityGroup": "reusable-build",
|
|
421
452
|
"status": "active"
|
|
@@ -548,12 +579,13 @@
|
|
|
548
579
|
"inputs": [
|
|
549
580
|
"buildchain-ref",
|
|
550
581
|
"buildchain-repository",
|
|
582
|
+
"mode",
|
|
551
583
|
"node-version",
|
|
552
584
|
"require-version-state",
|
|
553
585
|
"upload-artifacts",
|
|
554
586
|
"working-directory"
|
|
555
587
|
],
|
|
556
|
-
"inputCount":
|
|
588
|
+
"inputCount": 7,
|
|
557
589
|
"surface": "repository-workflow",
|
|
558
590
|
"capabilityGroup": "api-cli-reference",
|
|
559
591
|
"status": "active"
|
package/docs/MAP.md
CHANGED
|
@@ -28,7 +28,7 @@ workflow, action, and Node API export also carries a `capabilityGroup`,
|
|
|
28
28
|
| --- | --- | --- |
|
|
29
29
|
| Getting Started | `capability-registry.json`, `product-mechanism.json` | [`install.md`](install.md), [`product-mechanism.md`](product-mechanism.md), [`cli.md`](cli.md) |
|
|
30
30
|
| Release Passport and Trust | `release-model.json`, `artifact-schemas.json`, `kfd-claims.json` | [`release-passport.md`](release-passport.md), [`release-candidate.md`](release-candidate.md), [`publish-transaction.md`](publish-transaction.md), [`binary-distribution.md`](binary-distribution.md) |
|
|
31
|
-
| Reusable Build and Lifecycle | `workflow-registry.json`, `release-model.json` | [`reusable-build-surface.md`](reusable-build-surface.md), [`lifecycle-protocol.md`](lifecycle-protocol.md) |
|
|
31
|
+
| Reusable Build and Lifecycle | `workflow-registry.json`, `release-model.json` | [`reusable-build-surface.md`](reusable-build-surface.md), [`shifu-gate-profiles.md`](shifu-gate-profiles.md), [`lifecycle-protocol.md`](lifecycle-protocol.md) |
|
|
32
32
|
| KFD Trust and Surface Closure | `kfd-claims.json`, `public-surface-audit.json`, `cli-registry.json`, `node-api-registry.json` | [`kfd-support.md`](kfd-support.md), [`release-passport.md`](release-passport.md) |
|
|
33
33
|
| Site Bundle, Web Surfaces, and Propagation | `buildchain-site.json`, `site-manifest.json`, `page-registry.json`, `release-model.json` | [`site-bundle-contract.md`](site-bundle-contract.md), [`web-surface-deployments.md`](web-surface-deployments.md), [`release-propagation.md`](release-propagation.md) |
|
|
34
34
|
| Publication Artifacts | `publication-registry.json`, `workflow-registry.json`, `node-api-registry.json`, `manual-registry.json`, `kungfu-buildchain-publication-artifact-registry` | [`publication-artifacts.md`](publication-artifacts.md), [`reusable-build-surface.md`](reusable-build-surface.md) |
|
|
@@ -95,6 +95,7 @@ replace them.
|
|
|
95
95
|
| How do I add timestamped logs inside build scripts? | [`toolkit-observability.md`](toolkit-observability.md) | use | stable |
|
|
96
96
|
| What package-owned facts should buildchain.libkungfu.dev render? | [`site-bundle-contract.md`](site-bundle-contract.md) | use | stable |
|
|
97
97
|
| How do I call the reusable build workflow? | [`reusable-build-surface.md`](reusable-build-surface.md) | use | stable |
|
|
98
|
+
| How does Buildchain schedule and aggregate a project-owned Shifu Gate profile? | [`shifu-gate-profiles.md`](shifu-gate-profiles.md) | use/verify | draft |
|
|
98
99
|
| How do I use one build job that follows alpha during development and stable for releases? | [`reusable-build-surface.md`](reusable-build-surface.md#automatic-channel-router) | use | preview |
|
|
99
100
|
| How do self-hosted runners relay large artifacts through S3 before GitHub artifacts? | [`reusable-build-surface.md`](reusable-build-surface.md#artifact-transfer-relay) | use | stable |
|
|
100
101
|
| How do self-hosted runners reuse local Git checkout caches without weakening source locks? | [`reusable-build-surface.md`](reusable-build-surface.md#locked-source-checkout-cache) | use | stable |
|
|
@@ -158,6 +158,9 @@ commands = [
|
|
|
158
158
|
"pnpm run package",
|
|
159
159
|
]
|
|
160
160
|
|
|
161
|
+
[lifecycle.check]
|
|
162
|
+
command = "pnpm run check:source"
|
|
163
|
+
|
|
161
164
|
[lifecycle.verify]
|
|
162
165
|
shell = "bash"
|
|
163
166
|
script = """
|
|
@@ -167,6 +170,19 @@ git diff --check
|
|
|
167
170
|
"""
|
|
168
171
|
```
|
|
169
172
|
|
|
173
|
+
`lifecycle.check` is the repository-owned source-acceptance gate. It should
|
|
174
|
+
validate the checked-out source revision without entering the build, artifact,
|
|
175
|
+
or release lifecycle. Consumers can run it on GitHub-hosted Linux through
|
|
176
|
+
`.github/workflows/check.yml@v2` with `mode: source`; the reusable workflow runs
|
|
177
|
+
only `lifecycle.install` and `lifecycle.check`. The default `mode: verify`
|
|
178
|
+
continues to run `lifecycle.install` and `lifecycle.verify` for existing callers.
|
|
179
|
+
Both executed stages receive `BUILDCHAIN_CHECK_MODE=source` or
|
|
180
|
+
`BUILDCHAIN_CHECK_MODE=verify`, so a repository whose normal install path can
|
|
181
|
+
compile native tooling can select a provisioning-only install path for source
|
|
182
|
+
acceptance without weakening promotion installs.
|
|
183
|
+
The reusable job name remains `check`, and `upload-artifacts: false` disables
|
|
184
|
+
evidence upload without changing the job conclusion used by branch protection.
|
|
185
|
+
|
|
170
186
|
Shared environment variables can be declared once:
|
|
171
187
|
|
|
172
188
|
```toml
|
|
@@ -412,6 +412,13 @@ Consumers that want Buildchain to own the check wrapper can call
|
|
|
412
412
|
`lifecycle.install` and `lifecycle.verify` stages and fails the `check` job when
|
|
413
413
|
either declaration is missing or the command exits non-zero.
|
|
414
414
|
|
|
415
|
+
Development pull requests that need source acceptance without product build or
|
|
416
|
+
artifact verification can opt into `mode: source`. That mode runs only
|
|
417
|
+
`lifecycle.install` and `lifecycle.check` on GitHub-hosted `ubuntu-24.04` while
|
|
418
|
+
preserving the stable `check / check` required-check context. Existing callers
|
|
419
|
+
remain on `mode: verify` by default, and callers may set
|
|
420
|
+
`upload-artifacts: false` without weakening the job conclusion.
|
|
421
|
+
|
|
415
422
|
Typical consumer wrapper:
|
|
416
423
|
|
|
417
424
|
```yaml
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
status: draft
|
|
3
|
+
period: ongoing
|
|
4
|
+
theme: buildchain-shifu-gate-orchestration
|
|
5
|
+
doc_type: technical-manual
|
|
6
|
+
source_level: local-files
|
|
7
|
+
confidence: high
|
|
8
|
+
sensitivity: public
|
|
9
|
+
evidence_grade: B
|
|
10
|
+
review_state: self-reviewed
|
|
11
|
+
last_reviewed: 2026-07-13
|
|
12
|
+
ai_provenance:
|
|
13
|
+
model_family: GPT-5
|
|
14
|
+
product: Codex
|
|
15
|
+
generated_at: 2026-07-13
|
|
16
|
+
invisible_context_boundary: No private runner configuration, credentials, or unpublished Shifu implementation state was used.
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Shifu Gate profile orchestration
|
|
20
|
+
|
|
21
|
+
Buildchain can schedule and aggregate a project-owned Shifu Gate profile without
|
|
22
|
+
owning that project's gate ids, commands, dependencies, or dev/alpha/release
|
|
23
|
+
policy. The reusable workflow is
|
|
24
|
+
`.github/workflows/.gate-profile.yml`.
|
|
25
|
+
|
|
26
|
+
## Ownership boundary
|
|
27
|
+
|
|
28
|
+
| Concern | Owner | Enforced surface |
|
|
29
|
+
| ----------------------------------------------------------------------------------- | ------------------------------------- | ---------------------------------------------------------------------------- |
|
|
30
|
+
| Gate schema, profile planning, execution, receipt qualification | Shifu | `shifu gate plan`, `shifu gate run --profile`, `shifu gate receipt validate` |
|
|
31
|
+
| Concrete gate catalog and profile decisions | Consumer project | project Gate registry and detailed Gate docs |
|
|
32
|
+
| Runner labels and declared capabilities | Consumer workflow / Buildchain preset | `runner-preset` or `platforms-json` |
|
|
33
|
+
| Deterministic runner matrix, immutable checkout, receipt transport, aggregate check | Buildchain | `.gate-profile.yml` and `shifu-gate-profile.mjs` |
|
|
34
|
+
| Whether a profile aggregate is required for dev, alpha, or release | Consumer project | protected-branch required-check policy and caller workflow |
|
|
35
|
+
|
|
36
|
+
Buildchain treats the Shifu plan and receipt as versioned input contracts. It
|
|
37
|
+
does not reimplement policy selection, execute raw shell strings, convert an
|
|
38
|
+
explicit diagnostic gate run into qualification, or mint missing evidence.
|
|
39
|
+
|
|
40
|
+
## Runner matrix
|
|
41
|
+
|
|
42
|
+
The plan job asks the consumer's Shifu entrypoint for one plan per configured
|
|
43
|
+
platform. A platform is dispatchable only when:
|
|
44
|
+
|
|
45
|
+
- the Shifu plan is qualifying;
|
|
46
|
+
- every required selection is supported on that platform;
|
|
47
|
+
- the runner declares every capability requested by the selected gates; and
|
|
48
|
+
- all platform plans carry the same project id and registry digest.
|
|
49
|
+
|
|
50
|
+
Configured platforms are required by default. A required platform that cannot
|
|
51
|
+
host the profile fails before runner dispatch. A platform with
|
|
52
|
+
`"required": false` may be omitted, but the omission and reasons remain in the
|
|
53
|
+
matrix and aggregate. Matrix entries retain the Shifu plan digest, ordered gate
|
|
54
|
+
groups, required/advisory modes, action ids, definition digests, skips, and
|
|
55
|
+
unsupported selections.
|
|
56
|
+
|
|
57
|
+
`github-hosted` declares only the inherent `node` capability. Projects that
|
|
58
|
+
need a native compiler, product artifacts, devices, or other facilities must
|
|
59
|
+
use a suitable preset or declare a custom matrix. Capabilities are scheduling
|
|
60
|
+
claims, not installation instructions.
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
[
|
|
64
|
+
{
|
|
65
|
+
"id": "linux-native",
|
|
66
|
+
"name": "Linux native",
|
|
67
|
+
"platform": "linux",
|
|
68
|
+
"runner": "[\"self-hosted\",\"Linux\",\"X64\",\"product-build\"]",
|
|
69
|
+
"capabilities": ["node", "native-toolchain", "product-artifacts"]
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Execution and receipts
|
|
75
|
+
|
|
76
|
+
Every matrix job checks out the exact source SHA planned by Buildchain, invokes
|
|
77
|
+
`shifu gate run --profile`, writes the receipt outside the source checkout, and
|
|
78
|
+
then invokes `shifu gate receipt validate`. Buildchain uploads the original
|
|
79
|
+
receipt and validation result even when the run fails.
|
|
80
|
+
|
|
81
|
+
The fixed `Gate profile / aggregate` job fails closed for missing receipts,
|
|
82
|
+
invalid or stale Shifu validation, dirty or mismatched source SHA, registry or
|
|
83
|
+
plan drift, missing required results, required failures/skips, or gate action
|
|
84
|
+
and definition digest drift. Advisory failures remain visible but do not turn a
|
|
85
|
+
Shifu-qualifying receipt into a required failure. Buildchain's aggregate is
|
|
86
|
+
`buildchain.shifu-gate-aggregate/v1`; its digest covers the matrix, receipts,
|
|
87
|
+
per-gate evidence pointers, omissions, and issues.
|
|
88
|
+
|
|
89
|
+
## Consumer workflow
|
|
90
|
+
|
|
91
|
+
```yaml
|
|
92
|
+
jobs:
|
|
93
|
+
gates:
|
|
94
|
+
uses: kungfu-systems/buildchain/.github/workflows/.gate-profile.yml@v2
|
|
95
|
+
with:
|
|
96
|
+
gate-profile: alpha-pr
|
|
97
|
+
runner-preset: kungfu-v4-self-hosted
|
|
98
|
+
include-advisory: true
|
|
99
|
+
|
|
100
|
+
build:
|
|
101
|
+
needs: gates
|
|
102
|
+
uses: kungfu-systems/buildchain/.github/workflows/build.yml@v2
|
|
103
|
+
with:
|
|
104
|
+
release-candidate: true
|
|
105
|
+
gate-profile-aggregate-json: ${{ needs.gates.outputs.gate-aggregate-json }}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The command input is an argv map, not a shell string. The default supports the
|
|
109
|
+
ordinary Shifu launcher names on all three platforms. A project with a
|
|
110
|
+
different launcher can override it without teaching Buildchain project tasks:
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
gate-command-json: >-
|
|
114
|
+
{"linux":["./tools/shifu"],"macos":["./tools/shifu"],"windows":["./tools/shifu.cmd"]}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`gate-command-json` is the execution command. If execution needs a cache,
|
|
118
|
+
container, or other project-owned wrapper that should not make the read-only
|
|
119
|
+
plan depend on that service, pass a separate lightweight argv map through
|
|
120
|
+
`gate-plan-command-json`. It defaults to the execution command for backward
|
|
121
|
+
compatibility; Buildchain still treats both inputs as argv and never evaluates
|
|
122
|
+
a shell string.
|
|
123
|
+
|
|
124
|
+
Projects may also pass non-sensitive scalar environment through
|
|
125
|
+
`gate-environment-json`; Buildchain validates the JSON shape and forwards it
|
|
126
|
+
without interpreting names or values. Cache profile references use the same
|
|
127
|
+
opaque `shifu-cache-profile-ref` and `shifu-cache-profile-digest` inputs as the
|
|
128
|
+
reusable build. Do not place tokens, credentials, or other secrets in workflow
|
|
129
|
+
inputs or Gate receipts.
|
|
130
|
+
|
|
131
|
+
When a qualifying aggregate is passed to the build workflow, the
|
|
132
|
+
release-candidate passport binds its profile, source SHA, registry digest,
|
|
133
|
+
matrix digest, aggregate digest, receipt count, and result count. A failed,
|
|
134
|
+
non-qualifying, or source-mismatched aggregate cannot produce a valid passport.
|
|
135
|
+
Promote-only release validation preserves that same Gate evidence summary in
|
|
136
|
+
the final Release Passport release identity, so promotion cannot silently drop
|
|
137
|
+
the qualified profile provenance.
|
|
138
|
+
|
|
139
|
+
## Failure diagnosis and rollback
|
|
140
|
+
|
|
141
|
+
Start with the aggregate artifact, then the platform receipt named in its
|
|
142
|
+
issues. Reproduce the exact project decision with Shifu, for example:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
./shifu gate explain <gate-id> --profile <profile>
|
|
146
|
+
./shifu gate plan <profile> --platform <platform> --json
|
|
147
|
+
./shifu gate receipt validate <receipt.json> --json
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The existing reusable build workflow remains usable without Gate inputs. To
|
|
151
|
+
roll back Gate orchestration, remove the caller's `gates` job and
|
|
152
|
+
`gate-profile-aggregate-json` handoff; this does not alter the consumer's Shifu
|
|
153
|
+
registry or its direct diagnostic commands.
|
|
154
|
+
|
|
155
|
+
## Validation boundary
|
|
156
|
+
|
|
157
|
+
Unit fixtures prove deterministic matrix generation and required/advisory,
|
|
158
|
+
capability, unsupported, missing, stale, failure, and definition-drift
|
|
159
|
+
propagation. Because a train ref changes runtime scripts but not the outer
|
|
160
|
+
reusable workflow topology, an unreleased `.gate-profile.yml` must also be
|
|
161
|
+
validated through a trusted `workflow_dispatch` canary that references the
|
|
162
|
+
temporary workflow ref or exact SHA. See
|
|
163
|
+
[`runtime-train-validation.md`](runtime-train-validation.md).
|
|
@@ -122,15 +122,35 @@ jobs:
|
|
|
122
122
|
dry-run: false
|
|
123
123
|
secrets:
|
|
124
124
|
promotion-token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}
|
|
125
|
+
approval-token: ${{ secrets.BUILDCHAIN_APPROVAL_TOKEN }}
|
|
125
126
|
```
|
|
126
127
|
|
|
127
128
|
The promotion token must be repository-owned and capable of creating the
|
|
128
129
|
machine ledger branch, exact source-lock branch, and pull request. Repositories
|
|
129
|
-
that require an approving review can
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
that require an approving review can pass an independent repository-owned App,
|
|
131
|
+
bot, or human service-account token as `approval-token`. The approval identity
|
|
132
|
+
must differ from the promotion token identity that opens the PR. When no
|
|
133
|
+
`approval-token` is passed, `auto-approve: true` falls back to the caller
|
|
134
|
+
`github.token` and therefore requires GitHub Actions approval permission. The
|
|
132
135
|
generated PR may use auto-merge, but it never bypasses the target branch checks.
|
|
133
136
|
|
|
137
|
+
Before enabling `auto-approve` and `auto-merge`, the caller repository must
|
|
138
|
+
provide one approval path and enable auto-merge:
|
|
139
|
+
|
|
140
|
+
- pass an independent `approval-token`; or enable **Actions > General >
|
|
141
|
+
Workflow permissions > Allow GitHub Actions to create and approve pull
|
|
142
|
+
requests**, so the caller `github.token` can approve independently from the
|
|
143
|
+
promotion token that opened the PR;
|
|
144
|
+
- **General > Pull Requests > Allow auto-merge**, so Patrol can arm the
|
|
145
|
+
protected merge while required reviews and checks are still pending.
|
|
146
|
+
|
|
147
|
+
GitHub rejects approval from the same identity that opened the pull request;
|
|
148
|
+
Patrol propagates that review failure instead of reporting success.
|
|
149
|
+
|
|
150
|
+
Patrol treats a GraphQL refusal to enable auto-merge as a hard failure. A run
|
|
151
|
+
must not report publication authority when GitHub accepted the HTTP request but
|
|
152
|
+
returned a GraphQL error in the response body.
|
|
153
|
+
|
|
134
154
|
## Exact-source stable promotion
|
|
135
155
|
|
|
136
156
|
For a selected `2.12.0-alpha.4`, Patrol creates the immutable source branch:
|
package/package.json
CHANGED
|
@@ -100,6 +100,36 @@ function inferVersionFromReleaseManifest(buildSummary = {}) {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
function normalizeGateProfileEvidence(gateAggregate = undefined) {
|
|
104
|
+
if (gateAggregate === undefined || gateAggregate === null || gateAggregate === "") return undefined;
|
|
105
|
+
if (!gateAggregate || typeof gateAggregate !== "object" || Array.isArray(gateAggregate)) {
|
|
106
|
+
throw new Error("gateAggregate must be an object");
|
|
107
|
+
}
|
|
108
|
+
if (gateAggregate.contract !== "buildchain.shifu-gate-aggregate/v1") {
|
|
109
|
+
throw new Error("gateAggregate must use buildchain.shifu-gate-aggregate/v1");
|
|
110
|
+
}
|
|
111
|
+
const { digest, ...digestInput } = gateAggregate;
|
|
112
|
+
const expectedDigest = `sha256:${sha256Json(digestInput)}`;
|
|
113
|
+
if (digest !== expectedDigest) {
|
|
114
|
+
throw new Error("gateAggregate digest does not match its content");
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
contract: gateAggregate.contract,
|
|
118
|
+
digest,
|
|
119
|
+
profile: nonEmptyString(gateAggregate.profile, "gateAggregate.profile"),
|
|
120
|
+
sourceSha: nonEmptyString(gateAggregate.sourceSha, "gateAggregate.sourceSha"),
|
|
121
|
+
registry: {
|
|
122
|
+
projectId: nonEmptyString(gateAggregate.registry?.projectId, "gateAggregate.registry.projectId"),
|
|
123
|
+
digest: nonEmptyString(gateAggregate.registry?.digest, "gateAggregate.registry.digest"),
|
|
124
|
+
},
|
|
125
|
+
matrixDigest: nonEmptyString(gateAggregate.matrixDigest, "gateAggregate.matrixDigest"),
|
|
126
|
+
status: nonEmptyString(gateAggregate.status, "gateAggregate.status"),
|
|
127
|
+
qualifying: gateAggregate.qualifying === true,
|
|
128
|
+
receiptCount: Array.isArray(gateAggregate.receipts) ? gateAggregate.receipts.length : 0,
|
|
129
|
+
gateResultCount: Array.isArray(gateAggregate.gates) ? gateAggregate.gates.length : 0,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
103
133
|
export function createReleaseCandidatePassport({
|
|
104
134
|
repository = "",
|
|
105
135
|
pullRequest = {},
|
|
@@ -111,6 +141,7 @@ export function createReleaseCandidatePassport({
|
|
|
111
141
|
sourceTreeHash = "",
|
|
112
142
|
buildSummary = {},
|
|
113
143
|
buildchain = {},
|
|
144
|
+
gateAggregate = undefined,
|
|
114
145
|
workflow = {},
|
|
115
146
|
createdAt = nowIso(),
|
|
116
147
|
} = {}) {
|
|
@@ -122,6 +153,7 @@ export function createReleaseCandidatePassport({
|
|
|
122
153
|
|| normalizeTargetChannel(normalizedSummary.publishGate?.channel)
|
|
123
154
|
|| normalizeTargetChannel(pullRequest.baseRef);
|
|
124
155
|
const resolvedVersion = version || normalizedSummary.publishSource?.consumerVersion || inferVersionFromReleaseManifest(normalizedSummary);
|
|
156
|
+
const gateProfileEvidence = normalizeGateProfileEvidence(gateAggregate);
|
|
125
157
|
const candidate = {
|
|
126
158
|
schemaVersion: 1,
|
|
127
159
|
contract: RELEASE_CANDIDATE_PASSPORT_CONTRACT,
|
|
@@ -164,6 +196,7 @@ export function createReleaseCandidatePassport({
|
|
|
164
196
|
buildSummaryContract: optionalString(normalizedSummary.contract),
|
|
165
197
|
buildSummaryHash: sha256Json(normalizedSummary),
|
|
166
198
|
},
|
|
199
|
+
...(gateProfileEvidence ? { gateProfileEvidence } : {}),
|
|
167
200
|
};
|
|
168
201
|
candidate.candidateHash = sha256Json({
|
|
169
202
|
repository: candidate.repository,
|
|
@@ -171,6 +204,7 @@ export function createReleaseCandidatePassport({
|
|
|
171
204
|
source: candidate.source,
|
|
172
205
|
platformMatrix: candidate.platformMatrix,
|
|
173
206
|
buildchain: candidate.buildchain,
|
|
207
|
+
...(candidate.gateProfileEvidence ? { gateProfileEvidence: candidate.gateProfileEvidence } : {}),
|
|
174
208
|
});
|
|
175
209
|
return candidate;
|
|
176
210
|
}
|
|
@@ -225,5 +259,13 @@ export function validateReleaseCandidatePassport({
|
|
|
225
259
|
const expectedHash = sha256Json(buildSummary);
|
|
226
260
|
check(passport.diagnostics?.buildSummaryHash === expectedHash, "build summary hash mismatch");
|
|
227
261
|
}
|
|
262
|
+
if (passport.gateProfileEvidence) {
|
|
263
|
+
check(passport.gateProfileEvidence.contract === "buildchain.shifu-gate-aggregate/v1", "gate profile evidence contract mismatch");
|
|
264
|
+
check(passport.gateProfileEvidence.sourceSha === passport.source?.headSha, "gate profile evidence source SHA mismatch");
|
|
265
|
+
check(passport.gateProfileEvidence.status === "pass", "gate profile evidence status must be pass");
|
|
266
|
+
check(passport.gateProfileEvidence.qualifying === true, "gate profile evidence must be qualifying");
|
|
267
|
+
check(Boolean(passport.gateProfileEvidence.digest), "gate profile evidence digest is required");
|
|
268
|
+
check(Boolean(passport.gateProfileEvidence.matrixDigest), "gate profile matrix digest is required");
|
|
269
|
+
}
|
|
228
270
|
return { ok: errors.length === 0, errors };
|
|
229
271
|
}
|