@kungfu-tech/buildchain 2.14.2 → 2.14.3-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/agent-index.json +2 -0
- package/dist/site/artifact-schemas.json +4 -0
- package/dist/site/badge-endpoint-registry.json +320 -0
- package/dist/site/badges/v1/kfd-5/aligned.json +14 -0
- package/dist/site/badges/v1/kfd-5/declared.json +14 -0
- package/dist/site/badges/v1/kfd-5/downgraded.json +14 -0
- package/dist/site/badges/v1/kfd-5/draft.json +14 -0
- package/dist/site/badges/v1/kfd-5/failed.json +14 -0
- package/dist/site/badges/v1/kfd-5/missing.json +14 -0
- package/dist/site/badges/v1/kfd-5/passed.json +14 -0
- package/dist/site/badges/v1/kfd-5/planned.json +14 -0
- package/dist/site/badges/v1/kfd-6/aligned.json +14 -0
- package/dist/site/badges/v1/kfd-6/declared.json +14 -0
- package/dist/site/badges/v1/kfd-6/downgraded.json +14 -0
- package/dist/site/badges/v1/kfd-6/draft.json +14 -0
- package/dist/site/badges/v1/kfd-6/failed.json +14 -0
- package/dist/site/badges/v1/kfd-6/missing.json +14 -0
- package/dist/site/badges/v1/kfd-6/passed.json +14 -0
- package/dist/site/badges/v1/kfd-6/planned.json +14 -0
- package/dist/site/buildchain-contract.json +34 -19
- package/dist/site/buildchain-site.json +13 -11
- package/dist/site/capability-registry.json +1 -1
- package/dist/site/controller-registry.json +14 -2
- package/dist/site/kfd-claims.json +21 -6
- package/dist/site/kfd-upstream-aggregate.json +12 -12
- package/dist/site/manual-registry.json +2 -2
- package/dist/site/node-api-registry.json +15 -2
- package/dist/site/page-registry.json +4 -4
- package/dist/site/public-surface-audit.json +12 -4
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-model.json +3 -1
- package/dist/site/release-passport-check-manifest.json +167 -0
- package/dist/site/release-provenance.json +3 -0
- package/dist/site/schemas/release-passport-v1.schema.json +198 -0
- package/dist/site/site-manifest.json +6 -6
- package/dist/site/workflow-registry.json +4 -1
- package/docs/release-passport.md +19 -0
- package/docs/release-propagation.md +19 -7
- package/package.json +5 -2
- package/packages/core/index.js +9 -0
- package/packages/core/package-manager.js +32 -0
- package/packages/core/release-passport-contract.js +249 -0
- package/scripts/check-inventory.mjs +7 -1
- package/scripts/generate-site-bundle.mjs +15 -0
- package/scripts/validate-package-manager-contract.mjs +24 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://buildchain.libkungfu.dev/schemas/release-passport-v1.schema.json",
|
|
4
|
+
"title": "Buildchain Release Passport v1",
|
|
5
|
+
"description": "Standalone structural schema for the Buildchain-owned release-passport envelope. KFD subsection semantics remain owned by their canonical KFD schemas.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"schemaVersion",
|
|
9
|
+
"contract",
|
|
10
|
+
"product",
|
|
11
|
+
"release",
|
|
12
|
+
"artifacts",
|
|
13
|
+
"evidence",
|
|
14
|
+
"recovery"
|
|
15
|
+
],
|
|
16
|
+
"properties": {
|
|
17
|
+
"schemaVersion": {
|
|
18
|
+
"const": 1
|
|
19
|
+
},
|
|
20
|
+
"contract": {
|
|
21
|
+
"const": "kungfu-buildchain-release-passport"
|
|
22
|
+
},
|
|
23
|
+
"generatedAt": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"format": "date-time"
|
|
26
|
+
},
|
|
27
|
+
"product": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"required": [
|
|
30
|
+
"name",
|
|
31
|
+
"repository",
|
|
32
|
+
"mechanism"
|
|
33
|
+
],
|
|
34
|
+
"properties": {
|
|
35
|
+
"name": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"minLength": 1
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"minLength": 1
|
|
42
|
+
},
|
|
43
|
+
"mechanism": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"minLength": 1
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"additionalProperties": true
|
|
49
|
+
},
|
|
50
|
+
"release": {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"required": [
|
|
53
|
+
"tag"
|
|
54
|
+
],
|
|
55
|
+
"properties": {
|
|
56
|
+
"tag": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"minLength": 1
|
|
59
|
+
},
|
|
60
|
+
"sourceSha": {
|
|
61
|
+
"type": "string"
|
|
62
|
+
},
|
|
63
|
+
"releaseSha": {
|
|
64
|
+
"type": "string"
|
|
65
|
+
},
|
|
66
|
+
"releaseMaterialSha": {
|
|
67
|
+
"type": "string"
|
|
68
|
+
},
|
|
69
|
+
"targetRef": {
|
|
70
|
+
"type": "string"
|
|
71
|
+
},
|
|
72
|
+
"channel": {
|
|
73
|
+
"type": "string"
|
|
74
|
+
},
|
|
75
|
+
"line": {
|
|
76
|
+
"type": "string"
|
|
77
|
+
},
|
|
78
|
+
"package": {
|
|
79
|
+
"type": "object"
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"additionalProperties": true
|
|
83
|
+
},
|
|
84
|
+
"artifacts": {
|
|
85
|
+
"type": "array",
|
|
86
|
+
"minItems": 1,
|
|
87
|
+
"items": {
|
|
88
|
+
"type": "object",
|
|
89
|
+
"required": [
|
|
90
|
+
"name"
|
|
91
|
+
],
|
|
92
|
+
"properties": {
|
|
93
|
+
"name": {
|
|
94
|
+
"type": "string",
|
|
95
|
+
"minLength": 1
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"additionalProperties": true
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"evidence": {
|
|
102
|
+
"type": "object",
|
|
103
|
+
"required": [
|
|
104
|
+
"artifactEvidence",
|
|
105
|
+
"impact",
|
|
106
|
+
"agentIndex"
|
|
107
|
+
],
|
|
108
|
+
"properties": {
|
|
109
|
+
"artifactEvidence": {
|
|
110
|
+
"type": "string",
|
|
111
|
+
"minLength": 1
|
|
112
|
+
},
|
|
113
|
+
"impact": {
|
|
114
|
+
"type": "string",
|
|
115
|
+
"minLength": 1
|
|
116
|
+
},
|
|
117
|
+
"agentIndex": {
|
|
118
|
+
"type": "string",
|
|
119
|
+
"minLength": 1
|
|
120
|
+
},
|
|
121
|
+
"publishEvidence": {
|
|
122
|
+
"type": "string"
|
|
123
|
+
},
|
|
124
|
+
"transactionState": {
|
|
125
|
+
"type": "string"
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"additionalProperties": true
|
|
129
|
+
},
|
|
130
|
+
"recovery": {
|
|
131
|
+
"type": "object"
|
|
132
|
+
},
|
|
133
|
+
"workflow": {
|
|
134
|
+
"type": "object"
|
|
135
|
+
},
|
|
136
|
+
"runnerPolicy": {
|
|
137
|
+
"type": "object"
|
|
138
|
+
},
|
|
139
|
+
"versionImpact": {
|
|
140
|
+
"type": "object"
|
|
141
|
+
},
|
|
142
|
+
"surfaceImpacts": {
|
|
143
|
+
"type": "array",
|
|
144
|
+
"items": {
|
|
145
|
+
"type": "object"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"packageSet": {
|
|
149
|
+
"type": "object"
|
|
150
|
+
},
|
|
151
|
+
"anchorManifest": {
|
|
152
|
+
"type": "object"
|
|
153
|
+
},
|
|
154
|
+
"trustedPublishing": {
|
|
155
|
+
"type": "object"
|
|
156
|
+
},
|
|
157
|
+
"transaction": {
|
|
158
|
+
"type": "object"
|
|
159
|
+
},
|
|
160
|
+
"surfaceTimestampPolicy": {
|
|
161
|
+
"type": "object"
|
|
162
|
+
},
|
|
163
|
+
"buildFacts": {
|
|
164
|
+
"type": "array",
|
|
165
|
+
"items": {
|
|
166
|
+
"type": "object"
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
"buildSummary": {
|
|
170
|
+
"type": "object"
|
|
171
|
+
},
|
|
172
|
+
"platformArtifactManifests": {
|
|
173
|
+
"type": "array",
|
|
174
|
+
"items": {
|
|
175
|
+
"type": "object"
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
"distTagPromotion": {
|
|
179
|
+
"type": "object"
|
|
180
|
+
},
|
|
181
|
+
"controllerReceipts": {
|
|
182
|
+
"type": "array",
|
|
183
|
+
"items": {
|
|
184
|
+
"type": "object"
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
"kfd-1": {
|
|
188
|
+
"type": "object"
|
|
189
|
+
},
|
|
190
|
+
"kfd-2": {
|
|
191
|
+
"type": "object"
|
|
192
|
+
},
|
|
193
|
+
"kfd-3": {
|
|
194
|
+
"type": "object"
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
"additionalProperties": true
|
|
198
|
+
}
|
|
@@ -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-18T22:57:23.219Z",
|
|
5
|
+
"publishedAt": "2026-07-18T22:57:23.219Z",
|
|
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": "8b0423396f09f29e2ea05c38896228751767ab21",
|
|
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.3-alpha.0",
|
|
41
41
|
"versionSource": "package.json#version"
|
|
42
42
|
},
|
|
43
43
|
"entrypoint": "buildchain-site.json",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"path": "docs/release-passport.md",
|
|
70
70
|
"plane": "verify",
|
|
71
71
|
"exists": true,
|
|
72
|
-
"digest": "sha256:
|
|
72
|
+
"digest": "sha256:0029014ffab7d7228a12d8b92326b78cf6757a41f481b661c5997e2cef06d3cc"
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
"id": "controller-evidence",
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"path": "docs/release-propagation.md",
|
|
102
102
|
"plane": "use",
|
|
103
103
|
"exists": true,
|
|
104
|
-
"digest": "sha256:
|
|
104
|
+
"digest": "sha256:fc2b743e3d76c4f90cdd8c6560cf9d619ad25d74c15c074be96db7bcb6cc2a29"
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
"id": "readme-badges",
|
|
@@ -1444,17 +1444,20 @@
|
|
|
1444
1444
|
"buildchain-repository",
|
|
1445
1445
|
"downstream-base-ref",
|
|
1446
1446
|
"downstream-branch",
|
|
1447
|
+
"downstream-prepare-command",
|
|
1447
1448
|
"downstream-repository",
|
|
1448
1449
|
"downstream-target",
|
|
1450
|
+
"downstream-verify-command",
|
|
1449
1451
|
"dry-run",
|
|
1450
1452
|
"graph-json",
|
|
1451
1453
|
"lock-path",
|
|
1452
1454
|
"pr-body",
|
|
1453
1455
|
"pr-title",
|
|
1456
|
+
"refresh-managed-readme-badges",
|
|
1454
1457
|
"source-node",
|
|
1455
1458
|
"upstream-release-json"
|
|
1456
1459
|
],
|
|
1457
|
-
"inputCount":
|
|
1460
|
+
"inputCount": 16,
|
|
1458
1461
|
"secrets": [
|
|
1459
1462
|
"propagation-token"
|
|
1460
1463
|
],
|
package/docs/release-passport.md
CHANGED
|
@@ -19,6 +19,25 @@ do not infer execution evidence from a green job. See
|
|
|
19
19
|
The release passport surface is a welded contract. Additive fields are allowed;
|
|
20
20
|
breaking semantic changes require a new major line.
|
|
21
21
|
|
|
22
|
+
Independent verifiers can pin two package-owned, standalone files without
|
|
23
|
+
scraping this implementation:
|
|
24
|
+
|
|
25
|
+
- `dist/site/schemas/release-passport-v1.schema.json` is the self-contained
|
|
26
|
+
JSON Schema for the Buildchain-owned envelope;
|
|
27
|
+
- `dist/site/release-passport-check-manifest.json` names the normative checker,
|
|
28
|
+
required and conditional sibling evidence, local-resolution rules,
|
|
29
|
+
Buildchain aggregation fields, canonical KFD subsection schema authorities,
|
|
30
|
+
and forward-compatibility policy.
|
|
31
|
+
|
|
32
|
+
The same files are exported as
|
|
33
|
+
`@kungfu-tech/buildchain/site/schemas/release-passport-v1.schema.json` and
|
|
34
|
+
`@kungfu-tech/buildchain/site/release-passport-check-manifest.json`. The schema
|
|
35
|
+
checks the envelope shape. `buildchain verify release-passport` remains the
|
|
36
|
+
normative semantic checker: it resolves sibling evidence relative to the
|
|
37
|
+
passport and fails closed on missing evidence, digest drift, or inconsistent
|
|
38
|
+
release facts. Buildchain owns envelope aggregation; KFD retains schema and
|
|
39
|
+
compatibility authority for the `kfd-1`, `kfd-2`, and `kfd-3` subsections.
|
|
40
|
+
|
|
22
41
|
P0 protocol artifacts:
|
|
23
42
|
|
|
24
43
|
- `product-mechanism.json`
|
|
@@ -182,6 +182,8 @@ jobs:
|
|
|
182
182
|
downstream-target: site-libkungfu-dev
|
|
183
183
|
downstream-repository: kungfu-systems/site-libkungfu-dev
|
|
184
184
|
downstream-base-ref: dev/v2/v2.7
|
|
185
|
+
downstream-prepare-command: pnpm install --frozen-lockfile --ignore-scripts
|
|
186
|
+
downstream-verify-command: pnpm run check
|
|
185
187
|
dry-run: false
|
|
186
188
|
secrets:
|
|
187
189
|
propagation-token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}
|
|
@@ -199,13 +201,23 @@ open PR.
|
|
|
199
201
|
The workflow checks out the Buildchain runtime selected by
|
|
200
202
|
`buildchain-repository` and `buildchain-ref` into `.buildchain/runtime`, invokes
|
|
201
203
|
that runtime for the propagation plan and lock write, then checks out the
|
|
202
|
-
downstream repository
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
204
|
+
downstream repository and writes the exact lock. A consumer that must pin the
|
|
205
|
+
exact upstream package or regenerate deterministic files declares
|
|
206
|
+
`downstream-prepare-command`. The command receives
|
|
207
|
+
`BUILDCHAIN_UPSTREAM_PACKAGE_NAME`, `BUILDCHAIN_UPSTREAM_PACKAGE_VERSION`, and
|
|
208
|
+
`BUILDCHAIN_UPSTREAM_RELEASE_LOCK`. After preparation, Buildchain refreshes an
|
|
209
|
+
existing `<!-- buildchain:badges:start -->` README block by default. Consumers
|
|
210
|
+
can disable that step with `refresh-managed-readme-badges: false`.
|
|
211
|
+
|
|
212
|
+
`downstream-verify-command` runs against the final tree before any commit or
|
|
213
|
+
push, so consumers can use the same check as their PR workflow. Preparation,
|
|
214
|
+
badge refresh, and verification failures all fail closed. The workflow stages
|
|
215
|
+
the complete deterministic result, signs the propagation commit with DCO, and
|
|
216
|
+
then opens or updates the PR. It does not publish the downstream release
|
|
217
|
+
directly. The downstream repository keeps its normal Buildchain governance: the
|
|
218
|
+
PR updates source-of-truth facts, then downstream alpha or release publication
|
|
219
|
+
runs through its own protected channel. A byte-identical rerun is an explicit
|
|
220
|
+
successful no-op.
|
|
209
221
|
For unreleased runtime validation, keep the caller's reusable workflow reference
|
|
210
222
|
on `@v2` and pass a temporary train ref through `buildchain-ref`.
|
|
211
223
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.3-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",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"./release-candidate": "./packages/core/release-candidate.js",
|
|
39
39
|
"./stable-candidate-ledger": "./packages/core/stable-candidate-ledger.js",
|
|
40
40
|
"./release-passport": "./packages/core/release-passport.js",
|
|
41
|
+
"./release-passport-contract": "./packages/core/release-passport-contract.js",
|
|
41
42
|
"./release-propagation": "./packages/core/release-propagation.js",
|
|
42
43
|
"./surface-manifest": "./packages/core/surface-manifest.js",
|
|
43
44
|
"./buildchain-kfd-claims": "./packages/core/buildchain-kfd-claims.js",
|
|
@@ -57,6 +58,8 @@
|
|
|
57
58
|
"./site/kfd-upstream-aggregate.json": "./dist/site/kfd-upstream-aggregate.json",
|
|
58
59
|
"./site/release-model.json": "./dist/site/release-model.json",
|
|
59
60
|
"./site/artifact-schemas.json": "./dist/site/artifact-schemas.json",
|
|
61
|
+
"./site/release-passport-check-manifest.json": "./dist/site/release-passport-check-manifest.json",
|
|
62
|
+
"./site/schemas/release-passport-v1.schema.json": "./dist/site/schemas/release-passport-v1.schema.json",
|
|
60
63
|
"./site/buildchain-contract.json": "./dist/site/buildchain-contract.json",
|
|
61
64
|
"./site/product-mechanism.json": "./dist/site/product-mechanism.json",
|
|
62
65
|
"./site/release-provenance.json": "./dist/site/release-provenance.json",
|
|
@@ -104,7 +107,7 @@
|
|
|
104
107
|
"vitest": "^4.1.9"
|
|
105
108
|
},
|
|
106
109
|
"dependencies": {
|
|
107
|
-
"@kungfu-tech/kfd": "1.0.0-alpha.
|
|
110
|
+
"@kungfu-tech/kfd": "1.0.0-alpha.29",
|
|
108
111
|
"smol-toml": "1.7.0"
|
|
109
112
|
}
|
|
110
113
|
}
|
package/packages/core/index.js
CHANGED
|
@@ -37,6 +37,7 @@ export {
|
|
|
37
37
|
detectPackageManager,
|
|
38
38
|
getWorkspaceInfo,
|
|
39
39
|
shellJoin,
|
|
40
|
+
validatePackageManagerContract,
|
|
40
41
|
} from "./package-manager.js";
|
|
41
42
|
|
|
42
43
|
export {
|
|
@@ -320,6 +321,14 @@ export {
|
|
|
320
321
|
verifyReleasePassport,
|
|
321
322
|
} from "./release-passport.js";
|
|
322
323
|
|
|
324
|
+
export {
|
|
325
|
+
RELEASE_PASSPORT_CHECK_MANIFEST_CONTRACT,
|
|
326
|
+
RELEASE_PASSPORT_SCHEMA,
|
|
327
|
+
RELEASE_PASSPORT_SCHEMA_ID,
|
|
328
|
+
createReleasePassportCheckManifest,
|
|
329
|
+
validateReleasePassportSchema,
|
|
330
|
+
} from "./release-passport-contract.js";
|
|
331
|
+
|
|
323
332
|
export {
|
|
324
333
|
BUILDCHAIN_CONSUMER_ISSUE_CONTRACT,
|
|
325
334
|
BUILDCHAIN_WORKFLOW_FRICTION_ISSUE_CONTRACT,
|
|
@@ -15,6 +15,37 @@ function parsePackageManager(value) {
|
|
|
15
15
|
return match ? match[1] : null;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
export function validatePackageManagerContract({ cwd = process.cwd(), expectedManager = "" } = {}) {
|
|
19
|
+
const resolvedCwd = path.resolve(cwd);
|
|
20
|
+
const pkg = readJsonIfExists(path.join(resolvedCwd, "package.json"));
|
|
21
|
+
const declaredSpec = String(pkg?.packageManager || "").trim();
|
|
22
|
+
const declaredMatch = declaredSpec.match(/^([A-Za-z0-9._-]+)@([^\s]+)$/);
|
|
23
|
+
|
|
24
|
+
if (declaredSpec && !declaredMatch) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`Invalid packageManager declaration: ${declaredSpec}. Expected <manager>@<version>.`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const declaredName = declaredMatch?.[1] || "";
|
|
31
|
+
const detected = declaredName && !KNOWN_MANAGERS.has(declaredName)
|
|
32
|
+
? { name: "custom", reason: "packageManager" }
|
|
33
|
+
: detectPackageManager(resolvedCwd);
|
|
34
|
+
const expected = String(expectedManager || "").trim();
|
|
35
|
+
if (expected && expected !== "custom" && detected.name !== expected) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Package manager mismatch: workflow requested ${expected}, but the consumer declares ${detected.name}.`,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
name: detected.name,
|
|
43
|
+
reason: detected.reason,
|
|
44
|
+
declaredSpec,
|
|
45
|
+
declaredVersion: declaredMatch?.[2] || "",
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
18
49
|
export function assertPackageManager(manager) {
|
|
19
50
|
if (!KNOWN_MANAGERS.has(manager)) {
|
|
20
51
|
throw new Error(`Unsupported package manager: ${manager}`);
|
|
@@ -288,4 +319,5 @@ export default {
|
|
|
288
319
|
getWorkspaceInfo,
|
|
289
320
|
getYarnLockInfo,
|
|
290
321
|
shellJoin,
|
|
322
|
+
validatePackageManagerContract,
|
|
291
323
|
};
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import kfdStandards from "@kungfu-tech/kfd/standards.json" with { type: "json" };
|
|
2
|
+
import {
|
|
3
|
+
RELEASE_CHECK_REPORT_CONTRACT,
|
|
4
|
+
RELEASE_PASSPORT_CONTRACT,
|
|
5
|
+
} from "./release-passport.js";
|
|
6
|
+
|
|
7
|
+
export const RELEASE_PASSPORT_SCHEMA_ID =
|
|
8
|
+
"https://buildchain.libkungfu.dev/schemas/release-passport-v1.schema.json";
|
|
9
|
+
export const RELEASE_PASSPORT_CHECK_MANIFEST_CONTRACT =
|
|
10
|
+
"kungfu-buildchain-release-passport-check-manifest";
|
|
11
|
+
|
|
12
|
+
const OBJECT = { type: "object" };
|
|
13
|
+
const STRING = { type: "string", minLength: 1 };
|
|
14
|
+
const OPTIONAL_STRING = { type: "string" };
|
|
15
|
+
|
|
16
|
+
export const RELEASE_PASSPORT_SCHEMA = {
|
|
17
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
18
|
+
$id: RELEASE_PASSPORT_SCHEMA_ID,
|
|
19
|
+
title: "Buildchain Release Passport v1",
|
|
20
|
+
description:
|
|
21
|
+
"Standalone structural schema for the Buildchain-owned release-passport envelope. KFD subsection semantics remain owned by their canonical KFD schemas.",
|
|
22
|
+
type: "object",
|
|
23
|
+
required: [
|
|
24
|
+
"schemaVersion",
|
|
25
|
+
"contract",
|
|
26
|
+
"product",
|
|
27
|
+
"release",
|
|
28
|
+
"artifacts",
|
|
29
|
+
"evidence",
|
|
30
|
+
"recovery",
|
|
31
|
+
],
|
|
32
|
+
properties: {
|
|
33
|
+
schemaVersion: { const: 1 },
|
|
34
|
+
contract: { const: RELEASE_PASSPORT_CONTRACT },
|
|
35
|
+
generatedAt: { type: "string", format: "date-time" },
|
|
36
|
+
product: {
|
|
37
|
+
type: "object",
|
|
38
|
+
required: ["name", "repository", "mechanism"],
|
|
39
|
+
properties: {
|
|
40
|
+
name: STRING,
|
|
41
|
+
repository: STRING,
|
|
42
|
+
mechanism: STRING,
|
|
43
|
+
},
|
|
44
|
+
additionalProperties: true,
|
|
45
|
+
},
|
|
46
|
+
release: {
|
|
47
|
+
type: "object",
|
|
48
|
+
required: ["tag"],
|
|
49
|
+
properties: {
|
|
50
|
+
tag: STRING,
|
|
51
|
+
sourceSha: OPTIONAL_STRING,
|
|
52
|
+
releaseSha: OPTIONAL_STRING,
|
|
53
|
+
releaseMaterialSha: OPTIONAL_STRING,
|
|
54
|
+
targetRef: OPTIONAL_STRING,
|
|
55
|
+
channel: OPTIONAL_STRING,
|
|
56
|
+
line: OPTIONAL_STRING,
|
|
57
|
+
package: OBJECT,
|
|
58
|
+
},
|
|
59
|
+
additionalProperties: true,
|
|
60
|
+
},
|
|
61
|
+
artifacts: {
|
|
62
|
+
type: "array",
|
|
63
|
+
minItems: 1,
|
|
64
|
+
items: {
|
|
65
|
+
type: "object",
|
|
66
|
+
required: ["name"],
|
|
67
|
+
properties: { name: STRING },
|
|
68
|
+
additionalProperties: true,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
evidence: {
|
|
72
|
+
type: "object",
|
|
73
|
+
required: ["artifactEvidence", "impact", "agentIndex"],
|
|
74
|
+
properties: {
|
|
75
|
+
artifactEvidence: STRING,
|
|
76
|
+
impact: STRING,
|
|
77
|
+
agentIndex: STRING,
|
|
78
|
+
publishEvidence: OPTIONAL_STRING,
|
|
79
|
+
transactionState: OPTIONAL_STRING,
|
|
80
|
+
},
|
|
81
|
+
additionalProperties: true,
|
|
82
|
+
},
|
|
83
|
+
recovery: OBJECT,
|
|
84
|
+
workflow: OBJECT,
|
|
85
|
+
runnerPolicy: OBJECT,
|
|
86
|
+
versionImpact: OBJECT,
|
|
87
|
+
surfaceImpacts: { type: "array", items: OBJECT },
|
|
88
|
+
packageSet: OBJECT,
|
|
89
|
+
anchorManifest: OBJECT,
|
|
90
|
+
trustedPublishing: OBJECT,
|
|
91
|
+
transaction: OBJECT,
|
|
92
|
+
surfaceTimestampPolicy: OBJECT,
|
|
93
|
+
buildFacts: { type: "array", items: OBJECT },
|
|
94
|
+
buildSummary: OBJECT,
|
|
95
|
+
platformArtifactManifests: { type: "array", items: OBJECT },
|
|
96
|
+
distTagPromotion: OBJECT,
|
|
97
|
+
controllerReceipts: { type: "array", items: OBJECT },
|
|
98
|
+
"kfd-1": OBJECT,
|
|
99
|
+
"kfd-2": OBJECT,
|
|
100
|
+
"kfd-3": OBJECT,
|
|
101
|
+
},
|
|
102
|
+
additionalProperties: true,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const BUILDCHAIN_AGGREGATION_FIELDS = [
|
|
106
|
+
"product",
|
|
107
|
+
"release",
|
|
108
|
+
"workflow",
|
|
109
|
+
"runnerPolicy",
|
|
110
|
+
"versionImpact",
|
|
111
|
+
"surfaceImpacts",
|
|
112
|
+
"packageSet",
|
|
113
|
+
"anchorManifest",
|
|
114
|
+
"trustedPublishing",
|
|
115
|
+
"transaction",
|
|
116
|
+
"surfaceTimestampPolicy",
|
|
117
|
+
"buildFacts",
|
|
118
|
+
"buildSummary",
|
|
119
|
+
"platformArtifactManifests",
|
|
120
|
+
"distTagPromotion",
|
|
121
|
+
"controllerReceipts",
|
|
122
|
+
"artifacts",
|
|
123
|
+
"evidence",
|
|
124
|
+
"recovery",
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
function kfdSectionAuthority(id, standards = kfdStandards) {
|
|
128
|
+
const standard = standards?.standards?.[id] || {};
|
|
129
|
+
return {
|
|
130
|
+
section: id,
|
|
131
|
+
owner: "KFD",
|
|
132
|
+
package: standards?.source?.package || "@kungfu-tech/kfd",
|
|
133
|
+
metadataSchema: standards?.metadataSchema || {},
|
|
134
|
+
schemaIds: standard.schemaIds || {},
|
|
135
|
+
schemaPaths: standard.schemaPaths || {},
|
|
136
|
+
compatibility: standard.compatibility || {},
|
|
137
|
+
rule: `Buildchain aggregates and checks ${id} evidence but does not redefine its schema semantics.`,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function createReleasePassportCheckManifest({ standards = kfdStandards } = {}) {
|
|
142
|
+
return {
|
|
143
|
+
schemaVersion: 1,
|
|
144
|
+
contract: RELEASE_PASSPORT_CHECK_MANIFEST_CONTRACT,
|
|
145
|
+
passport: {
|
|
146
|
+
contract: RELEASE_PASSPORT_CONTRACT,
|
|
147
|
+
schemaVersion: 1,
|
|
148
|
+
schema: {
|
|
149
|
+
id: RELEASE_PASSPORT_SCHEMA_ID,
|
|
150
|
+
path: "schemas/release-passport-v1.schema.json",
|
|
151
|
+
npmExport: "@kungfu-tech/buildchain/site/schemas/release-passport-v1.schema.json",
|
|
152
|
+
},
|
|
153
|
+
checker: {
|
|
154
|
+
contract: RELEASE_CHECK_REPORT_CONTRACT,
|
|
155
|
+
command: "buildchain verify release-passport <buildchain.release.json>",
|
|
156
|
+
nodeApi: "@kungfu-tech/buildchain/release-passport#verifyReleasePassport",
|
|
157
|
+
result: "check-report.json",
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
ownership: {
|
|
161
|
+
envelopeOwner: "Buildchain",
|
|
162
|
+
aggregationFields: BUILDCHAIN_AGGREGATION_FIELDS,
|
|
163
|
+
kfdSections: ["kfd-1", "kfd-2", "kfd-3"].map((id) =>
|
|
164
|
+
kfdSectionAuthority(id, standards),
|
|
165
|
+
),
|
|
166
|
+
},
|
|
167
|
+
localClosure: {
|
|
168
|
+
entrypoint: "buildchain.release.json",
|
|
169
|
+
resolutionBase: "directory containing the passport",
|
|
170
|
+
requiredSiblings: [
|
|
171
|
+
"product.mechanism",
|
|
172
|
+
"evidence.artifactEvidence",
|
|
173
|
+
"evidence.impact",
|
|
174
|
+
"evidence.agentIndex",
|
|
175
|
+
],
|
|
176
|
+
conditionalSiblings: [
|
|
177
|
+
{
|
|
178
|
+
when: "evidence.publishEvidence is present or packageSet is present",
|
|
179
|
+
pointer: "evidence.publishEvidence",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
when: "evidence.transactionState is present",
|
|
183
|
+
pointer: "evidence.transactionState",
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
rule:
|
|
187
|
+
"The normative checker resolves every declared sibling relative to the passport and fails closed on missing required evidence, digest drift, or semantic mismatch.",
|
|
188
|
+
},
|
|
189
|
+
compatibility: {
|
|
190
|
+
envelope:
|
|
191
|
+
"Compatible optional fields may be added while schemaVersion remains 1; removing or changing required-field semantics requires a new schema id or contract version.",
|
|
192
|
+
siblings:
|
|
193
|
+
"New optional siblings may be added in v1; making a sibling required requires a new schema id or contract version.",
|
|
194
|
+
kfdSections:
|
|
195
|
+
"Each KFD subsection follows the compatibility metadata published by @kungfu-tech/kfd; Buildchain must not silently reinterpret a KFD schema version.",
|
|
196
|
+
unknownFields: "Independent readers must preserve or ignore unknown additive fields.",
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function matchesType(value, type) {
|
|
202
|
+
if (type === "object") return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
203
|
+
if (type === "array") return Array.isArray(value);
|
|
204
|
+
if (type === "integer") return Number.isInteger(value);
|
|
205
|
+
return typeof value === type;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function validateSchemaNode(schema, value, pointer, issues) {
|
|
209
|
+
if (schema.type && !matchesType(value, schema.type)) {
|
|
210
|
+
issues.push({ pointer, code: "type", message: `${pointer} must be ${schema.type}` });
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (Object.hasOwn(schema, "const") && value !== schema.const) {
|
|
214
|
+
issues.push({ pointer, code: "const", message: `${pointer} must equal ${JSON.stringify(schema.const)}` });
|
|
215
|
+
}
|
|
216
|
+
if (schema.minLength && typeof value === "string" && value.length < schema.minLength) {
|
|
217
|
+
issues.push({ pointer, code: "minLength", message: `${pointer} must not be empty` });
|
|
218
|
+
}
|
|
219
|
+
if (schema.minItems && Array.isArray(value) && value.length < schema.minItems) {
|
|
220
|
+
issues.push({ pointer, code: "minItems", message: `${pointer} must contain at least ${schema.minItems} item(s)` });
|
|
221
|
+
}
|
|
222
|
+
if (Array.isArray(value) && schema.items) {
|
|
223
|
+
value.forEach((entry, index) => validateSchemaNode(schema.items, entry, `${pointer}/${index}`, issues));
|
|
224
|
+
}
|
|
225
|
+
if (matchesType(value, "object")) {
|
|
226
|
+
for (const key of schema.required || []) {
|
|
227
|
+
if (!Object.hasOwn(value, key)) {
|
|
228
|
+
issues.push({ pointer: `${pointer}/${key}`, code: "required", message: `${pointer}/${key} is required` });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
for (const [key, childSchema] of Object.entries(schema.properties || {})) {
|
|
232
|
+
if (Object.hasOwn(value, key)) {
|
|
233
|
+
validateSchemaNode(childSchema, value[key], `${pointer}/${key}`, issues);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function validateReleasePassportSchema(passport) {
|
|
240
|
+
const issues = [];
|
|
241
|
+
validateSchemaNode(RELEASE_PASSPORT_SCHEMA, passport, "#", issues);
|
|
242
|
+
return {
|
|
243
|
+
schemaVersion: 1,
|
|
244
|
+
contract: "kungfu-buildchain-release-passport-schema-validation",
|
|
245
|
+
schemaId: RELEASE_PASSPORT_SCHEMA_ID,
|
|
246
|
+
ok: issues.length === 0,
|
|
247
|
+
issues,
|
|
248
|
+
};
|
|
249
|
+
}
|