@kungfu-tech/buildchain 3.0.4-alpha.0 → 3.0.4-alpha.10

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.
Files changed (74) hide show
  1. package/AGENTS.md +14 -5
  2. package/README.md +18 -0
  3. package/bin/buildchain.mjs +136 -73
  4. package/bin/internal/trust-release-cli.mjs +15 -537
  5. package/bin/internal/trust-release-command-handlers.mjs +14 -0
  6. package/bin/internal/trust-release-inspection-handlers.mjs +175 -0
  7. package/bin/internal/trust-release-release-handlers.mjs +317 -0
  8. package/bin/internal/trust-release-verification-handlers.mjs +306 -0
  9. package/dist/site/buildchain-contract.json +36 -25
  10. package/dist/site/buildchain-site.json +1474 -61
  11. package/dist/site/capability-registry.json +8 -5
  12. package/dist/site/cli-registry.json +1869 -0
  13. package/dist/site/controller-registry.json +16 -3
  14. package/dist/site/kfd-claims.json +84 -10
  15. package/dist/site/kfd-upstream-aggregate.json +1 -1
  16. package/dist/site/manual-registry.json +49 -6
  17. package/dist/site/node-api-registry.json +17802 -11
  18. package/dist/site/page-registry.json +1441 -59
  19. package/dist/site/public-surface-audit.json +2863 -358
  20. package/dist/site/publication-registry.json +4 -4
  21. package/dist/site/release-model.json +7 -0
  22. package/dist/site/site-manifest.json +34 -10
  23. package/dist/site/workflow-registry.json +13 -5
  24. package/docs/MAP.md +21 -6
  25. package/docs/cli-reference.md +1936 -0
  26. package/docs/cli.md +15 -1
  27. package/docs/getting-started.md +167 -0
  28. package/docs/install.md +7 -8
  29. package/docs/node-api-reference.md +1952 -0
  30. package/docs/release-propagation.md +166 -8
  31. package/docs/site-bundle-contract.md +10 -4
  32. package/docs/versioning.md +5 -4
  33. package/package.json +8 -5
  34. package/packages/core/buildchain-agent-manuals.js +37 -0
  35. package/packages/core/buildchain-kfd-claims.js +4 -37
  36. package/packages/core/controller-evidence.js +8 -1
  37. package/packages/core/index.js +1 -13
  38. package/packages/core/paper-agent-entry.js +14 -11
  39. package/packages/core/paper-fleet.js +34 -7
  40. package/packages/core/paper-npm-bootstrap.js +492 -0
  41. package/packages/core/paper-repository.js +1 -0
  42. package/packages/core/paper.js +356 -674
  43. package/packages/core/public-surface-cli.js +12 -1
  44. package/packages/core/publication-package.js +7 -0
  45. package/packages/core/release-passport.js +67 -71
  46. package/packages/core/release-propagation-common.js +64 -0
  47. package/packages/core/release-propagation-execution-profile.js +59 -0
  48. package/packages/core/release-propagation-release.js +196 -0
  49. package/packages/core/release-propagation-stage-evidence.js +364 -0
  50. package/packages/core/release-propagation-work-capture.js +64 -0
  51. package/packages/core/release-propagation-work-constants.js +34 -0
  52. package/packages/core/release-propagation-work-control.js +203 -0
  53. package/packages/core/release-propagation-work-transitions.js +145 -0
  54. package/packages/core/release-propagation-work.js +517 -0
  55. package/packages/core/release-propagation.js +34 -158
  56. package/scripts/aggregate-build-summary.mjs +67 -4
  57. package/scripts/auditable-demo-renditions.mjs +131 -0
  58. package/scripts/auditable-demo.mjs +88 -91
  59. package/scripts/aws-windows-jit-controller-core.mjs +269 -0
  60. package/scripts/aws-windows-jit-controller.mjs +502 -0
  61. package/scripts/check-internal-architecture.mjs +178 -52
  62. package/scripts/check-inventory.mjs +2 -2
  63. package/scripts/check-maintainability.mjs +76 -17
  64. package/scripts/generate-public-reference.mjs +68 -0
  65. package/scripts/generate-site-bundle.mjs +35 -41
  66. package/scripts/maintainability-metrics.mjs +24 -4
  67. package/scripts/paper-work-fleet-cli.mjs +22 -3
  68. package/scripts/public-reference.mjs +557 -0
  69. package/scripts/release-propagation.mjs +126 -0
  70. package/scripts/resolve-artifact-transfer-mode.mjs +117 -0
  71. package/scripts/site-reference-registry.mjs +174 -0
  72. package/scripts/verify-golden-path.mjs +169 -0
  73. package/scripts/web-surface-core.mjs +46 -207
  74. package/scripts/web-surface-routing.mjs +286 -0
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+
7
+ const VALID_MODES = new Set(["github-artifacts", "s3-to-github-artifacts"]);
8
+
9
+ function firstValue(...values) {
10
+ return values.find((value) => String(value || "").trim()) || "";
11
+ }
12
+
13
+ function resolveArtifactTransferMode(env = process.env) {
14
+ const mode = env.INPUT_TRANSFER_MODE || "github-artifacts";
15
+ if (!VALID_MODES.has(mode)) {
16
+ throw new Error(
17
+ `artifact-transfer-mode must be github-artifacts or s3-to-github-artifacts, got: ${mode}`,
18
+ );
19
+ }
20
+ if (mode === "github-artifacts") {
21
+ return {
22
+ mode,
23
+ s3Bucket: "",
24
+ s3Region: "",
25
+ s3Prefix: "",
26
+ oidcAudience: "",
27
+ };
28
+ }
29
+ const s3Bucket = firstValue(env.INPUT_S3_BUCKET, env.VAR_S3_BUCKET);
30
+ const s3Region = firstValue(env.INPUT_S3_REGION, env.VAR_S3_REGION);
31
+ const s3Prefix = firstValue(
32
+ env.INPUT_S3_PREFIX,
33
+ env.VAR_S3_PREFIX,
34
+ "buildchain-artifacts",
35
+ );
36
+ const uploadRole = firstValue(
37
+ env.INPUT_S3_UPLOAD_ROLE_ARN,
38
+ env.VAR_S3_UPLOAD_ROLE_ARN,
39
+ env.SECRET_S3_UPLOAD_ROLE_ARN,
40
+ env.INPUT_S3_ROLE_ARN,
41
+ env.VAR_S3_ROLE_ARN,
42
+ env.SECRET_S3_ROLE_ARN,
43
+ );
44
+ const downloadRole = firstValue(
45
+ env.INPUT_S3_DOWNLOAD_ROLE_ARN,
46
+ env.VAR_S3_DOWNLOAD_ROLE_ARN,
47
+ env.SECRET_S3_DOWNLOAD_ROLE_ARN,
48
+ env.INPUT_S3_ROLE_ARN,
49
+ env.VAR_S3_ROLE_ARN,
50
+ env.SECRET_S3_ROLE_ARN,
51
+ );
52
+ if (!s3Bucket) {
53
+ throw new Error(
54
+ "artifact-transfer-mode=s3-to-github-artifacts requires artifact-relay-s3-bucket or BUILDCHAIN_ARTIFACT_RELAY_S3_BUCKET",
55
+ );
56
+ }
57
+ if (!s3Region) {
58
+ throw new Error(
59
+ "artifact-transfer-mode=s3-to-github-artifacts requires artifact-relay-s3-region or BUILDCHAIN_ARTIFACT_RELAY_S3_REGION",
60
+ );
61
+ }
62
+ if (!uploadRole) {
63
+ throw new Error(
64
+ "artifact-transfer-mode=s3-to-github-artifacts requires an upload role ARN input, variable, or secret",
65
+ );
66
+ }
67
+ if (!downloadRole) {
68
+ throw new Error(
69
+ "artifact-transfer-mode=s3-to-github-artifacts requires a download role ARN input, variable, or secret",
70
+ );
71
+ }
72
+ const oidcAudience = firstValue(
73
+ env.INPUT_OIDC_AUDIENCE,
74
+ env.VAR_OIDC_AUDIENCE,
75
+ s3Region.startsWith("cn-") ? "sts.amazonaws.com.cn" : "sts.amazonaws.com",
76
+ );
77
+ return { mode, s3Bucket, s3Region, s3Prefix, oidcAudience };
78
+ }
79
+
80
+ function writeArtifactTransferOutputs(outputPath, resolution) {
81
+ if (!outputPath) {
82
+ throw new Error("GITHUB_OUTPUT is required");
83
+ }
84
+ fs.mkdirSync(path.dirname(path.resolve(outputPath)), { recursive: true });
85
+ fs.appendFileSync(
86
+ outputPath,
87
+ [
88
+ `mode=${resolution.mode}`,
89
+ `s3-bucket=${resolution.s3Bucket}`,
90
+ `s3-region=${resolution.s3Region}`,
91
+ `s3-prefix=${resolution.s3Prefix}`,
92
+ `oidc-audience=${resolution.oidcAudience}`,
93
+ "",
94
+ ].join("\n"),
95
+ );
96
+ }
97
+
98
+ function main() {
99
+ const resolution = resolveArtifactTransferMode();
100
+ writeArtifactTransferOutputs(process.env.GITHUB_OUTPUT, resolution);
101
+ }
102
+
103
+ if (
104
+ process.argv[1] &&
105
+ import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href
106
+ ) {
107
+ try {
108
+ main();
109
+ } catch (error) {
110
+ console.error(
111
+ `::error::${error instanceof Error ? error.message : String(error)}`,
112
+ );
113
+ process.exitCode = 1;
114
+ }
115
+ }
116
+
117
+ export { resolveArtifactTransferMode, writeArtifactTransferOutputs };
@@ -0,0 +1,174 @@
1
+ import { createNodeApiReference } from "./public-reference.mjs";
2
+
3
+ export function createSiteNodeApiRegistry({
4
+ root,
5
+ packageJson,
6
+ nodeApiMeta,
7
+ sha256File,
8
+ publicSurfaceLifecycle,
9
+ }) {
10
+ const reference = createNodeApiReference({ root, packageJson });
11
+ const referenceByExport = new Map(
12
+ reference.map((entry) => [entry.export, entry]),
13
+ );
14
+ const symbolsById = new Map();
15
+ for (const surface of reference) {
16
+ const meta = nodeApiMeta(surface.export);
17
+ for (const symbol of surface.symbols) {
18
+ const id = `${symbol.source.path}#${symbol.name}`;
19
+ if (!symbolsById.has(id)) {
20
+ symbolsById.set(id, {
21
+ id,
22
+ ...symbol,
23
+ maturity: meta.maturity,
24
+ audience: meta.audience,
25
+ });
26
+ }
27
+ }
28
+ }
29
+ return {
30
+ schemaVersion: 1,
31
+ contract: "kungfu-buildchain-node-api-registry",
32
+ package: packageJson.name,
33
+ moduleSystem: packageJson.type || "module",
34
+ symbols: [...symbolsById.values()].sort((left, right) =>
35
+ left.id.localeCompare(right.id),
36
+ ),
37
+ exports: Object.entries(packageJson.exports || {})
38
+ .filter(
39
+ ([specifier]) =>
40
+ !specifier.startsWith("./site/") && specifier !== "./package.json",
41
+ )
42
+ .map(([specifier, target]) => {
43
+ const meta = nodeApiMeta(specifier);
44
+ const surface = referenceByExport.get(specifier);
45
+ return {
46
+ specifier:
47
+ specifier === "."
48
+ ? packageJson.name
49
+ : `${packageJson.name}/${specifier.replace(/^\.\//, "")}`,
50
+ export: specifier,
51
+ target,
52
+ digest:
53
+ typeof target === "string"
54
+ ? sha256File(target.replace(/^\.\//, ""))
55
+ : "",
56
+ summary: meta.summary,
57
+ capabilityGroup: meta.capabilityGroup,
58
+ audience: meta.audience,
59
+ symbolCount: surface?.symbols.length || 0,
60
+ symbolIds: (surface?.symbols || []).map(
61
+ (symbol) => `${symbol.source.path}#${symbol.name}`,
62
+ ),
63
+ ...publicSurfaceLifecycle({
64
+ owner: "buildchain-core",
65
+ maturity: meta.maturity,
66
+ nonDuplicationRationale:
67
+ "Existing package subpath retained as the canonical API boundary for this capability.",
68
+ }),
69
+ };
70
+ }),
71
+ docs: [
72
+ ["cli-and-node-package", "docs/cli.md"],
73
+ ["node-api-reference", "docs/node-api-reference.md"],
74
+ ["build-facts", "docs/build-facts.md"],
75
+ ["kfd-support", "docs/kfd-support.md"],
76
+ ["readme-badges", "docs/readme-badges.md"],
77
+ ["homebrew", "docs/homebrew.md"],
78
+ ["site-bundle-contract", "docs/site-bundle-contract.md"],
79
+ ].map(([id, docPath]) => ({
80
+ id,
81
+ path: docPath,
82
+ digest: sha256File(docPath),
83
+ })),
84
+ guidance:
85
+ "These are the public Node import surfaces and symbols shipped by the npm package. Prefer them over internal file paths.",
86
+ };
87
+ }
88
+
89
+ function assertCliReferenceRegistry(cliRegistry) {
90
+ for (const command of cliRegistry.commands || []) {
91
+ for (const field of [
92
+ "paths",
93
+ "syntaxes",
94
+ "options",
95
+ "aliases",
96
+ "helpCommands",
97
+ ]) {
98
+ if (!Array.isArray(command[field])) {
99
+ throw new Error(
100
+ `cli-registry.json command missing generated ${field}: ${command.id || command.usage}`,
101
+ );
102
+ }
103
+ }
104
+ if (
105
+ command.paths.length === 0 ||
106
+ command.syntaxes.length === 0 ||
107
+ command.helpCommands.length === 0
108
+ ) {
109
+ throw new Error(
110
+ `cli-registry.json command has an empty generated reference: ${command.id || command.usage}`,
111
+ );
112
+ }
113
+ }
114
+ }
115
+
116
+ function assertNodeSymbol(symbol) {
117
+ const arraysComplete =
118
+ Array.isArray(symbol.parameters) &&
119
+ Array.isArray(symbol.errors) &&
120
+ symbol.errors.length > 0 &&
121
+ Array.isArray(symbol.sideEffects) &&
122
+ symbol.sideEffects.length > 0 &&
123
+ Array.isArray(symbol.audience);
124
+ const sourceComplete =
125
+ symbol.source?.path && Number.isInteger(symbol.source?.line);
126
+ if (
127
+ !symbol.id ||
128
+ !symbol.name ||
129
+ !symbol.kind ||
130
+ !symbol.signature ||
131
+ !symbol.returns ||
132
+ !symbol.maturity ||
133
+ !symbol.example ||
134
+ !arraysComplete ||
135
+ !sourceComplete
136
+ ) {
137
+ throw new Error(
138
+ `node-api-registry.json symbol is incomplete: ${symbol.id || "<unknown>"}`,
139
+ );
140
+ }
141
+ }
142
+
143
+ function assertNodeReferenceRegistry(nodeApiRegistry) {
144
+ const symbolsById = new Map(
145
+ (nodeApiRegistry.symbols || []).map((symbol) => [symbol.id, symbol]),
146
+ );
147
+ for (const symbol of symbolsById.values()) assertNodeSymbol(symbol);
148
+ for (const surface of nodeApiRegistry.exports || []) {
149
+ if (
150
+ !Number.isInteger(surface.symbolCount) ||
151
+ surface.symbolCount !== surface.symbolIds?.length ||
152
+ surface.symbolCount === 0
153
+ ) {
154
+ throw new Error(
155
+ `node-api-registry.json export has incomplete symbol closure: ${surface.export || surface.specifier}`,
156
+ );
157
+ }
158
+ for (const id of surface.symbolIds) {
159
+ if (!symbolsById.has(id)) {
160
+ throw new Error(
161
+ `node-api-registry.json export references an unknown symbol: ${surface.specifier}#${id}`,
162
+ );
163
+ }
164
+ }
165
+ }
166
+ }
167
+
168
+ export function assertPublicReferenceRegistry({
169
+ cliRegistry,
170
+ nodeApiRegistry,
171
+ }) {
172
+ assertCliReferenceRegistry(cliRegistry);
173
+ assertNodeReferenceRegistry(nodeApiRegistry);
174
+ }
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from "node:child_process";
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
9
+
10
+ function run(command, args, { cwd, json = false } = {}) {
11
+ const result = spawnSync(command, args, {
12
+ cwd,
13
+ encoding: "utf8",
14
+ env: {
15
+ ...process.env,
16
+ npm_config_audit: "false",
17
+ npm_config_fund: "false",
18
+ },
19
+ timeout: 10 * 60 * 1000,
20
+ });
21
+ if (result.error || result.status !== 0) {
22
+ throw new Error(
23
+ `${command} ${args.join(" ")} failed (${result.status}): ${result.stderr || result.stdout || result.error?.message}`,
24
+ );
25
+ }
26
+ return json ? JSON.parse(result.stdout) : result.stdout;
27
+ }
28
+
29
+ function assert(condition, message) {
30
+ if (!condition) throw new Error(message);
31
+ }
32
+
33
+ export function verifyGoldenPath() {
34
+ const temporary = fs.mkdtempSync(
35
+ path.join(os.tmpdir(), "buildchain-golden-path-"),
36
+ );
37
+ const packDir = path.join(temporary, "pack");
38
+ const consumer = path.join(temporary, "consumer");
39
+ fs.mkdirSync(packDir);
40
+ fs.mkdirSync(consumer);
41
+ try {
42
+ const packed = run(
43
+ "npm",
44
+ ["pack", "--json", "--pack-destination", packDir],
45
+ { cwd: root, json: true },
46
+ );
47
+ const tarball = path.join(packDir, packed[0].filename);
48
+ fs.writeFileSync(
49
+ path.join(consumer, "package.json"),
50
+ `${JSON.stringify(
51
+ {
52
+ name: "buildchain-golden-path-consumer",
53
+ version: "0.1.0",
54
+ private: true,
55
+ scripts: { build: 'node -e ""', check: 'node -e ""' },
56
+ },
57
+ null,
58
+ 2,
59
+ )}\n`,
60
+ );
61
+ run("npm", ["install", "--ignore-scripts", "--package-lock", tarball], {
62
+ cwd: consumer,
63
+ });
64
+ const buildchain = path.join(
65
+ consumer,
66
+ "node_modules",
67
+ ".bin",
68
+ process.platform === "win32" ? "buildchain.cmd" : "buildchain",
69
+ );
70
+ const version = run(buildchain, ["--version"], { cwd: consumer }).trim();
71
+ const initialized = run(
72
+ buildchain,
73
+ ["init", "--type", "package", "--package-manager", "npm"],
74
+ { cwd: consumer, json: true },
75
+ );
76
+ const validation = run(
77
+ buildchain,
78
+ [
79
+ "validate",
80
+ "--require-version-state",
81
+ "--require-lifecycle-stages",
82
+ "install,build,verify",
83
+ ],
84
+ { cwd: consumer, json: true },
85
+ );
86
+ const workflow = fs.readFileSync(
87
+ path.join(consumer, ".github", "workflows", "build.yml"),
88
+ "utf8",
89
+ );
90
+ const releaseDryRun = run(
91
+ buildchain,
92
+ ["release", "--dry-run", "--target-ref", "alpha/v3/v3.0", "--json"],
93
+ { cwd: consumer, json: true },
94
+ );
95
+ const passportPath = path.join(
96
+ consumer,
97
+ ".buildchain",
98
+ "golden-path",
99
+ "buildchain.release.json",
100
+ );
101
+ fs.mkdirSync(path.dirname(passportPath), { recursive: true });
102
+ run(
103
+ process.execPath,
104
+ [
105
+ "--input-type=module",
106
+ "--eval",
107
+ `import fs from "node:fs"; import { createReleasePassport } from "@kungfu-tech/buildchain"; const value=createReleasePassport({repository:"example/consumer",tag:"v0.1.0-alpha.0",sourceSha:"a".repeat(40),assets:[{name:"consumer.tgz",sha256:"b".repeat(64)}]}); fs.writeFileSync(${JSON.stringify(passportPath)}, JSON.stringify(value,null,2)+"\\n");`,
108
+ ],
109
+ { cwd: consumer },
110
+ );
111
+ const inspection = run(
112
+ buildchain,
113
+ ["inspect", "release", "--passport", passportPath, "--json"],
114
+ { cwd: consumer, json: true },
115
+ );
116
+
117
+ assert(
118
+ initialized.type === "package",
119
+ "Golden Path init did not retain the package project type",
120
+ );
121
+ assert(
122
+ validation.config?.path === ".buildchain/buildchain.toml",
123
+ "Golden Path validation did not read the generated config",
124
+ );
125
+ assert(
126
+ ["install", "build", "verify"].every((name) =>
127
+ (validation.lifecycleStages || []).some((stage) => stage.name === name),
128
+ ),
129
+ "Golden Path validation did not retain the required lifecycle stages",
130
+ );
131
+ assert(
132
+ /uses:\s+kungfu-systems\/buildchain\/.github\/workflows\/.build.yml@v3/.test(
133
+ workflow,
134
+ ),
135
+ "Golden Path workflow is not a thin v3 reusable-workflow caller",
136
+ );
137
+ assert(
138
+ /buildchain-ref:/.test(workflow),
139
+ "Golden Path workflow lacks the bounded runtime override input",
140
+ );
141
+ assert(
142
+ releaseDryRun && typeof releaseDryRun === "object",
143
+ "Golden Path release dry-run did not return JSON",
144
+ );
145
+ assert(
146
+ inspection && typeof inspection === "object",
147
+ "Golden Path Release Passport inspection did not return JSON",
148
+ );
149
+ return {
150
+ contract: "kungfu-buildchain-golden-path-verification/v1",
151
+ ok: true,
152
+ version,
153
+ projectType: initialized.type,
154
+ validationPath: validation.config?.path,
155
+ reusableWorkflow: ".github/workflows/build.yml",
156
+ releaseDryRun: true,
157
+ releasePassportInspection: true,
158
+ };
159
+ } finally {
160
+ fs.rmSync(temporary, { recursive: true, force: true });
161
+ }
162
+ }
163
+
164
+ try {
165
+ process.stdout.write(`${JSON.stringify(verifyGoldenPath(), null, 2)}\n`);
166
+ } catch (error) {
167
+ console.error(`buildchain Golden Path: ${error.message}`);
168
+ process.exitCode = 1;
169
+ }