@kungfu-tech/buildchain 4.0.8 → 4.0.9-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/AGENTS.md +12 -19
- package/CONTRIBUTING.md +6 -9
- package/README.md +11 -1
- package/architecture/agent-change-map.md +11 -4
- package/architecture/build-environments.json +71 -0
- package/architecture/build-orchestration.json +18 -0
- package/architecture/ci-lane-change-budget.json +34 -2
- package/architecture/internal-capabilities.json +17 -4
- package/architecture/maintainability-debt.json +34 -123
- package/architecture/maintainability-policy.json +19 -210
- package/architecture/release-topology.json +6 -5
- package/architecture/universal-workflow-bootstrap.json +5 -4
- package/architecture/universal-workflow-train-admission.json +2 -2
- package/bin/buildchain.mjs +1 -1
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
- package/dist/site/buildchain-contract.json +41 -1131
- package/dist/site/buildchain-site.json +39 -144
- package/dist/site/capability-registry.json +1 -1
- package/dist/site/cli-registry.json +1 -1
- package/dist/site/controller-registry.json +30 -868
- package/dist/site/kfd-claims.json +132 -394
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +6 -6
- package/dist/site/node-api-registry.json +34 -34
- package/dist/site/page-registry.json +30 -135
- package/dist/site/public-surface-audit.json +81 -401
- package/dist/site/publication-registry.json +2 -2
- package/dist/site/site-manifest.json +8 -8
- package/dist/site/workflow-registry.json +135 -391
- package/docs/MAP.md +5 -4
- package/docs/cli-reference.md +1 -1
- package/docs/getting-started.md +8 -7
- package/docs/node-api-reference.md +79 -79
- package/docs/reusable-build-surface.md +159 -1430
- package/docs/runtime-train-validation.md +45 -172
- package/package.json +1 -1
- package/packages/core/build-configuration.js +117 -0
- package/packages/core/buildchain-config.js +2 -0
- package/packages/core/buildchain-contract.js +14 -44
- package/packages/core/controller-evidence.js +2 -9
- package/scripts/buildchain-cli-help.mjs +1 -1
- package/scripts/check-floating-consumer-policy-contract.mjs +4 -7
- package/scripts/check-inventory.mjs +81 -54
- package/scripts/check-universal-workflow-bootstrap.mjs +12 -2
- package/scripts/generate-channel-build-workflow.mjs +50 -410
- package/scripts/generate-site-bundle.mjs +2 -1
- package/scripts/generate-universal-workflow-facades.mjs +1 -0
- package/scripts/init-repo.mjs +25 -26
- package/scripts/resolve-build-configuration.mjs +105 -0
- package/scripts/universal-workflow-self-dogfood.mjs +8 -1
- package/scripts/verify-golden-path.mjs +3 -3
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
3
4
|
import {
|
|
4
5
|
assertPublicSurfaceReverseAudit,
|
|
5
6
|
collectPublicSurfaceReverseAudit,
|
|
6
7
|
} from "../packages/core/public-surface-audit.js";
|
|
7
|
-
import { evaluateBuildchainContractLock } from "../packages/core/buildchain-contract.js";
|
|
8
|
+
import { createBuildchainContractLock, evaluateBuildchainContractLock, finalizeBuildchainContractWorld } from "../packages/core/buildchain-contract.js";
|
|
8
9
|
import {
|
|
9
|
-
canAdmitSelfDogfoodLockEvaluation,
|
|
10
|
-
contractForSelfDogfoodEvaluation,
|
|
11
10
|
hasQualifiedSelfDogfoodBootstrapAuthority,
|
|
12
11
|
resolveSelfDogfoodMajor,
|
|
13
12
|
} from "../packages/core/self-dogfood-version.js";
|
|
@@ -203,9 +202,6 @@ const selfDogfoodWorkflow = fs.readFileSync(
|
|
|
203
202
|
const selfDogfoodAlphaLock = JSON.parse(
|
|
204
203
|
fs.readFileSync(path.join(root, ".buildchain/alpha-contract-lock.json"), "utf8"),
|
|
205
204
|
);
|
|
206
|
-
const currentBuildchainContract = JSON.parse(
|
|
207
|
-
fs.readFileSync(path.join(root, "dist/site/buildchain-contract.json"), "utf8"),
|
|
208
|
-
);
|
|
209
205
|
const selfDogfoodBootstrapAuthority = JSON.parse(
|
|
210
206
|
fs.readFileSync(path.join(root, "architecture/bootstrap-authority.json"), "utf8"),
|
|
211
207
|
);
|
|
@@ -227,23 +223,58 @@ if (!/^[0-9a-f]{40}$/.test(selfDogfoodAlphaLock.buildchain?.resolvedSha || ""))
|
|
|
227
223
|
if (selfDogfoodAlphaLock.buildchain?.compatibilityPolicy !== "major-compatible") {
|
|
228
224
|
throw new Error("Buildchain self-dogfood alpha lock must enforce major-compatible policy");
|
|
229
225
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
226
|
+
// Consumer locks describe accepted published runtimes, not this unpublished producer.
|
|
227
|
+
// Validate their immutable source worlds here; hosted admission checks floating drift.
|
|
228
|
+
for (const channel of ["alpha-contract-lock.json", "contract-lock.json"]) {
|
|
229
|
+
const lock = JSON.parse(
|
|
230
|
+
fs.readFileSync(path.join(root, ".buildchain", channel), "utf8"),
|
|
231
|
+
);
|
|
232
|
+
const sha = lock.buildchain?.resolvedSha;
|
|
233
|
+
if (!/^[0-9a-f]{40}$/u.test(sha || ""))
|
|
234
|
+
throw new Error(`${channel}: invalid accepted SHA`);
|
|
235
|
+
let world;
|
|
236
|
+
try {
|
|
237
|
+
world = JSON.parse(
|
|
238
|
+
execFileSync(
|
|
239
|
+
"git",
|
|
240
|
+
["show", `${sha}:dist/site/buildchain-contract.json`],
|
|
241
|
+
{ cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] },
|
|
242
|
+
),
|
|
243
|
+
);
|
|
244
|
+
} catch {
|
|
245
|
+
const response = await fetch(
|
|
246
|
+
`https://raw.githubusercontent.com/kungfu-systems/buildchain/${sha}/dist/site/buildchain-contract.json`,
|
|
247
|
+
{ signal: AbortSignal.timeout(30000) },
|
|
248
|
+
);
|
|
249
|
+
if (!response.ok)
|
|
250
|
+
throw new Error(
|
|
251
|
+
`${channel}: accepted source contract unavailable (${response.status})`,
|
|
252
|
+
);
|
|
253
|
+
world = await response.json();
|
|
254
|
+
}
|
|
255
|
+
const verified = finalizeBuildchainContractWorld(world);
|
|
256
|
+
for (const field of ["contractDigest", "compatibilityDigest"]) {
|
|
257
|
+
if (
|
|
258
|
+
world[field] !== verified[field] ||
|
|
259
|
+
lock.buildchain[field] !== world[field]
|
|
260
|
+
)
|
|
261
|
+
throw new Error(`${channel}: accepted ${field} mismatch`);
|
|
262
|
+
}
|
|
263
|
+
const accepted = createBuildchainContractLock({ contractWorld: verified }).buildchain;
|
|
264
|
+
if (JSON.stringify(lock.buildchain.surfaces) !== JSON.stringify(accepted.surfaces) || lock.buildchain.majorLine !== verified.majorLine || lock.buildchain.compatibilityPolicy !== "major-compatible") throw new Error(`${channel}: accepted surface set or policy mismatch`);
|
|
265
|
+
const ref = channel.startsWith("alpha")
|
|
266
|
+
? `v${selfDogfoodMajor}-alpha`
|
|
267
|
+
: `v${selfDogfoodMajor}`;
|
|
268
|
+
const evaluation = evaluateBuildchainContractLock({
|
|
269
|
+
lock,
|
|
270
|
+
current: verified,
|
|
271
|
+
runtimeRef: ref,
|
|
272
|
+
runtimeSha: sha,
|
|
273
|
+
runtimeClass: channel.startsWith("alpha") ? "alpha" : "stable",
|
|
274
|
+
workflowShellRef: ref,
|
|
275
|
+
});
|
|
276
|
+
if (!evaluation.ok || lock.buildchain.ref !== ref)
|
|
277
|
+
throw new Error(`${channel}: invalid accepted runtime contract`);
|
|
247
278
|
}
|
|
248
279
|
for (const requiredSnippet of [
|
|
249
280
|
`/.github/workflows/build.yml@v${selfDogfoodMajor}-alpha`,
|
|
@@ -262,8 +293,8 @@ const reusableBuildWorkflow = fs.readFileSync(
|
|
|
262
293
|
);
|
|
263
294
|
for (const requiredSnippet of [
|
|
264
295
|
"BUILDCHAIN_WORKFLOW_REF: ${{ job.workflow_ref }}",
|
|
265
|
-
"
|
|
266
|
-
|
|
296
|
+
"BUILDCHAIN_WORKFLOW_SHA: ${{ job.workflow_sha }}",
|
|
297
|
+
"Resolve rooted build plan from TOML",
|
|
267
298
|
]) {
|
|
268
299
|
if (!reusableBuildWorkflow.includes(requiredSnippet)) {
|
|
269
300
|
throw new Error(`reusable build workflow missing called-workflow identity: ${requiredSnippet}`);
|
|
@@ -343,23 +374,13 @@ const promotionOverrideAuthorization = fs.readFileSync(
|
|
|
343
374
|
if (!promotionOverrideAuthorization.includes("promotion runtime override is only allowed for trusted workflow_dispatch runs")) {
|
|
344
375
|
throw new Error("promotion runtime override authorization must remain fail closed");
|
|
345
376
|
}
|
|
346
|
-
for (const
|
|
347
|
-
"
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
"needs.resolve-channel.outputs.runtime-override != 'true' && needs.resolve-channel.outputs.channel == 'stable'",
|
|
351
|
-
"needs.resolve-channel.outputs.buildchain-ref",
|
|
352
|
-
"needs.resolve-channel.outputs.contract-lock-path",
|
|
353
|
-
]) {
|
|
354
|
-
if (!channelBuildWorkflow.includes(requiredSnippet)) {
|
|
355
|
-
throw new Error(`channel build workflow missing routing contract: ${requiredSnippet}`);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
if ((channelBuildWorkflow.match(/uses: \.\/\.github\/workflows\/\.build\.yml/g) || []).length !== 3) {
|
|
359
|
-
throw new Error("channel build workflow must bind override, alpha, and stable to the exact caller workflow shell");
|
|
377
|
+
for (const workflow of [channelBuildWorkflow, reusableBuildWorkflow]) {
|
|
378
|
+
const block = workflow.split(" inputs:\n")[1].split(" secrets:\n")[0];
|
|
379
|
+
const names = [...block.matchAll(/^ ([a-z0-9-]+):$/gm)].map((match) => match[1]);
|
|
380
|
+
if (JSON.stringify(names) !== '["config-path"]') throw new Error("ordinary build inputs must contain only config-path");
|
|
360
381
|
}
|
|
361
|
-
if (channelBuildWorkflow.
|
|
362
|
-
throw new Error("
|
|
382
|
+
if ((channelBuildWorkflow.match(/uses: \.\/\.github\/workflows\/\.build\.yml/g) || []).length !== 1) {
|
|
383
|
+
throw new Error("public build facade must invoke its exact backbone once");
|
|
363
384
|
}
|
|
364
385
|
for (const requiredSnippet of [
|
|
365
386
|
"group: buildchain-release-promotion-${{ github.repository }}",
|
|
@@ -780,8 +801,9 @@ const registeredActionIds = (workflowRegistry.actions || []).map((entry) => entr
|
|
|
780
801
|
const readmeActionIndex = fs.readFileSync(path.join(root, "README.md"), "utf8");
|
|
781
802
|
const mapActionIndex = fs.readFileSync(path.join(root, "docs/MAP.md"), "utf8");
|
|
782
803
|
const retrospectiveActionIndex = fs.readFileSync(path.join(root, ".github/retrospectives/2026-07-10-buildchain-consolidation.md"), "utf8");
|
|
783
|
-
|
|
784
|
-
|
|
804
|
+
const buildOwners = JSON.parse(fs.readFileSync(path.join(root, "architecture/build-orchestration.json"), "utf8")).owners;
|
|
805
|
+
if (registeredActionIds.length !== 8 + Object.keys(buildOwners).length) {
|
|
806
|
+
throw new Error(`workflow-registry.json must expose the eight public actions and owned build composites, got ${registeredActionIds.length}`);
|
|
785
807
|
}
|
|
786
808
|
for (const actionId of registeredActionIds) {
|
|
787
809
|
if (!readmeActionIndex.includes(`actions/${actionId}`) || !mapActionIndex.includes(`actions/${actionId}`)) {
|
|
@@ -925,16 +947,16 @@ for (const requiredSnippet of [
|
|
|
925
947
|
}
|
|
926
948
|
const reusableBuildSurfaceDoc = fs.readFileSync(path.join(root, "docs/reusable-build-surface.md"), "utf8");
|
|
927
949
|
for (const requiredSnippet of [
|
|
928
|
-
"
|
|
929
|
-
"
|
|
930
|
-
"buildchain
|
|
931
|
-
"
|
|
932
|
-
"
|
|
933
|
-
"
|
|
934
|
-
"
|
|
935
|
-
"
|
|
936
|
-
"
|
|
937
|
-
"
|
|
950
|
+
"config-path",
|
|
951
|
+
"buildchain.toml",
|
|
952
|
+
".buildchain/contract-lock.json",
|
|
953
|
+
".buildchain/alpha-contract-lock.json",
|
|
954
|
+
"architecture/build-environments.json",
|
|
955
|
+
"build.contract",
|
|
956
|
+
"cache roots",
|
|
957
|
+
"exact consumer source",
|
|
958
|
+
"build-lifecycle-stage",
|
|
959
|
+
"build-artifact-transfer",
|
|
938
960
|
]) {
|
|
939
961
|
if (!reusableBuildSurfaceDoc.includes(requiredSnippet)) {
|
|
940
962
|
throw new Error(`reusable build surface doc missing contract lock snippet: ${requiredSnippet}`);
|
|
@@ -1319,7 +1341,12 @@ if (!Array.isArray(inventory.migratedActions) || inventory.migratedActions.lengt
|
|
|
1319
1341
|
throw new Error("migratedActions must be empty; buildchain v2 only ships native actions");
|
|
1320
1342
|
}
|
|
1321
1343
|
const shippedActions = internalActions;
|
|
1322
|
-
|
|
1344
|
+
if (inventory.compositeActionOwnership !== "architecture/build-orchestration.json#owners") throw new Error("composite action ownership must use the build orchestration contract");
|
|
1345
|
+
const compositeNames = Object.keys(buildOwners).map((file) => {
|
|
1346
|
+
if (!/^actions\/[^/]+\/action\.yml$/.test(file) || !/using:\s*composite/.test(fs.readFileSync(path.join(root, file), "utf8"))) throw new Error(`invalid owned composite action: ${file}`);
|
|
1347
|
+
return path.posix.basename(path.posix.dirname(file));
|
|
1348
|
+
});
|
|
1349
|
+
const shippedActionNames = [...shippedActions.map((action) => action.path.replace(/^actions\//, "")), ...compositeNames].sort();
|
|
1323
1350
|
|
|
1324
1351
|
if (JSON.stringify(actualActions) !== JSON.stringify(shippedActionNames)) {
|
|
1325
1352
|
throw new Error(
|
|
@@ -75,13 +75,23 @@ for (const relative of contract.retiredWorkflowSurfaces) {
|
|
|
75
75
|
const activeWorkflows = contract.inventoryWorkflows.filter(
|
|
76
76
|
(relative) =>
|
|
77
77
|
!contract.retiredWorkflowSurfaces.includes(relative) &&
|
|
78
|
+
!contract.configurationGovernedWorkflows.includes(relative) &&
|
|
78
79
|
relative !== contract.bootstrap.consumerRecoveryWorkflow,
|
|
79
80
|
);
|
|
80
81
|
assert.deepEqual(
|
|
81
82
|
contract.bootstrapGovernedWorkflows,
|
|
82
83
|
activeWorkflows,
|
|
83
|
-
"every active
|
|
84
|
-
);
|
|
84
|
+
"every active reusable workflow must have its declared configuration or Bootstrap authority",
|
|
85
|
+
);
|
|
86
|
+
assert.deepEqual(contract.configurationGovernedWorkflows, [
|
|
87
|
+
".github/workflows/.build.yml", ".github/workflows/build.yml",
|
|
88
|
+
]);
|
|
89
|
+
for (const relative of contract.configurationGovernedWorkflows) {
|
|
90
|
+
const source = fs.readFileSync(path.join(root, relative), "utf8");
|
|
91
|
+
const inputBlock = source.split(" inputs:\n")[1].split(" secrets:\n")[0];
|
|
92
|
+
assert.deepEqual([...inputBlock.matchAll(/^ ([a-z0-9-]+):$/gmu)].map((match) => match[1]), ["config-path"]);
|
|
93
|
+
assert.doesNotMatch(source, /universal-request-json/u);
|
|
94
|
+
}
|
|
85
95
|
for (const relative of contract.bootstrapGovernedWorkflows) {
|
|
86
96
|
assert.ok(
|
|
87
97
|
contract.inventoryWorkflows.includes(relative),
|