@kungfu-tech/buildchain 2.12.7-alpha.22 → 2.12.7-alpha.4
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/actions/promote-buildchain-ref/README.md +1 -5
- package/dist/site/buildchain-contract.json +22 -47
- package/dist/site/buildchain-site.json +14 -14
- package/dist/site/capability-registry.json +1 -1
- package/dist/site/controller-registry.json +3 -23
- package/dist/site/kfd-claims.json +8 -59
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +2 -2
- package/dist/site/node-api-registry.json +6 -6
- package/dist/site/page-registry.json +8 -8
- package/dist/site/public-surface-audit.json +8 -68
- package/dist/site/publication-authority-registry.json +1 -25
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +6 -6
- package/dist/site/workflow-registry.json +5 -68
- package/docs/publication-artifacts.md +19 -22
- package/docs/publication-authority.md +7 -10
- package/docs/release-candidate.md +0 -19
- package/package.json +1 -1
- package/packages/core/buildchain-publication-authority.js +0 -1
- package/packages/core/controller-evidence.js +1 -1
- package/packages/core/index.js +0 -7
- package/packages/core/publication-authority.js +2 -41
- package/packages/core/publication-control-plane-audit.js +1 -3
- package/scripts/assemble-self-publication-admission.mjs +8 -17
- package/scripts/audit-publication-control-plane.mjs +2 -5
- package/scripts/locked-source-checkout.mjs +13 -50
- package/packages/core/publication-artifact-candidate.js +0 -128
- package/scripts/assemble-publication-artifact-admission.mjs +0 -190
- package/scripts/publication-artifact-candidate.mjs +0 -122
|
@@ -215,19 +215,14 @@
|
|
|
215
215
|
"reusable": true,
|
|
216
216
|
"inputs": [
|
|
217
217
|
"admission-json",
|
|
218
|
-
"authority-workflow-path",
|
|
219
218
|
"auto-admission",
|
|
220
|
-
"auto-admission-kind",
|
|
221
|
-
"auto-no-gate",
|
|
222
219
|
"buildchain-ref",
|
|
223
220
|
"buildchain-repository",
|
|
224
221
|
"control-plane-audit-json",
|
|
225
222
|
"dry-run",
|
|
226
|
-
"evidence-controller-artifact",
|
|
227
223
|
"evidence-manifest-pattern",
|
|
228
224
|
"evidence-passport-artifact",
|
|
229
225
|
"evidence-payload-pattern",
|
|
230
|
-
"evidence-publication-artifact",
|
|
231
226
|
"evidence-repository",
|
|
232
227
|
"evidence-run-id",
|
|
233
228
|
"evidence-summary-artifact",
|
|
@@ -238,13 +233,12 @@
|
|
|
238
233
|
"publication-target",
|
|
239
234
|
"publication-version",
|
|
240
235
|
"publisher-workflow-path",
|
|
241
|
-
"required-status-check",
|
|
242
236
|
"runner-provenance-json",
|
|
243
237
|
"source-sha",
|
|
244
238
|
"target-ref",
|
|
245
239
|
"used-nonces-json"
|
|
246
240
|
],
|
|
247
|
-
"inputCount":
|
|
241
|
+
"inputCount": 23,
|
|
248
242
|
"secrets": [],
|
|
249
243
|
"secretCount": 0,
|
|
250
244
|
"outputs": [
|
|
@@ -1031,54 +1025,6 @@
|
|
|
1031
1025
|
"capabilityGroup": "api-cli-reference",
|
|
1032
1026
|
"status": "active"
|
|
1033
1027
|
},
|
|
1034
|
-
{
|
|
1035
|
-
"id": "paper-release-sealed",
|
|
1036
|
-
"path": ".github/workflows/paper-release-sealed.yml",
|
|
1037
|
-
"reusable": true,
|
|
1038
|
-
"inputs": [
|
|
1039
|
-
"artifact-name",
|
|
1040
|
-
"artifact-paths",
|
|
1041
|
-
"artifact-retention-days",
|
|
1042
|
-
"branch-protection-bypass-apps",
|
|
1043
|
-
"branch-protection-bypass-teams",
|
|
1044
|
-
"branch-protection-bypass-users",
|
|
1045
|
-
"buildchain-contract-lock-path",
|
|
1046
|
-
"buildchain-ref",
|
|
1047
|
-
"buildchain-repository",
|
|
1048
|
-
"dry-run",
|
|
1049
|
-
"github-release",
|
|
1050
|
-
"github-release-notes",
|
|
1051
|
-
"github-release-title",
|
|
1052
|
-
"package-name",
|
|
1053
|
-
"publish-dist-tag",
|
|
1054
|
-
"publisher-workflow-path",
|
|
1055
|
-
"release-passport-impact-json",
|
|
1056
|
-
"release-passport-output-dir",
|
|
1057
|
-
"release-passport-product-name",
|
|
1058
|
-
"required-status-check",
|
|
1059
|
-
"target-ref",
|
|
1060
|
-
"target-sha",
|
|
1061
|
-
"toolchain-type",
|
|
1062
|
-
"verify-command",
|
|
1063
|
-
"working-directory"
|
|
1064
|
-
],
|
|
1065
|
-
"inputCount": 25,
|
|
1066
|
-
"secrets": [
|
|
1067
|
-
"BUILDCHAIN_PROMOTION_TOKEN"
|
|
1068
|
-
],
|
|
1069
|
-
"secretCount": 1,
|
|
1070
|
-
"outputs": [
|
|
1071
|
-
"github-release-url",
|
|
1072
|
-
"package-name",
|
|
1073
|
-
"package-version",
|
|
1074
|
-
"public-release-tag",
|
|
1075
|
-
"release-passport-path"
|
|
1076
|
-
],
|
|
1077
|
-
"outputCount": 5,
|
|
1078
|
-
"surface": "repository-workflow",
|
|
1079
|
-
"capabilityGroup": "api-cli-reference",
|
|
1080
|
-
"status": "active"
|
|
1081
|
-
},
|
|
1082
1028
|
{
|
|
1083
1029
|
"id": "paper-release",
|
|
1084
1030
|
"path": ".github/workflows/paper-release.yml",
|
|
@@ -1248,11 +1194,7 @@
|
|
|
1248
1194
|
"buildchain-ref",
|
|
1249
1195
|
"buildchain-repository",
|
|
1250
1196
|
"node-version",
|
|
1251
|
-
"package-name",
|
|
1252
|
-
"prepare-paper-package",
|
|
1253
|
-
"publish-dist-tag",
|
|
1254
1197
|
"setup-node",
|
|
1255
|
-
"target-ref",
|
|
1256
1198
|
"toolchain-command",
|
|
1257
1199
|
"toolchain-digest",
|
|
1258
1200
|
"toolchain-image",
|
|
@@ -1260,7 +1202,7 @@
|
|
|
1260
1202
|
"verify-command",
|
|
1261
1203
|
"working-directory"
|
|
1262
1204
|
],
|
|
1263
|
-
"inputCount":
|
|
1205
|
+
"inputCount": 17,
|
|
1264
1206
|
"secrets": [],
|
|
1265
1207
|
"secretCount": 0,
|
|
1266
1208
|
"outputs": [
|
|
@@ -1271,17 +1213,13 @@
|
|
|
1271
1213
|
"controller-receipt-digest",
|
|
1272
1214
|
"controller-receipt-json",
|
|
1273
1215
|
"controller-receipt-status",
|
|
1274
|
-
"package-name",
|
|
1275
|
-
"package-version",
|
|
1276
1216
|
"publication-artifact-name",
|
|
1277
1217
|
"publication-manifest-json",
|
|
1278
1218
|
"publication-manifest-path",
|
|
1279
1219
|
"publication-passport-path",
|
|
1280
|
-
"publication-registry-path"
|
|
1281
|
-
"publish-dist-tag",
|
|
1282
|
-
"runtime-sha"
|
|
1220
|
+
"publication-registry-path"
|
|
1283
1221
|
],
|
|
1284
|
-
"outputCount":
|
|
1222
|
+
"outputCount": 12,
|
|
1285
1223
|
"surface": "repository-workflow",
|
|
1286
1224
|
"capabilityGroup": "reusable-build",
|
|
1287
1225
|
"status": "active"
|
|
@@ -1310,7 +1248,6 @@
|
|
|
1310
1248
|
"package-manager",
|
|
1311
1249
|
"publication-admission-json",
|
|
1312
1250
|
"publication-auto-admission",
|
|
1313
|
-
"publication-auto-no-gate",
|
|
1314
1251
|
"publication-control-plane-audit-json",
|
|
1315
1252
|
"publication-expected-json",
|
|
1316
1253
|
"publication-gate-aggregate-json",
|
|
@@ -1348,7 +1285,7 @@
|
|
|
1348
1285
|
"target-sha",
|
|
1349
1286
|
"trusted-publishing"
|
|
1350
1287
|
],
|
|
1351
|
-
"inputCount":
|
|
1288
|
+
"inputCount": 55,
|
|
1352
1289
|
"secrets": [
|
|
1353
1290
|
"BUILDCHAIN_ISSUE_APP_ID",
|
|
1354
1291
|
"BUILDCHAIN_ISSUE_APP_PRIVATE_KEY",
|
|
@@ -182,42 +182,40 @@ on:
|
|
|
182
182
|
|
|
183
183
|
jobs:
|
|
184
184
|
paper-release:
|
|
185
|
-
uses: kungfu-systems/buildchain/.github/workflows/paper-release
|
|
185
|
+
uses: kungfu-systems/buildchain/.github/workflows/paper-release.yml@v2
|
|
186
186
|
permissions:
|
|
187
|
-
actions: read
|
|
188
187
|
checks: write
|
|
189
188
|
contents: write
|
|
190
189
|
id-token: write
|
|
191
190
|
issues: write
|
|
192
191
|
with:
|
|
193
192
|
buildchain-ref: ${{ inputs.buildchain-ref || '' }}
|
|
194
|
-
|
|
193
|
+
publication-admission-json: ${{ needs.authority.outputs.admission-json }}
|
|
194
|
+
publication-runner-provenance-json: ${{ needs.authority.outputs.runner-provenance-json }}
|
|
195
|
+
publication-control-plane-audit-json: ${{ needs.authority.outputs.control-plane-audit-json }}
|
|
196
|
+
publication-expected-json: ${{ needs.authority.outputs.expected-json }}
|
|
195
197
|
toolchain-type: config
|
|
196
198
|
verify-command: make check
|
|
197
|
-
artifact-paths: _build/paper-name.pdf
|
|
198
199
|
buildchain-contract-lock-path: .buildchain/contract-lock.json
|
|
199
|
-
secrets:
|
|
200
|
-
BUILDCHAIN_PROMOTION_TOKEN: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}
|
|
201
200
|
```
|
|
202
201
|
|
|
203
|
-
The
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
admitted candidate, recomputes the capability binding, and publishes without
|
|
212
|
-
executing consumer build commands. npm binds the OIDC identity to the consumer
|
|
213
|
-
workflow named by `publisher-workflow-path`.
|
|
202
|
+
The publication job does not accept a long-lived promotion token. The caller
|
|
203
|
+
must first produce a fresh sealed admission, runner provenance, external
|
|
204
|
+
control-plane audit, and exact expected bindings. The credential-free verifier
|
|
205
|
+
job checks those receipts; only then can the publication job use its short-lived
|
|
206
|
+
`github.token` and caller-bound OIDC trusted publisher identity. npm binds that
|
|
207
|
+
identity to the consumer workflow filename; an npm Environment restriction is
|
|
208
|
+
optional and must be represented explicitly when configured. The workflow fails
|
|
209
|
+
before the publication build when the target branch protection cannot be read.
|
|
214
210
|
|
|
215
211
|
The preset:
|
|
216
212
|
|
|
217
|
-
- resolves the floating Buildchain runtime
|
|
218
|
-
|
|
213
|
+
- resolves the same floating Buildchain runtime and contract lock as the build
|
|
214
|
+
workflow;
|
|
215
|
+
- verifies that the declared promotion authority can read the protected target
|
|
216
|
+
channel before starting the publication build;
|
|
219
217
|
- builds the PDF through the declared pinned LaTeX Docker toolchain or custom
|
|
220
|
-
command
|
|
218
|
+
command;
|
|
221
219
|
- verifies the paper repository;
|
|
222
220
|
- writes the publication manifest, publication passport, optional archive
|
|
223
221
|
registry, and source bundle;
|
|
@@ -228,8 +226,7 @@ The preset:
|
|
|
228
226
|
- creates a `publish-gate/<alpha|release>/.../<version>` source lock for the
|
|
229
227
|
channel commit and requires `promote-buildchain-ref` to verify that lock
|
|
230
228
|
before any publish side effect;
|
|
231
|
-
-
|
|
232
|
-
package through npm Trusted Publishing without rebuilding it;
|
|
229
|
+
- publishes the package through npm Trusted Publishing;
|
|
233
230
|
- writes Buildchain release/passport evidence; and
|
|
234
231
|
- creates or updates the exact-version GitHub Release by default, uploading
|
|
235
232
|
every file declared by `publication.primary_artifact` and
|
|
@@ -109,20 +109,17 @@ artifacts. The denial explicitly records that npm Trusted Publishing and OIDC
|
|
|
109
109
|
were not evaluated, so downstream diagnostics cannot misclassify an admission
|
|
110
110
|
assembly failure as an npm authentication failure.
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
Buildchain's own `workflow_run` promotion lane may assemble those inputs only
|
|
113
|
+
for `kungfu-systems/buildchain`. It downloads the exact prior RC passport,
|
|
114
114
|
summary, referenced controller receipt, manifests, and product payloads; proves
|
|
115
115
|
the admitted channel commit has the same Git tree as the RC; performs the live
|
|
116
116
|
read-only control-plane audit; records the GitHub-hosted job as ephemeral runner
|
|
117
|
-
provenance; and
|
|
118
|
-
caller no-Gate decision. The
|
|
117
|
+
provenance; and creates an explicit Buildchain-owned no-Gate decision. The
|
|
119
118
|
independent verifier then recomputes every receipt and payload digest exactly as
|
|
120
|
-
it does for externally supplied admission.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
Manual apply and consumers that do not opt in still require their own explicit
|
|
125
|
-
admission inputs.
|
|
119
|
+
it does for externally supplied admission. The self-assembly mode rejects other
|
|
120
|
+
repositories, unknown refs, non-exact source SHAs, and any caller other than
|
|
121
|
+
`.github/workflows/buildchain-ref-promotion.yml`. Manual apply and external
|
|
122
|
+
consumer workflows still require their own explicit admission inputs.
|
|
126
123
|
|
|
127
124
|
Before authority verification, the reusable promotion controller runs the same
|
|
128
125
|
release transaction selector in read-only mode. That plan supplies one exact
|
|
@@ -116,22 +116,3 @@ KFD-1 source/artifact contract surfaces, audits KFD-2 public release claims, and
|
|
|
116
116
|
compares KFD-3 declared shipped public surfaces with artifact-exposed public
|
|
117
117
|
surfaces. The release passport records the results under `kfd-1`, `kfd-2`, and
|
|
118
118
|
the KFD-provided `kfd-3` section.
|
|
119
|
-
|
|
120
|
-
Managed consumers may also ask the promotion wrapper to assemble sealed
|
|
121
|
-
publication evidence from the exact release candidate instead of producing
|
|
122
|
-
short-lived admission JSON in repository-specific workflow code:
|
|
123
|
-
|
|
124
|
-
```yaml
|
|
125
|
-
publication-auto-admission: true
|
|
126
|
-
publication-auto-no-gate: true
|
|
127
|
-
publication-publisher-workflow-path: .github/workflows/buildchain-ref-promotion.yml
|
|
128
|
-
publication-product: Example Product
|
|
129
|
-
publication-target: npm:@example/product
|
|
130
|
-
publication-package-name: "@example/product"
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
`publication-auto-no-gate` is an explicit consumer decision, not a default. A
|
|
134
|
-
consumer with a Shifu Gate registry supplies `publication-gate-aggregate-json`
|
|
135
|
-
instead. Buildchain still requires caller-owned RC evidence, an exact authority
|
|
136
|
-
runtime and source SHA, a repository-local publisher workflow, matching npm
|
|
137
|
-
target/package identity, and a qualifying control-plane audit.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "2.12.7-alpha.
|
|
3
|
+
"version": "2.12.7-alpha.4",
|
|
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",
|
|
@@ -27,7 +27,6 @@ const DESCRIPTORS = Object.freeze([
|
|
|
27
27
|
[".github/workflows/buildchain-stable-candidate-patrol.yml", "governance-write"],
|
|
28
28
|
[".github/workflows/dev-pr-auto-merge.yml", "governance-write"],
|
|
29
29
|
[".github/workflows/npm-publish.yml", "dry-run-only"],
|
|
30
|
-
[".github/workflows/paper-release-sealed.yml", "product-publication", true, ["npm-publish", "github-release"], "trusted-publishing", "none", "fixed", "caller-bound"],
|
|
31
30
|
[".github/workflows/paper-release.yml", "product-publication", true, ["npm-publish", "github-release"], "trusted-publishing", "none", "fixed", "caller-bound"],
|
|
32
31
|
[".github/workflows/patrol-daily.yml", "governance-write"],
|
|
33
32
|
[".github/workflows/patrol-monthly.yml", "governance-write"],
|
|
@@ -303,7 +303,7 @@ function normalizeEvidence(entries = []) {
|
|
|
303
303
|
function receiptStatus(plan, stages) {
|
|
304
304
|
if (stages.some((stage) => stage.status === "failed")) return "failed";
|
|
305
305
|
if (stages.length > 0 && stages.every((stage) => stage.status === "skipped")) return "skipped";
|
|
306
|
-
if (stages.some((stage) => stage.status === "cancelled" ||
|
|
306
|
+
if (stages.some((stage) => stage.status === "cancelled" || stage.status === "missing")) return "partial";
|
|
307
307
|
const required = new Set(plan.expected.stages.filter((stage) => stage.required).map((stage) => stage.id));
|
|
308
308
|
if (stages.filter((stage) => required.has(stage.id)).every((stage) => stage.status === "passed")) return "passed";
|
|
309
309
|
return "partial";
|
package/packages/core/index.js
CHANGED
|
@@ -153,13 +153,6 @@ export {
|
|
|
153
153
|
verifyPublicationAdmission,
|
|
154
154
|
} from "./publication-authority.js";
|
|
155
155
|
|
|
156
|
-
export {
|
|
157
|
-
PUBLICATION_ARTIFACT_CANDIDATE_CONTRACT,
|
|
158
|
-
createPublicationArtifactCandidate,
|
|
159
|
-
publicationArtifactCandidateDigest,
|
|
160
|
-
resolvePublicationCandidateFile,
|
|
161
|
-
} from "./publication-artifact-candidate.js";
|
|
162
|
-
|
|
163
156
|
export {
|
|
164
157
|
buildchainPublicationAuthorityDescriptors,
|
|
165
158
|
createBuildchainPublicationAuthorityRegistry,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
|
|
3
3
|
import { validateControllerReceipt } from "./controller-evidence.js";
|
|
4
|
-
import { createPublicationArtifactCandidate } from "./publication-artifact-candidate.js";
|
|
5
4
|
import { sha256Json, validateReleaseCandidatePassport } from "./release-candidate.js";
|
|
6
5
|
|
|
7
6
|
export const PUBLICATION_AUTHORITY_REGISTRY_CONTRACT =
|
|
@@ -382,7 +381,6 @@ export function createPublicationArtifactManifestSet({
|
|
|
382
381
|
}
|
|
383
382
|
|
|
384
383
|
function validateGateAggregate(gateAggregate, { admission, passport }) {
|
|
385
|
-
const passportSourceSha = passport.source?.headSha || passport.source?.sha;
|
|
386
384
|
if (gateAggregate?.contract === "buildchain.shifu-gate-aggregate/v1") {
|
|
387
385
|
const { digest, ...payload } = gateAggregate;
|
|
388
386
|
const actualDigest = sha256Json(payload);
|
|
@@ -392,7 +390,7 @@ function validateGateAggregate(gateAggregate, { admission, passport }) {
|
|
|
392
390
|
if (gateAggregate.status !== "pass" || gateAggregate.qualifying !== true) {
|
|
393
391
|
throw new Error("gate aggregate is not qualifying");
|
|
394
392
|
}
|
|
395
|
-
if (![admission.sourceSha,
|
|
393
|
+
if (![admission.sourceSha, passport.source?.headSha].includes(gateAggregate.sourceSha)) {
|
|
396
394
|
throw new Error("gate aggregate source SHA mismatch");
|
|
397
395
|
}
|
|
398
396
|
normalizeDigest(gateAggregate.registry?.digest, "gateAggregate.registry.digest");
|
|
@@ -410,7 +408,7 @@ function validateGateAggregate(gateAggregate, { admission, passport }) {
|
|
|
410
408
|
if (gateAggregate.required !== false) {
|
|
411
409
|
throw new Error("a required Gate policy must supply a qualifying Shifu Gate aggregate");
|
|
412
410
|
}
|
|
413
|
-
if (![admission.sourceSha,
|
|
411
|
+
if (![admission.sourceSha, passport.source?.headSha].includes(gateAggregate.sourceSha)) {
|
|
414
412
|
throw new Error("publication Gate decision source SHA mismatch");
|
|
415
413
|
}
|
|
416
414
|
const normalizedPolicy = {
|
|
@@ -430,43 +428,6 @@ function validatePublicationEvidence(publicationEvidence, admission) {
|
|
|
430
428
|
if (!publicationEvidence || typeof publicationEvidence !== "object" || Array.isArray(publicationEvidence)) {
|
|
431
429
|
throw new Error("independent publication evidence is required");
|
|
432
430
|
}
|
|
433
|
-
if (publicationEvidence.publicationArtifactCandidate) {
|
|
434
|
-
const candidate = createPublicationArtifactCandidate(publicationEvidence.publicationArtifactCandidate);
|
|
435
|
-
const passport = publicationEvidence.publicationArtifactCandidate.passport;
|
|
436
|
-
const controllerReceipt = publicationEvidence.publicationArtifactCandidate.controllerReceipt;
|
|
437
|
-
const controllerReceiptDigest = normalizeDigest(controllerReceipt.digest, "controllerReceipt.digest");
|
|
438
|
-
if (controllerReceiptDigest !== normalizeDigest(admission.controllerReceiptDigest, "admission.controllerReceiptDigest")) {
|
|
439
|
-
throw new Error("controller receipt evidence binding mismatch");
|
|
440
|
-
}
|
|
441
|
-
const contractDigest = normalizeDigest(controllerReceipt.runtime?.contractDigest, "controllerReceipt.runtime.contractDigest");
|
|
442
|
-
if (contractDigest !== normalizeDigest(admission.contractDigest, "admission.contractDigest")) {
|
|
443
|
-
throw new Error("runtime contract evidence binding mismatch");
|
|
444
|
-
}
|
|
445
|
-
const gate = validateGateAggregate(publicationEvidence.gateAggregate, { admission, passport });
|
|
446
|
-
if (gate.gateAggregateDigest !== normalizeDigest(admission.gateAggregateDigest, "admission.gateAggregateDigest")) {
|
|
447
|
-
throw new Error("Gate aggregate evidence binding mismatch");
|
|
448
|
-
}
|
|
449
|
-
if (gate.policyDigest !== normalizeDigest(admission.policyDigest, "admission.policyDigest")) {
|
|
450
|
-
throw new Error("Gate policy evidence binding mismatch");
|
|
451
|
-
}
|
|
452
|
-
if (candidate.candidateDigest !== normalizeDigest(admission.artifactDigest, "admission.artifactDigest")) {
|
|
453
|
-
throw new Error("publication artifact candidate evidence binding mismatch");
|
|
454
|
-
}
|
|
455
|
-
return {
|
|
456
|
-
sourceTreeSha: candidate.sourceTreeSha,
|
|
457
|
-
controllerReceiptDigest,
|
|
458
|
-
contractDigest,
|
|
459
|
-
gateAggregateDigest: gate.gateAggregateDigest,
|
|
460
|
-
policyDigest: gate.policyDigest,
|
|
461
|
-
artifactDigest: candidate.candidateDigest,
|
|
462
|
-
evidenceDigest: publicationAuthorityDigest({
|
|
463
|
-
sourceTreeSha: candidate.sourceTreeSha,
|
|
464
|
-
controllerReceiptDigest,
|
|
465
|
-
gateAggregateDigest: gate.gateAggregateDigest,
|
|
466
|
-
artifactDigest: candidate.candidateDigest,
|
|
467
|
-
}),
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
431
|
const passport = publicationEvidence.releaseCandidatePassport;
|
|
471
432
|
const buildSummary = publicationEvidence.buildSummary;
|
|
472
433
|
const passportValidation = validateReleaseCandidatePassport({
|
|
@@ -16,7 +16,6 @@ export function evaluatePublicationControlPlaneSnapshot({
|
|
|
16
16
|
branch,
|
|
17
17
|
packageName,
|
|
18
18
|
publisherMode = "npm-trusted-publisher",
|
|
19
|
-
requiredStatusCheck = "check",
|
|
20
19
|
snapshot,
|
|
21
20
|
observedAt,
|
|
22
21
|
expiresAt,
|
|
@@ -73,8 +72,7 @@ export function evaluatePublicationControlPlaneSnapshot({
|
|
|
73
72
|
branchPolicy.protected === true &&
|
|
74
73
|
branchPolicy.enforcementLevel === "everyone" &&
|
|
75
74
|
Array.isArray(branchPolicy.requiredStatusChecks) &&
|
|
76
|
-
branchPolicy.
|
|
77
|
-
branchPolicy.requiredStatusChecks.includes(requiredStatusCheck) &&
|
|
75
|
+
branchPolicy.requiredStatusChecks.includes("check") &&
|
|
78
76
|
branchPolicy.requiredCheckPassed === true &&
|
|
79
77
|
branchPolicy.sourceSha === branchPolicy.headSha &&
|
|
80
78
|
branchPolicy.mergedPullRequest === true &&
|
|
@@ -115,22 +115,13 @@ async function main() {
|
|
|
115
115
|
manifests,
|
|
116
116
|
payloads: manifests.map((manifest) => payloadFor(manifest, path.join(evidenceRoot, "payloads"))),
|
|
117
117
|
});
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
gateAggregate = createPublicationGateDecision({
|
|
127
|
-
sourceSha,
|
|
128
|
-
profile: process.env.BUILDCHAIN_GATE_PROFILE || "managed-release-candidate-no-gate",
|
|
129
|
-
required: false,
|
|
130
|
-
rationale: process.env.BUILDCHAIN_GATE_RATIONALE || "The consumer explicitly declared no Shifu Gate registry for this publication transaction.",
|
|
131
|
-
policy: { scope: "managed-release-candidate", repository },
|
|
132
|
-
});
|
|
133
|
-
}
|
|
118
|
+
const gateAggregate = createPublicationGateDecision({
|
|
119
|
+
sourceSha,
|
|
120
|
+
profile: process.env.BUILDCHAIN_GATE_PROFILE || "buildchain-self-publication",
|
|
121
|
+
required: false,
|
|
122
|
+
rationale: process.env.BUILDCHAIN_GATE_RATIONALE || "Buildchain self-publication has no consumer-owned Shifu Gate registry.",
|
|
123
|
+
policy: { scope: "buildchain-self-publication", repository },
|
|
124
|
+
});
|
|
134
125
|
const runnerProvenance = createRunnerProvenance({
|
|
135
126
|
runnerClass: "ephemeral",
|
|
136
127
|
os: required("RUNNER_OS"),
|
|
@@ -187,6 +178,6 @@ async function main() {
|
|
|
187
178
|
}
|
|
188
179
|
|
|
189
180
|
main().catch((error) => {
|
|
190
|
-
console.error(`assemble
|
|
181
|
+
console.error(`assemble self publication admission: ${error.message}`);
|
|
191
182
|
process.exitCode = 1;
|
|
192
183
|
});
|
|
@@ -191,7 +191,6 @@ function main() {
|
|
|
191
191
|
const workflowPath = flag("workflow", ".github/workflows/release-candidate-promote.yml");
|
|
192
192
|
const workflowRef = flag("workflow-ref");
|
|
193
193
|
const publisherWorkflowPath = flag("publisher-workflow", workflowPath);
|
|
194
|
-
const requiredStatusCheck = flag("required-status-check", "check");
|
|
195
194
|
const jobId = flag("job", "promote");
|
|
196
195
|
const environment = flag("environment", "none");
|
|
197
196
|
const providerEnvironment = environment === "none" ? "" : environment;
|
|
@@ -281,16 +280,15 @@ function main() {
|
|
|
281
280
|
const checkRuns = githubJson(`repos/${repository}/commits/${sourceSha}/check-runs?per_page=100`, "source check runs");
|
|
282
281
|
const requiredStatusCheckPolicy = branchState.protection?.required_status_checks || {};
|
|
283
282
|
const requiredStatusChecks = requiredStatusCheckPolicy.contexts || [];
|
|
284
|
-
const requiredCheckSource = (requiredStatusCheckPolicy.checks || []).find((entry) => entry.context ===
|
|
283
|
+
const requiredCheckSource = (requiredStatusCheckPolicy.checks || []).find((entry) => entry.context === "check");
|
|
285
284
|
branchPolicy = {
|
|
286
285
|
ref: branch,
|
|
287
286
|
policyMode: "provider-enforced-transaction",
|
|
288
287
|
protected: branchState.protected === true,
|
|
289
288
|
enforcementLevel: branchState.protection?.required_status_checks?.enforcement_level || "",
|
|
290
289
|
requiredStatusChecks,
|
|
291
|
-
requiredStatusCheck,
|
|
292
290
|
requiredCheckPassed: (checkRuns.check_runs || []).some((entry) =>
|
|
293
|
-
entry.name ===
|
|
291
|
+
entry.name === "check" &&
|
|
294
292
|
entry.conclusion === "success" &&
|
|
295
293
|
(!requiredCheckSource?.app_id || entry.app?.id === requiredCheckSource.app_id)
|
|
296
294
|
),
|
|
@@ -352,7 +350,6 @@ function main() {
|
|
|
352
350
|
branch,
|
|
353
351
|
packageName,
|
|
354
352
|
publisherMode,
|
|
355
|
-
requiredStatusCheck,
|
|
356
353
|
observedAt: observedAt.toISOString(),
|
|
357
354
|
expiresAt: expiresAt.toISOString(),
|
|
358
355
|
snapshot: {
|
|
@@ -6,7 +6,6 @@ import path from "node:path";
|
|
|
6
6
|
import { pathToFileURL } from "node:url";
|
|
7
7
|
|
|
8
8
|
export const LOCKED_SOURCE_CHECKOUT_CONTRACT = "kungfu-buildchain-locked-source-checkout-cache";
|
|
9
|
-
export const ISOLATED_GIT_GLOBAL_CONFIG = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
10
9
|
|
|
11
10
|
const GIT_SHA_PATTERN = /^[0-9a-f]{40}$/i;
|
|
12
11
|
|
|
@@ -134,24 +133,6 @@ function githubAuthEnv(token = "") {
|
|
|
134
133
|
};
|
|
135
134
|
}
|
|
136
135
|
|
|
137
|
-
function isolatedGitFetchEnv(env = {}, targetPath) {
|
|
138
|
-
const configuredCount = Number.parseInt(env.GIT_CONFIG_COUNT || "0", 10);
|
|
139
|
-
const safeDirectoryIndex = Number.isInteger(configuredCount) && configuredCount >= 0
|
|
140
|
-
? configuredCount
|
|
141
|
-
: 0;
|
|
142
|
-
return {
|
|
143
|
-
...env,
|
|
144
|
-
// Runner-global URL rewrites are shared mutable state. A concurrent job
|
|
145
|
-
// may point the same repository URL at a different single-SHA bundle, so
|
|
146
|
-
// network fetches must not consult the account-level Git config.
|
|
147
|
-
GIT_CONFIG_GLOBAL: ISOLATED_GIT_GLOBAL_CONFIG,
|
|
148
|
-
GIT_CONFIG_NOSYSTEM: "1",
|
|
149
|
-
GIT_CONFIG_COUNT: String(safeDirectoryIndex + 1),
|
|
150
|
-
[`GIT_CONFIG_KEY_${safeDirectoryIndex}`]: "safe.directory",
|
|
151
|
-
[`GIT_CONFIG_VALUE_${safeDirectoryIndex}`]: path.resolve(targetPath),
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
136
|
function markSafeDirectory(targetPath, timeoutMs) {
|
|
156
137
|
try {
|
|
157
138
|
git(["config", "--global", "--add", "safe.directory", path.resolve(targetPath)], {
|
|
@@ -222,32 +203,9 @@ export function fetchSourceCommit({
|
|
|
222
203
|
sourceTreeSha = "",
|
|
223
204
|
timeoutMs,
|
|
224
205
|
env = {},
|
|
225
|
-
allowFullFetchRetry = false,
|
|
226
206
|
runGit = git,
|
|
227
207
|
containsCommit = hasCommit,
|
|
228
208
|
}) {
|
|
229
|
-
const fetchEnv = isolatedGitFetchEnv(env, targetPath);
|
|
230
|
-
const fetch = (refspec) => {
|
|
231
|
-
const options = { cwd: targetPath, timeoutMs, env: fetchEnv };
|
|
232
|
-
try {
|
|
233
|
-
runGit(["fetch", "--no-tags", "--depth=1", remoteName, refspec], options);
|
|
234
|
-
return "shallow";
|
|
235
|
-
} catch (error) {
|
|
236
|
-
if (
|
|
237
|
-
!allowFullFetchRetry
|
|
238
|
-
|| !/dumb http transport does not support shallow capabilities/i.test(
|
|
239
|
-
String(error?.message || error || ""),
|
|
240
|
-
)
|
|
241
|
-
) {
|
|
242
|
-
throw error;
|
|
243
|
-
}
|
|
244
|
-
// Dumb HTTP mirrors are intentionally simple static cache endpoints.
|
|
245
|
-
// Retry only that explicit capability mismatch without --depth; all
|
|
246
|
-
// network fallbacks remain shallow and bounded by their own policy.
|
|
247
|
-
runGit(["fetch", "--no-tags", remoteName, refspec], options);
|
|
248
|
-
return "full";
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
209
|
try {
|
|
252
210
|
runGit(["remote", "remove", remoteName], { cwd: targetPath, timeoutMs, stdio: "ignore" });
|
|
253
211
|
} catch {
|
|
@@ -261,9 +219,13 @@ export function fetchSourceCommit({
|
|
|
261
219
|
|
|
262
220
|
if (fetchRef) {
|
|
263
221
|
try {
|
|
264
|
-
|
|
222
|
+
runGit(["fetch", "--no-tags", "--depth=1", remoteName, `+${fetchRef}:refs/buildchain/source-ref`], {
|
|
223
|
+
cwd: targetPath,
|
|
224
|
+
timeoutMs,
|
|
225
|
+
env,
|
|
226
|
+
});
|
|
265
227
|
if (containsCommit(targetPath, sha, timeoutMs)) {
|
|
266
|
-
return { selector: "ref", checkoutSha: sha
|
|
228
|
+
return { selector: "ref", checkoutSha: sha };
|
|
267
229
|
}
|
|
268
230
|
if (/^refs\/pull\/\d+\/merge$/.test(fetchRef) && sourceTreeSha) {
|
|
269
231
|
const fetchedSha = runGit(["rev-parse", "refs/buildchain/source-ref^{commit}"], {
|
|
@@ -275,7 +237,7 @@ export function fetchSourceCommit({
|
|
|
275
237
|
timeoutMs,
|
|
276
238
|
});
|
|
277
239
|
if (fetchedTree === sourceTreeSha) {
|
|
278
|
-
return { selector: "ref-tree", checkoutSha: fetchedSha
|
|
240
|
+
return { selector: "ref-tree", checkoutSha: fetchedSha };
|
|
279
241
|
}
|
|
280
242
|
}
|
|
281
243
|
} catch (error) {
|
|
@@ -287,11 +249,15 @@ export function fetchSourceCommit({
|
|
|
287
249
|
}
|
|
288
250
|
}
|
|
289
251
|
|
|
290
|
-
|
|
252
|
+
runGit(["fetch", "--no-tags", "--depth=1", remoteName, `+${sha}:refs/buildchain/source`], {
|
|
253
|
+
cwd: targetPath,
|
|
254
|
+
timeoutMs,
|
|
255
|
+
env,
|
|
256
|
+
});
|
|
291
257
|
if (!containsCommit(targetPath, sha, timeoutMs)) {
|
|
292
258
|
throw new Error(`fetched ${fetchRef || sha}, but ${sha} is not available`);
|
|
293
259
|
}
|
|
294
|
-
return { selector: "sha", checkoutSha: sha
|
|
260
|
+
return { selector: "sha", checkoutSha: sha };
|
|
295
261
|
}
|
|
296
262
|
|
|
297
263
|
export function runBoundedFetch({ attempts = 1, fetch, onAttempt = () => {}, onRetry = () => {}, shouldRetry = () => true }) {
|
|
@@ -447,9 +413,7 @@ export function lockedSourceCheckout({
|
|
|
447
413
|
fetchRef,
|
|
448
414
|
sourceTreeSha: treeSha,
|
|
449
415
|
timeoutMs,
|
|
450
|
-
allowFullFetchRetry: true,
|
|
451
416
|
});
|
|
452
|
-
evidence.cache.fetchMode = fetchResult.fetchMode;
|
|
453
417
|
checkoutSha = fetchResult.checkoutSha || sha;
|
|
454
418
|
checkoutFetchedCommit(targetPath, checkoutSha, timeoutMs);
|
|
455
419
|
} else {
|
|
@@ -494,7 +458,6 @@ export function lockedSourceCheckout({
|
|
|
494
458
|
shouldRetry: retryableGitFetchError,
|
|
495
459
|
});
|
|
496
460
|
evidence.cache.githubFetchAttempts = fetchResult.attempts;
|
|
497
|
-
evidence.cache.fetchMode = fetchResult.value.fetchMode;
|
|
498
461
|
checkoutSha = fetchResult.value.checkoutSha || sha;
|
|
499
462
|
} catch (error) {
|
|
500
463
|
evidence.durationMs = Date.now() - startedAt;
|