@kungfu-tech/buildchain 2.8.0 → 2.8.1

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.
@@ -87,6 +87,9 @@ if (rootPackage.exports?.["."] !== "./packages/core/index.js") {
87
87
  if (rootPackage.exports?.["./diagnostics"] !== "./packages/core/diagnostics.js") {
88
88
  throw new Error("root package must export @kungfu-tech/buildchain/diagnostics");
89
89
  }
90
+ if (rootPackage.exports?.["./buildchain-contract"] !== "./packages/core/buildchain-contract.js") {
91
+ throw new Error("root package must export @kungfu-tech/buildchain/buildchain-contract");
92
+ }
90
93
  if (rootPackage.exports?.["./issue-reporting"] !== "./packages/core/issue-reporting.js") {
91
94
  throw new Error("root package must export @kungfu-tech/buildchain/issue-reporting");
92
95
  }
@@ -141,6 +144,12 @@ if (!coreIndexSource.includes("collectBuildchainDiagnostics")) {
141
144
  if (!coreIndexSource.includes("reportBuildchainIssue")) {
142
145
  throw new Error("packages/core/index.js must export reportBuildchainIssue");
143
146
  }
147
+ if (!coreIndexSource.includes("createBuildchainContractWorld")) {
148
+ throw new Error("packages/core/index.js must export Buildchain contract lock APIs");
149
+ }
150
+ if (!coreIndexSource.includes("KFD2_RELEASE_TRUST_PASSPORT_CONTRACT")) {
151
+ throw new Error("packages/core/index.js must export KFD-2 release trust passport contract");
152
+ }
144
153
  for (const requiredSnippet of [
145
154
  "createKfd1ReleaseGateEvidence",
146
155
  "resolveKfd1Metadata",
@@ -150,6 +159,15 @@ for (const requiredSnippet of [
150
159
  throw new Error(`packages/core/index.js must export KFD-1 gate API: ${requiredSnippet}`);
151
160
  }
152
161
  }
162
+ for (const requiredSnippet of [
163
+ "createKfd3CollaborationInterfaceReleaseGateEvidence",
164
+ "resolveKfd3Metadata",
165
+ "validateKfd3CollaborationInterfaceReleaseGateEvidence",
166
+ ]) {
167
+ if (!coreIndexSource.includes(requiredSnippet)) {
168
+ throw new Error(`packages/core/index.js must export KFD-3 gate API: ${requiredSnippet}`);
169
+ }
170
+ }
153
171
  for (const requiredSnippet of [
154
172
  "surfaceImpacts[]",
155
173
  "versionImpact.final",
@@ -168,15 +186,35 @@ for (const requiredSnippet of [
168
186
  const releasePassportDoc = fs.readFileSync(path.join(root, "docs/release-passport.md"), "utf8");
169
187
  for (const requiredSnippet of [
170
188
  "--kfd-1-witness-json",
189
+ "--kfd-2-claim-json",
171
190
  "@kungfu-tech/kfd",
172
191
  "currently named `kfd-1`",
173
192
  "Buildchain formatting policy",
174
193
  "Verification fails closed",
194
+ "KFD-2 release trust passport audit",
195
+ "Unbound public claims fail",
196
+ "Floating Buildchain contract lock",
197
+ "buildchain.contract-lock.json",
198
+ "--kfd-3-prebuild-witness-json",
199
+ "--kfd-3-artifact-verify-cmd",
200
+ "currently named `kfd-3`",
201
+ "KFD-3 collaboration-interface release gate",
175
202
  ]) {
176
203
  if (!releasePassportDoc.includes(requiredSnippet)) {
177
204
  throw new Error(`release passport doc missing KFD-1 gate snippet: ${requiredSnippet}`);
178
205
  }
179
206
  }
207
+ const reusableBuildSurfaceDoc = fs.readFileSync(path.join(root, "docs/reusable-build-surface.md"), "utf8");
208
+ for (const requiredSnippet of [
209
+ "Floating Ref Contract Lock",
210
+ "dist/site/buildchain-contract.json",
211
+ "buildchain-contract-drift-issue-mode",
212
+ "compatible drift",
213
+ ]) {
214
+ if (!reusableBuildSurfaceDoc.includes(requiredSnippet)) {
215
+ throw new Error(`reusable build surface doc missing contract lock snippet: ${requiredSnippet}`);
216
+ }
217
+ }
180
218
  for (const [docName, docSource] of Object.entries({ "docs/cli.md": cliDoc, "docs/install.md": installDoc })) {
181
219
  for (const requiredSnippet of [
182
220
  "minimumReleaseAgeExclude",
@@ -252,10 +290,14 @@ for (const requiredSnippet of [
252
290
  "uses: ./.github/workflows/release-candidate-promote.yml",
253
291
  "github.event.workflow_run.event == 'push'",
254
292
  "target-sha: ${{ github.event.workflow_run.head_sha || inputs.sha || github.sha }}",
293
+ "github-release: true",
255
294
  "publish-required-artifacts-json: \"[]\"",
256
295
  "release-passport-impact-json: >-",
257
- "Buildchain v2.8 promotes KFD-1 contract-world release gates",
296
+ "Buildchain v2.8 promotes KFD self-verification",
258
297
  "kfd-1-contract-world-release-gate",
298
+ "kfd-2-release-trust-passport-audit",
299
+ "kfd-3-collaboration-interface-trust-proof",
300
+ "publish-source-lock-enforcement",
259
301
  "required-check-protection",
260
302
  "\"surfaceImpacts\":[",
261
303
  ]) {
@@ -267,9 +309,69 @@ const releaseCandidatePromoteWorkflow = fs.readFileSync(path.join(root, ".github
267
309
  for (const requiredSnippet of [
268
310
  "release-passport-kfd-1-witness-jsons:",
269
311
  "release-passport-kfd-1-witness-jsons: ${{ inputs.release-passport-kfd-1-witness-jsons }}",
312
+ "release-passport-kfd-2-claim-jsons:",
313
+ "release-passport-kfd-2-claim-jsons: ${{ inputs.release-passport-kfd-2-claim-jsons }}",
314
+ "release-passport-kfd-3-prebuild-witness-jsons:",
315
+ "release-passport-kfd-3-prebuild-witness-jsons: ${{ inputs.release-passport-kfd-3-prebuild-witness-jsons }}",
316
+ "release-passport-kfd-3-artifact-witness-jsons:",
317
+ "release-passport-kfd-3-artifact-verify-command:",
318
+ "github-release:",
319
+ "Publish GitHub Release evidence",
320
+ "scripts/ensure-github-release.mjs",
321
+ "gh release upload \"${RELEASE_TAG}\" \"${upload_args[@]}\" --clobber",
322
+ "require-publish-source-lock: \"true\"",
323
+ "publish-source-ref: ${{ steps.publish-gate.outputs.ref }}",
324
+ "publish-source-sha: ${{ steps.publish-gate.outputs.sha }}",
325
+ "publish-source-locked: ${{ steps.publish-gate.outputs.locked }}",
270
326
  ]) {
271
327
  if (!releaseCandidatePromoteWorkflow.includes(requiredSnippet)) {
272
- throw new Error(`release candidate promote workflow missing KFD-1 gate pass-through: ${requiredSnippet}`);
328
+ throw new Error(`release candidate promote workflow missing KFD gate pass-through: ${requiredSnippet}`);
329
+ }
330
+ }
331
+ const workflowDir = path.join(root, ".github/workflows");
332
+ for (const workflowFile of fs.readdirSync(workflowDir).filter((entry) => entry.endsWith(".yml"))) {
333
+ const workflowPath = path.join(workflowDir, workflowFile);
334
+ const workflowSource = fs.readFileSync(workflowPath, "utf8");
335
+ if (!/uses:\s*(?:\.\/\.buildchain\/runtime\/actions\/promote-buildchain-ref|\.\/actions\/promote-buildchain-ref|.*\/actions\/promote-buildchain-ref(?:@|$))/m.test(workflowSource)) {
336
+ continue;
337
+ }
338
+ for (const requiredSnippet of [
339
+ "require-publish-source-lock:",
340
+ "publish-source-ref:",
341
+ "publish-source-sha:",
342
+ "publish-source-locked:",
343
+ ]) {
344
+ if (!workflowSource.includes(requiredSnippet)) {
345
+ throw new Error(`${workflowFile} calls promote-buildchain-ref without publish source-lock input: ${requiredSnippet}`);
346
+ }
347
+ }
348
+ }
349
+ for (const retiredWorkflow of [
350
+ ".release-new-version.yml",
351
+ ".release-elastic-beanstalk.yml",
352
+ ".sam-release.yml",
353
+ ".wheel-release.yml",
354
+ ]) {
355
+ const retiredSource = fs.readFileSync(path.join(workflowDir, retiredWorkflow), "utf8");
356
+ for (const requiredSnippet of [
357
+ "release path is retired",
358
+ "release-candidate-promote.yml@v2",
359
+ "publish-gate source-lock enforcement",
360
+ ]) {
361
+ if (!retiredSource.includes(requiredSnippet)) {
362
+ throw new Error(`${retiredWorkflow} must fail closed and point callers at the source-locked release-candidate-promote model: ${requiredSnippet}`);
363
+ }
364
+ }
365
+ for (const forbiddenSnippet of [
366
+ "npm publish --access=public",
367
+ "actions/publish-prebuilt@v2",
368
+ "actions/bump-version@v2",
369
+ "beanstalk-deploy@",
370
+ "sam deploy",
371
+ ]) {
372
+ if (retiredSource.includes(forbiddenSnippet)) {
373
+ throw new Error(`${retiredWorkflow} must not keep retired publish side effects: ${forbiddenSnippet}`);
374
+ }
273
375
  }
274
376
  }
275
377
  for (const forbiddenSnippet of [
@@ -388,7 +490,7 @@ for (const artifact of [
388
490
  }
389
491
  }
390
492
 
391
- for (const siteFile of ["buildchain-site.json", "site-manifest.json", "cli-registry.json", "release-model.json"]) {
493
+ for (const siteFile of ["buildchain-site.json", "site-manifest.json", "cli-registry.json", "release-model.json", "buildchain-contract.json"]) {
392
494
  if (!fs.existsSync(path.join(root, "dist", "site", siteFile))) {
393
495
  throw new Error(`site bundle missing ${siteFile}`);
394
496
  }
@@ -3,15 +3,15 @@ import { pathToFileURL } from "node:url";
3
3
 
4
4
  export function classifyReleaseTag(tag) {
5
5
  const normalized = String(tag || "").trim();
6
- const match = normalized.match(/^v\d+\.\d+\.\d+(?:-alpha\.\d+)?$/);
6
+ const match = normalized.match(/^v\d+\.\d+\.\d+(?:-[0-9A-Za-z][0-9A-Za-z.-]*)?$/);
7
7
  if (!match) {
8
- throw new Error(`Unsupported Buildchain release tag: ${tag}`);
8
+ throw new Error(`Unsupported semver release tag: ${tag}`);
9
9
  }
10
- const alpha = normalized.includes("-alpha.");
10
+ const prerelease = normalized.includes("-");
11
11
  return {
12
12
  tag: normalized,
13
- prerelease: alpha,
14
- makeLatest: alpha ? "false" : "true",
13
+ prerelease,
14
+ makeLatest: prerelease ? "false" : "true",
15
15
  };
16
16
  }
17
17
 
@@ -2,6 +2,7 @@
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { pathToFileURL } from "node:url";
5
+ import { createBuildchainContractWorld } from "../packages/core/buildchain-contract.js";
5
6
 
6
7
  const SITE_BUNDLE_CONTRACT = "kungfu-buildchain-site-bundle";
7
8
  const root = path.resolve(import.meta.dirname, "..");
@@ -146,6 +147,7 @@ function buildSiteBundle() {
146
147
  "workflow-registry.json",
147
148
  "release-model.json",
148
149
  "artifact-schemas.json",
150
+ "buildchain-contract.json",
149
151
  "product-mechanism.json",
150
152
  "release-provenance.json",
151
153
  "agent-index.json",
@@ -228,6 +230,7 @@ function buildSiteBundle() {
228
230
  "cli-registry.json",
229
231
  "workflow-registry.json",
230
232
  "artifact-schemas.json",
233
+ "buildchain-contract.json",
231
234
  ],
232
235
  instruction: "Use this bundle as the package-owned fact source for Buildchain pages. Do not infer current release mechanics from prose alone.",
233
236
  };
@@ -252,6 +255,7 @@ function buildSiteBundle() {
252
255
  "workflow-registry.json": workflowRegistry,
253
256
  "release-model.json": releaseModel,
254
257
  "artifact-schemas.json": artifactSchemas,
258
+ "buildchain-contract.json": createBuildchainContractWorld({ root }),
255
259
  "product-mechanism.json": productMechanism,
256
260
  "release-provenance.json": releaseProvenance,
257
261
  "agent-index.json": agentIndex,
@@ -106,7 +106,7 @@ export function selectReleaseCandidateRuns({ runs = [], pullRequest, workflowNam
106
106
  const matchesHeadBranch = prHeadBranch
107
107
  && runHeadBranch === prHeadBranch
108
108
  && (!prHeadRepository || !runHeadRepository || runHeadRepository === prHeadRepository);
109
- const matchesPr = matchesPrNumber || matchesHeadSha || matchesHeadBranch;
109
+ const matchesPr = matchesPrNumber || matchesHeadSha || (!prNumber && !prHeadSha && matchesHeadBranch);
110
110
  const matchesWorkflow = !workflowName || run.name === workflowName || run.workflow_name === workflowName;
111
111
  return matchesPr && matchesWorkflow && run.event === "pull_request" && run.status === "completed" && run.conclusion === "success";
112
112
  });