@kungfu-tech/buildchain 4.0.8 → 4.0.9-alpha.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.
- 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 +51 -2
- package/architecture/internal-capabilities.json +17 -4
- package/architecture/maintainability-debt.json +37 -126
- package/architecture/maintainability-policy.json +19 -210
- package/architecture/release-topology.json +9 -7
- package/architecture/universal-workflow-bootstrap.json +5 -4
- package/architecture/universal-workflow-train-admission.json +2 -2
- package/architecture/workflow-taxonomy.json +13 -0
- 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 +48 -153
- package/dist/site/capability-registry.json +2 -2
- package/dist/site/cli-registry.json +1 -1
- package/dist/site/controller-registry.json +30 -868
- package/dist/site/kfd-claims.json +148 -396
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +7 -7
- package/dist/site/node-api-registry.json +37 -37
- package/dist/site/page-registry.json +36 -141
- package/dist/site/public-surface-audit.json +93 -402
- package/dist/site/publication-authority-registry.json +22 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +11 -11
- package/dist/site/workflow-registry.json +165 -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 +85 -85
- package/docs/release-governance.md +19 -18
- package/docs/reusable-build-surface.md +160 -1430
- package/docs/runtime-train-validation.md +45 -172
- package/docs/workflow-catalog.md +1 -0
- 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/buildchain-publication-authority.js +1 -0
- 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 +91 -77
- package/scripts/check-maintainability.mjs +23 -8
- 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
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { loadBuildchainConfig } from "../packages/core/buildchain-config.js";
|
|
6
|
+
import { containedBuildPath, discoverBuildConfiguration, normalizeBuildConfiguration } from "../packages/core/build-configuration.js";
|
|
7
|
+
import { writeGitHubOutputs } from "./github-output.mjs";
|
|
8
|
+
|
|
9
|
+
const runtimeRoot = path.resolve(import.meta.dirname, "..");
|
|
10
|
+
const digest = (value) => `sha256:${crypto.createHash("sha256").update(value).digest("hex")}`;
|
|
11
|
+
const rootOf = (value) => digest(JSON.stringify(value));
|
|
12
|
+
|
|
13
|
+
export function resolveBuildConfiguration({ root, locator = "", workflowRef, workflowSha, repository, sourceSha, sourceRef, callerWorkflowRef = "", eventName = "", baseRef = "", environmentRegistry = JSON.parse(fs.readFileSync(path.join(runtimeRoot, "architecture/build-environments.json"), "utf8")) }) {
|
|
14
|
+
for (const [label, value] of Object.entries({ workflowSha, sourceSha })) {
|
|
15
|
+
if (!/^[a-f0-9]{40}$/u.test(value || "")) throw new Error(`${label} must be an exact SHA`);
|
|
16
|
+
}
|
|
17
|
+
const match = String(workflowRef).match(/^([^/]+\/[^/]+)\/(\.github\/workflows\/[^@]+)@(?:refs\/(?:heads|tags)\/)?(v([1-9]\d*)(-alpha)?)$/u);
|
|
18
|
+
if (!match || match[1] !== repository) throw new Error("Ordinary builds require the exact called workflow identity on a floating channel");
|
|
19
|
+
const identity = {
|
|
20
|
+
repository, ref: match[3], full_ref: `refs/tags/${match[3]}`,
|
|
21
|
+
sha: workflowSha, channel: match[5] ? "alpha" : "stable", major: match[4],
|
|
22
|
+
visible_workflow: match[2],
|
|
23
|
+
};
|
|
24
|
+
const callerPath = callerWorkflowRef.split("@")[0].split("/.github/workflows/")[1];
|
|
25
|
+
if (callerPath) {
|
|
26
|
+
const caller = fs.readFileSync(containedBuildPath(root, `.github/workflows/${callerPath}`), "utf8");
|
|
27
|
+
const calls = [...caller.matchAll(/uses:\s+kungfu-systems\/buildchain\/(\.github\/workflows\/(?:build|\.build|public-build-candidate|\.build-engine)\.yml)@(v\d+(?:-alpha)?)\s*$/gmu)]
|
|
28
|
+
.filter((call) => call[2] === identity.ref);
|
|
29
|
+
const paths = [...new Set(calls.map((call) => call[1]))];
|
|
30
|
+
if (paths.length !== 1) throw new Error("Unable to derive one visible build workflow from the exact caller");
|
|
31
|
+
identity.visible_workflow = paths[0];
|
|
32
|
+
}
|
|
33
|
+
const project = discoverBuildConfiguration(root, locator);
|
|
34
|
+
const loaded = loadBuildchainConfig(path.resolve(root, project.cwd));
|
|
35
|
+
const build = normalizeBuildConfiguration(loaded.config.build);
|
|
36
|
+
if (environmentRegistry.schema !== "buildchain.build-environments/v1" || !Object.hasOwn(environmentRegistry.profiles, build.environment)) {
|
|
37
|
+
throw new Error(`Unknown governed build environment: ${build.environment}`);
|
|
38
|
+
}
|
|
39
|
+
const overrides = environmentRegistry.profiles[build.environment];
|
|
40
|
+
const environment = Object.fromEntries(Object.entries(environmentRegistry.defaults).map(([group, values]) => [group, { ...values, ...overrides[group] }]));
|
|
41
|
+
const projectPath = (relative) => {
|
|
42
|
+
containedBuildPath(path.resolve(root, project.cwd), relative);
|
|
43
|
+
const selected = path.posix.join(project.cwd, relative);
|
|
44
|
+
containedBuildPath(root, selected);
|
|
45
|
+
return selected;
|
|
46
|
+
};
|
|
47
|
+
const evidence = (relative) => relative ? JSON.stringify(JSON.parse(fs.readFileSync(containedBuildPath(root, projectPath(relative)), "utf8"))) : "";
|
|
48
|
+
const dependencyFiles = ["pnpm-lock.yaml", "package-lock.json", "Cargo.lock", "go.sum"].map(projectPath).filter((file) => fs.existsSync(path.join(root, file)));
|
|
49
|
+
const plan = {
|
|
50
|
+
schema: "buildchain.build-plan/v1", project,
|
|
51
|
+
configuration_root: digest(fs.readFileSync(loaded.filePath)),
|
|
52
|
+
identity,
|
|
53
|
+
source: {
|
|
54
|
+
sha: sourceSha, ref: sourceRef,
|
|
55
|
+
candidate_channel: /^(?:refs\/heads\/)?release\//u.test(baseRef || sourceRef) ? "release"
|
|
56
|
+
: /^(?:refs\/heads\/)?(?:alpha|dev)\//u.test(baseRef || sourceRef) ? "alpha"
|
|
57
|
+
: eventName === "workflow_dispatch" ? (identity.channel === "stable" ? "release" : "alpha") : "none",
|
|
58
|
+
},
|
|
59
|
+
environment, environment_root: rootOf(environment),
|
|
60
|
+
build,
|
|
61
|
+
lifecycle: Object.fromEntries(["install", "build", "verify"].map((stage) => [stage, { required: stage === "build" || Boolean(loaded.config.lifecycle?.[stage]), configured: Boolean(loaded.config.lifecycle?.[stage]) }])),
|
|
62
|
+
tools: { node: build.tools.node, setup_node: Boolean(build.tools.node), rust: build.tools.rust || "stable", setup_rust: Boolean(build.tools.rust), go: build.tools.go, setup_go: Boolean(build.tools.go) },
|
|
63
|
+
artifacts: {
|
|
64
|
+
name: build.artifacts.name,
|
|
65
|
+
paths: build.artifacts.paths.map(projectPath).join("\n"),
|
|
66
|
+
expected_json: JSON.stringify({
|
|
67
|
+
minFiles: build.artifacts.min_files, maxFiles: build.artifacts.max_files,
|
|
68
|
+
minTotalBytes: build.artifacts.min_total_bytes,
|
|
69
|
+
requiredPaths: build.artifacts.required_paths.map(projectPath),
|
|
70
|
+
}),
|
|
71
|
+
},
|
|
72
|
+
evidence: { gate_profile_json: evidence(build.evidence.gate_profile_path), candidate_family_json: evidence(build.evidence.candidate_family_path) },
|
|
73
|
+
cache: {
|
|
74
|
+
dependency_root: rootOf(dependencyFiles.map((file) => [file, digest(fs.readFileSync(path.join(root, file)))])),
|
|
75
|
+
toolchain_root: rootOf({ tools: build.tools, environment: environment.tools }),
|
|
76
|
+
policy_root: rootOf(environment.cache),
|
|
77
|
+
},
|
|
78
|
+
contract: { lock_path: identity.channel === "alpha" ? ".buildchain/alpha-contract-lock.json" : ".buildchain/contract-lock.json" },
|
|
79
|
+
};
|
|
80
|
+
for (const [stage, status] of Object.entries(plan.lifecycle)) {
|
|
81
|
+
if (status.required && !status.configured) throw new Error(`buildchain.toml must declare lifecycle.${stage}`);
|
|
82
|
+
}
|
|
83
|
+
return { plan, root: rootOf(plan) };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
87
|
+
try {
|
|
88
|
+
const resolved = resolveBuildConfiguration({
|
|
89
|
+
root: process.env.BUILDCHAIN_CONSUMER_ROOT,
|
|
90
|
+
locator: process.env.BUILDCHAIN_CONFIG_PATH,
|
|
91
|
+
workflowRef: process.env.BUILDCHAIN_WORKFLOW_REF,
|
|
92
|
+
workflowSha: process.env.BUILDCHAIN_WORKFLOW_SHA,
|
|
93
|
+
repository: process.env.BUILDCHAIN_WORKFLOW_REPOSITORY,
|
|
94
|
+
sourceSha: process.env.GITHUB_SHA,
|
|
95
|
+
sourceRef: process.env.GITHUB_REF,
|
|
96
|
+
callerWorkflowRef: process.env.GITHUB_WORKFLOW_REF,
|
|
97
|
+
eventName: process.env.GITHUB_EVENT_NAME,
|
|
98
|
+
baseRef: process.env.GITHUB_BASE_REF,
|
|
99
|
+
});
|
|
100
|
+
writeGitHubOutputs({ "plan-json": JSON.stringify(resolved.plan), "plan-root": resolved.root });
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error(`::error::${String(error.message).replaceAll("\n", "%0A")}`);
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -70,7 +70,14 @@ export function createUniversalSelfDogfoodRequest({
|
|
|
70
70
|
id: "bootstrap-conformance",
|
|
71
71
|
payload: {
|
|
72
72
|
schema: "kungfu-buildchain-v4-universal-bootstrap-conformance/v1",
|
|
73
|
-
expectedGovernedWorkflowCount:
|
|
73
|
+
expectedGovernedWorkflowCount: JSON.parse(
|
|
74
|
+
fs.readFileSync(
|
|
75
|
+
new URL(
|
|
76
|
+
"../architecture/universal-workflow-bootstrap.json",
|
|
77
|
+
import.meta.url,
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
).bootstrapGovernedWorkflows.length,
|
|
74
81
|
},
|
|
75
82
|
}
|
|
76
83
|
: {
|
|
@@ -129,14 +129,14 @@ export function verifyGoldenPath() {
|
|
|
129
129
|
"Golden Path validation did not retain the required lifecycle stages",
|
|
130
130
|
);
|
|
131
131
|
assert(
|
|
132
|
-
/uses:\s+kungfu-systems\/buildchain
|
|
132
|
+
/uses:\s+kungfu-systems\/buildchain\/\.github\/workflows\/build\.yml@v4/.test(
|
|
133
133
|
workflow,
|
|
134
134
|
),
|
|
135
135
|
"Golden Path workflow is not a thin v4 reusable-workflow caller",
|
|
136
136
|
);
|
|
137
137
|
assert(
|
|
138
|
-
|
|
139
|
-
"Golden Path
|
|
138
|
+
!/\n\s+(?:inputs|with|steps):/.test(workflow),
|
|
139
|
+
"Golden Path ordinary caller must have zero inputs and delegate its steps",
|
|
140
140
|
);
|
|
141
141
|
assert(
|
|
142
142
|
releaseDryRun && typeof releaseDryRun === "object",
|