@kungfu-tech/buildchain 3.0.3-alpha.1 → 3.0.3-alpha.2
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/README.md +9 -4
- package/bin/buildchain.mjs +2 -256
- package/dist/site/buildchain-contract.json +5 -5
- package/dist/site/buildchain-site.json +19 -14
- package/dist/site/capability-registry.json +2 -2
- package/dist/site/cli-registry.json +72 -0
- package/dist/site/kfd-claims.json +95 -11
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +1 -1
- package/dist/site/node-api-registry.json +7 -7
- package/dist/site/page-registry.json +13 -8
- package/dist/site/public-surface-audit.json +72 -14
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +5 -5
- package/docs/cli.md +31 -7
- package/docs/publication-artifacts.md +27 -0
- package/package.json +1 -1
- package/packages/core/README.md +3 -2
- package/packages/core/github-governance-authority.js +10 -25
- package/packages/core/index.js +1 -1
- package/packages/core/paper-fleet.js +260 -0
- package/packages/core/paper-repository.js +210 -0
- package/packages/core/paper-work.js +365 -0
- package/packages/core/paper.js +117 -163
- package/packages/core/public-surface-audit.js +26 -78
- package/packages/core/public-surface-cli.js +117 -0
- package/scripts/buildchain-cli-help.mjs +256 -0
- package/scripts/generate-site-bundle.mjs +1 -187
- package/scripts/paper-work-fleet-cli.mjs +563 -0
- package/scripts/paper.mjs +31 -45
- package/scripts/site-capability-metadata.mjs +202 -0
package/packages/core/paper.js
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
1
|
import crypto from "node:crypto";
|
|
3
2
|
import fs from "node:fs";
|
|
4
3
|
import os from "node:os";
|
|
5
4
|
import path from "node:path";
|
|
6
|
-
import {
|
|
7
|
-
loadBuildchainConfig,
|
|
8
|
-
validateBuildchainConfig,
|
|
9
|
-
} from "./buildchain-config.js";
|
|
5
|
+
import { validateBuildchainConfig } from "./buildchain-config.js";
|
|
10
6
|
import {
|
|
11
7
|
createBuildchainContractLock,
|
|
12
8
|
createBuildchainContractWorld,
|
|
@@ -22,6 +18,37 @@ import {
|
|
|
22
18
|
PUBLICATION_SEALED_BUNDLE_CONTRACT,
|
|
23
19
|
verifyPublicationSealedBundle,
|
|
24
20
|
} from "./publication-sealed-bundle.js";
|
|
21
|
+
import {
|
|
22
|
+
PAPER_PATHS,
|
|
23
|
+
commandResult,
|
|
24
|
+
gitResult,
|
|
25
|
+
gitValue,
|
|
26
|
+
normalizeRepository,
|
|
27
|
+
paperConfig,
|
|
28
|
+
parsePaperVersion,
|
|
29
|
+
readJson,
|
|
30
|
+
resolvePaperRepository,
|
|
31
|
+
sha256Text,
|
|
32
|
+
stableJson,
|
|
33
|
+
} from "./paper-repository.js";
|
|
34
|
+
|
|
35
|
+
export { PAPER_PATHS, resolvePaperRepository } from "./paper-repository.js";
|
|
36
|
+
export {
|
|
37
|
+
PAPER_WORK_START_PLAN_CONTRACT,
|
|
38
|
+
PAPER_WORK_SUBMIT_PLAN_CONTRACT,
|
|
39
|
+
createPaperWorkStartPlan,
|
|
40
|
+
createPaperWorkSubmitPlan,
|
|
41
|
+
executePaperWorkStart,
|
|
42
|
+
executePaperWorkSubmitPush,
|
|
43
|
+
} from "./paper-work.js";
|
|
44
|
+
export {
|
|
45
|
+
PAPER_FLEET_AUDIT_CONTRACT,
|
|
46
|
+
PAPER_FLEET_UPDATE_PLAN_CONTRACT,
|
|
47
|
+
collectPaperFleetAudit,
|
|
48
|
+
discoverPaperFleet,
|
|
49
|
+
planPaperFleetUpdate,
|
|
50
|
+
writePaperFleetUpdate,
|
|
51
|
+
} from "./paper-fleet.js";
|
|
25
52
|
|
|
26
53
|
export const PAPER_SCAFFOLD_CONTRACT = "kungfu-buildchain-paper-scaffold";
|
|
27
54
|
export const PAPER_MIGRATION_CONTRACT = "kungfu-buildchain-paper-migration";
|
|
@@ -50,22 +77,6 @@ export const PAPER_STATE_ORDER = Object.freeze([
|
|
|
50
77
|
"production-visible",
|
|
51
78
|
]);
|
|
52
79
|
|
|
53
|
-
export const PAPER_PATHS = Object.freeze({
|
|
54
|
-
config: ".buildchain/buildchain.toml",
|
|
55
|
-
versionPin: ".buildchain-version",
|
|
56
|
-
contractLock: ".buildchain/contract-lock.json",
|
|
57
|
-
buildWorkflow: ".github/workflows/build.yml",
|
|
58
|
-
releaseWorkflow: ".github/workflows/paper-release.yml",
|
|
59
|
-
reproducibilityReceipt:
|
|
60
|
-
".buildchain/publication/reproducibility-receipt.json",
|
|
61
|
-
sealedBundle: ".buildchain/admitted/sealed-bundle.json",
|
|
62
|
-
admission: ".buildchain/admitted/publication-admission.json",
|
|
63
|
-
capability: ".buildchain/admitted/publication-capability.json",
|
|
64
|
-
npmBootstrap: ".buildchain/paper/npm-bootstrap.json",
|
|
65
|
-
npmTrust: ".buildchain/paper/npm-trust.json",
|
|
66
|
-
provisioningAuthority: ".buildchain/paper/provisioning-authority.json",
|
|
67
|
-
visibility: ".buildchain/paper/visibility.json",
|
|
68
|
-
});
|
|
69
80
|
const PAPER_SCAFFOLD_PATHS = Object.freeze([
|
|
70
81
|
PAPER_PATHS.config,
|
|
71
82
|
PAPER_PATHS.contractLock,
|
|
@@ -100,23 +111,6 @@ function toPosix(value) {
|
|
|
100
111
|
.join("/");
|
|
101
112
|
}
|
|
102
113
|
|
|
103
|
-
function stableJson(value) {
|
|
104
|
-
if (Array.isArray(value)) {
|
|
105
|
-
return `[${value.map(stableJson).join(",")}]`;
|
|
106
|
-
}
|
|
107
|
-
if (value && typeof value === "object") {
|
|
108
|
-
return `{${Object.keys(value)
|
|
109
|
-
.sort()
|
|
110
|
-
.map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`)
|
|
111
|
-
.join(",")}}`;
|
|
112
|
-
}
|
|
113
|
-
return JSON.stringify(value);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function sha256Text(value) {
|
|
117
|
-
return `sha256:${crypto.createHash("sha256").update(String(value)).digest("hex")}`;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
114
|
function sha256File(filePath) {
|
|
121
115
|
return `sha256:${crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex")}`;
|
|
122
116
|
}
|
|
@@ -125,21 +119,6 @@ function jsonText(value) {
|
|
|
125
119
|
return `${JSON.stringify(value, null, 2)}\n`;
|
|
126
120
|
}
|
|
127
121
|
|
|
128
|
-
function readJson(filePath) {
|
|
129
|
-
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
|
|
130
|
-
return { exists: false, value: undefined, error: "" };
|
|
131
|
-
}
|
|
132
|
-
try {
|
|
133
|
-
return {
|
|
134
|
-
exists: true,
|
|
135
|
-
value: JSON.parse(fs.readFileSync(filePath, "utf8")),
|
|
136
|
-
error: "",
|
|
137
|
-
};
|
|
138
|
-
} catch (error) {
|
|
139
|
-
return { exists: true, value: undefined, error: error.message };
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
122
|
function existingFileFact(cwd, relativePath) {
|
|
144
123
|
const filePath = path.resolve(cwd, relativePath);
|
|
145
124
|
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
|
|
@@ -160,51 +139,6 @@ function normalizePackageName(value, label = "package name") {
|
|
|
160
139
|
return normalized;
|
|
161
140
|
}
|
|
162
141
|
|
|
163
|
-
function normalizeRepository(value) {
|
|
164
|
-
const normalized = String(value || "")
|
|
165
|
-
.trim()
|
|
166
|
-
.replace(/^git\+/, "")
|
|
167
|
-
.replace(/^git@github\.com:/, "")
|
|
168
|
-
.replace(/^ssh:\/\/git@github\.com\//, "")
|
|
169
|
-
.replace(/^https?:\/\/github\.com\//, "")
|
|
170
|
-
.replace(/\.git$/, "")
|
|
171
|
-
.replace(/^\/+|\/+$/g, "");
|
|
172
|
-
if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(normalized)) {
|
|
173
|
-
return "";
|
|
174
|
-
}
|
|
175
|
-
return normalized;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function commandResult(
|
|
179
|
-
command,
|
|
180
|
-
args,
|
|
181
|
-
{ cwd, env = process.env, timeout = 15000 } = {},
|
|
182
|
-
) {
|
|
183
|
-
const result = spawnSync(command, args, {
|
|
184
|
-
cwd,
|
|
185
|
-
env,
|
|
186
|
-
encoding: "utf8",
|
|
187
|
-
timeout,
|
|
188
|
-
maxBuffer: 2 * 1024 * 1024,
|
|
189
|
-
});
|
|
190
|
-
return {
|
|
191
|
-
ok: result.status === 0,
|
|
192
|
-
status: result.status ?? 1,
|
|
193
|
-
stdout: String(result.stdout || "").trim(),
|
|
194
|
-
stderr: String(result.stderr || "").trim(),
|
|
195
|
-
error: result.error?.message || "",
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function gitResult(cwd, args) {
|
|
200
|
-
return commandResult("git", args, { cwd });
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function gitValue(cwd, args) {
|
|
204
|
-
const result = gitResult(cwd, args);
|
|
205
|
-
return result.ok ? result.stdout : "";
|
|
206
|
-
}
|
|
207
|
-
|
|
208
142
|
function buildchainPackageIdentity(buildchainRoot, explicitVersion = "") {
|
|
209
143
|
const packageJson = readJson(
|
|
210
144
|
path.resolve(buildchainRoot, "package.json"),
|
|
@@ -541,16 +475,58 @@ clean:
|
|
|
541
475
|
`;
|
|
542
476
|
}
|
|
543
477
|
|
|
544
|
-
function
|
|
545
|
-
return
|
|
546
|
-
|
|
478
|
+
function paperPackageScripts(current = {}) {
|
|
479
|
+
return {
|
|
480
|
+
...current,
|
|
481
|
+
"buildchain:paper": "buildchain paper",
|
|
482
|
+
"paper:preflight": "buildchain paper preflight --json",
|
|
483
|
+
"paper:work:start": "buildchain paper work start",
|
|
484
|
+
"paper:work:submit": "buildchain paper work submit",
|
|
485
|
+
"paper:status": "buildchain paper status --json",
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function managedPaperPackageJson(current, buildchainVersion) {
|
|
490
|
+
if (!/^3\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(buildchainVersion)) {
|
|
491
|
+
throw new Error(
|
|
492
|
+
"paper repositories require an exact Buildchain v3 version",
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
const packageManager = current.packageManager || "pnpm@11.7.0";
|
|
496
|
+
if (!packageManager.startsWith("pnpm@")) {
|
|
497
|
+
throw new Error("paper repositories require a pnpm packageManager");
|
|
498
|
+
}
|
|
499
|
+
return {
|
|
500
|
+
...current,
|
|
547
501
|
private: true,
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
502
|
+
scripts: paperPackageScripts(current.scripts),
|
|
503
|
+
devDependencies: {
|
|
504
|
+
...(current.devDependencies || {}),
|
|
505
|
+
"@kungfu-tech/buildchain": buildchainVersion,
|
|
552
506
|
},
|
|
553
|
-
|
|
507
|
+
packageManager,
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function scaffoldPackageJson({
|
|
512
|
+
name,
|
|
513
|
+
packageName,
|
|
514
|
+
repository,
|
|
515
|
+
buildchainVersion,
|
|
516
|
+
}) {
|
|
517
|
+
return jsonText({
|
|
518
|
+
...managedPaperPackageJson(
|
|
519
|
+
{
|
|
520
|
+
name: packageName,
|
|
521
|
+
description: `${name} publication source repository.`,
|
|
522
|
+
repository: {
|
|
523
|
+
type: "git",
|
|
524
|
+
url: `git+https://github.com/${repository}.git`,
|
|
525
|
+
},
|
|
526
|
+
license: "Apache-2.0",
|
|
527
|
+
},
|
|
528
|
+
buildchainVersion,
|
|
529
|
+
),
|
|
554
530
|
});
|
|
555
531
|
}
|
|
556
532
|
|
|
@@ -562,9 +538,10 @@ This repository is a Buildchain-governed publication artifact source.
|
|
|
562
538
|
## Local workflow
|
|
563
539
|
|
|
564
540
|
\`\`\`sh
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
541
|
+
pnpm paper:preflight
|
|
542
|
+
pnpm paper:work:start -- <topic>
|
|
543
|
+
pnpm paper:work:submit
|
|
544
|
+
make pdf
|
|
568
545
|
\`\`\`
|
|
569
546
|
|
|
570
547
|
The public package identity is \`${packageName}\`. Buildchain owns reproducible
|
|
@@ -644,8 +621,13 @@ function scaffoldFiles({
|
|
|
644
621
|
acceptedAt,
|
|
645
622
|
});
|
|
646
623
|
const contractLockText = jsonText(contractLock);
|
|
647
|
-
const buildWorkflow = scaffoldBuildWorkflow(buildchainSha
|
|
648
|
-
|
|
624
|
+
const buildWorkflow = scaffoldBuildWorkflow(buildchainSha, {
|
|
625
|
+
artifactName: name,
|
|
626
|
+
});
|
|
627
|
+
const releaseWorkflow = scaffoldReleaseWorkflow(buildchainSha, {
|
|
628
|
+
artifactPaths: "_build/main.pdf",
|
|
629
|
+
releasePassportProductName: title,
|
|
630
|
+
});
|
|
649
631
|
const provisioningAuthority = createPaperProvisioningAuthority({
|
|
650
632
|
repository,
|
|
651
633
|
packageName,
|
|
@@ -673,7 +655,15 @@ function scaffoldFiles({
|
|
|
673
655
|
[PAPER_PATHS.releaseWorkflow, releaseWorkflow],
|
|
674
656
|
[PAPER_PATHS.provisioningAuthority, jsonText(provisioningAuthority)],
|
|
675
657
|
["Makefile", scaffoldMakefile()],
|
|
676
|
-
[
|
|
658
|
+
[
|
|
659
|
+
"package.json",
|
|
660
|
+
scaffoldPackageJson({
|
|
661
|
+
name,
|
|
662
|
+
packageName,
|
|
663
|
+
repository,
|
|
664
|
+
buildchainVersion,
|
|
665
|
+
}),
|
|
666
|
+
],
|
|
677
667
|
["README.md", scaffoldReadme({ title, packageName })],
|
|
678
668
|
["docs/MAP.md", scaffoldMap()],
|
|
679
669
|
["paper/main.tex", scaffoldMainTex(title)],
|
|
@@ -953,12 +943,21 @@ function migrationFiles({
|
|
|
953
943
|
buildWorkflow,
|
|
954
944
|
releaseWorkflow,
|
|
955
945
|
});
|
|
946
|
+
const currentPackage = readJson(path.resolve(cwd, "package.json"));
|
|
947
|
+
if (!currentPackage.exists || currentPackage.error || !currentPackage.value) {
|
|
948
|
+
throw new Error("paper migration requires a valid package.json");
|
|
949
|
+
}
|
|
950
|
+
const packageJson = managedPaperPackageJson(
|
|
951
|
+
currentPackage.value,
|
|
952
|
+
runtimeIdentity.version,
|
|
953
|
+
);
|
|
956
954
|
return new Map([
|
|
957
955
|
[PAPER_PATHS.contractLock, contractLockText],
|
|
958
956
|
[PAPER_PATHS.versionPin, `${runtimeIdentity.version}\n`],
|
|
959
957
|
[PAPER_PATHS.buildWorkflow, buildWorkflow],
|
|
960
958
|
[PAPER_PATHS.releaseWorkflow, releaseWorkflow],
|
|
961
959
|
[PAPER_PATHS.provisioningAuthority, jsonText(provisioningAuthority)],
|
|
960
|
+
["package.json", jsonText(packageJson)],
|
|
962
961
|
]);
|
|
963
962
|
}
|
|
964
963
|
|
|
@@ -1063,6 +1062,12 @@ export function planPaperMigration({
|
|
|
1063
1062
|
description:
|
|
1064
1063
|
"Write the reviewed Buildchain-owned control files without changing paper content or publication configuration.",
|
|
1065
1064
|
},
|
|
1065
|
+
{
|
|
1066
|
+
id: "refresh-pnpm-lock",
|
|
1067
|
+
command: "pnpm install --lockfile-only",
|
|
1068
|
+
description:
|
|
1069
|
+
"Bind the exact Buildchain v3 dependency into pnpm-lock.yaml after the reviewed package update.",
|
|
1070
|
+
},
|
|
1066
1071
|
],
|
|
1067
1072
|
};
|
|
1068
1073
|
Object.defineProperty(result, "_plannedFiles", {
|
|
@@ -1133,26 +1138,6 @@ export function writePaperMigration(plan) {
|
|
|
1133
1138
|
};
|
|
1134
1139
|
}
|
|
1135
1140
|
|
|
1136
|
-
function paperConfig(cwd) {
|
|
1137
|
-
const loaded = loadBuildchainConfig(cwd);
|
|
1138
|
-
if (!loaded) {
|
|
1139
|
-
return {
|
|
1140
|
-
loaded: undefined,
|
|
1141
|
-
error: `${PAPER_PATHS.config} is missing`,
|
|
1142
|
-
};
|
|
1143
|
-
}
|
|
1144
|
-
if (loaded.config.project?.type !== "publication-artifact") {
|
|
1145
|
-
return {
|
|
1146
|
-
loaded,
|
|
1147
|
-
error: 'project.type must be "publication-artifact"',
|
|
1148
|
-
};
|
|
1149
|
-
}
|
|
1150
|
-
if (!loaded.config.publication) {
|
|
1151
|
-
return { loaded, error: "[publication] is missing" };
|
|
1152
|
-
}
|
|
1153
|
-
return { loaded, error: "" };
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
1141
|
function state(id, status, reason, evidence = []) {
|
|
1157
1142
|
return {
|
|
1158
1143
|
id,
|
|
@@ -1591,20 +1576,6 @@ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
|
|
|
1591
1576
|
};
|
|
1592
1577
|
}
|
|
1593
1578
|
|
|
1594
|
-
export function resolvePaperRepository(cwd = process.cwd()) {
|
|
1595
|
-
const packagePath = path.resolve(cwd, "package.json");
|
|
1596
|
-
const sourcePackage = readJson(packagePath).value;
|
|
1597
|
-
const configured =
|
|
1598
|
-
typeof sourcePackage?.repository === "string"
|
|
1599
|
-
? sourcePackage.repository
|
|
1600
|
-
: sourcePackage?.repository?.url;
|
|
1601
|
-
const fromPackage = normalizeRepository(configured);
|
|
1602
|
-
if (fromPackage) return fromPackage;
|
|
1603
|
-
return normalizeRepository(
|
|
1604
|
-
gitValue(cwd, ["config", "--get", "remote.origin.url"]),
|
|
1605
|
-
);
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
1579
|
function validatePaperProvisioningAuthority(cwd) {
|
|
1609
1580
|
const authorityPath = path.resolve(cwd, PAPER_PATHS.provisioningAuthority);
|
|
1610
1581
|
const source = readJson(authorityPath);
|
|
@@ -2527,23 +2498,6 @@ export function createPaperBuildPlan({
|
|
|
2527
2498
|
};
|
|
2528
2499
|
}
|
|
2529
2500
|
|
|
2530
|
-
function parsePaperVersion(version) {
|
|
2531
|
-
const normalized = String(version || "")
|
|
2532
|
-
.trim()
|
|
2533
|
-
.replace(/^v/, "");
|
|
2534
|
-
const match = normalized.match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/);
|
|
2535
|
-
if (!match) {
|
|
2536
|
-
throw new Error("publication.version must be semver before planning Alpha");
|
|
2537
|
-
}
|
|
2538
|
-
return {
|
|
2539
|
-
version: normalized,
|
|
2540
|
-
major: Number(match[1]),
|
|
2541
|
-
minor: Number(match[2]),
|
|
2542
|
-
patch: Number(match[3]),
|
|
2543
|
-
prerelease: match[4] || "",
|
|
2544
|
-
};
|
|
2545
|
-
}
|
|
2546
|
-
|
|
2547
2501
|
function resolvePaperChannelRef(cwd, ref) {
|
|
2548
2502
|
const candidates = [
|
|
2549
2503
|
{
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
commandId,
|
|
6
|
+
enumerateCliCommandsFromBin,
|
|
7
|
+
} from "./public-surface-cli.js";
|
|
8
|
+
|
|
9
|
+
export { enumerateCliCommandsFromBin } from "./public-surface-cli.js";
|
|
4
10
|
|
|
5
11
|
export const BUILDCHAIN_PUBLIC_SURFACE_AUDIT_CONTRACT = "kungfu-buildchain-public-surface-reverse-audit";
|
|
6
12
|
|
|
@@ -40,82 +46,6 @@ function listDirectories(root, dir) {
|
|
|
40
46
|
.sort();
|
|
41
47
|
}
|
|
42
48
|
|
|
43
|
-
function commandId(first = "", second = "", third = "") {
|
|
44
|
-
const head = String(first || "").trim();
|
|
45
|
-
const sub = String(second || "").trim();
|
|
46
|
-
const leaf = String(third || "").trim();
|
|
47
|
-
const normalizedLeaf = leaf === "..." ? "" : leaf;
|
|
48
|
-
if (!head) return "";
|
|
49
|
-
if (["-h", "--help", "help"].includes(head)) return "help";
|
|
50
|
-
if (["-v", "--version", "version"].includes(head)) return "version";
|
|
51
|
-
if (head === "release" && ["--dry-run", "dry-run", "explain"].includes(sub)) return "release-dry-run";
|
|
52
|
-
if (head === "release" && sub === "line") return "release-line-open";
|
|
53
|
-
if (head === "release") return "release-transaction";
|
|
54
|
-
if (head === "transaction") return "transaction-inspect";
|
|
55
|
-
if (head === "collect" && sub) return `collect-${sub}`;
|
|
56
|
-
if (head === "verify" && sub) return `verify-${sub}`;
|
|
57
|
-
if (head === "explain" && sub) return `explain-${sub}`;
|
|
58
|
-
if (head === "inspect" && sub) return `inspect-${sub}`;
|
|
59
|
-
if (head === "npm" && sub) return `npm-${sub}`;
|
|
60
|
-
if (head === "lifecycle" && sub) return "lifecycle";
|
|
61
|
-
if (head === "log" && sub) return "logging";
|
|
62
|
-
if (head === "diagnostics" && sub) return `diagnostics-${sub}`;
|
|
63
|
-
if (head === "facts" && sub) return "build-facts";
|
|
64
|
-
if (head === "kfd") {
|
|
65
|
-
if (!sub || sub === "...") return "kfd";
|
|
66
|
-
if (sub === "schema") return normalizedLeaf ? `kfd-schema-${normalizedLeaf}` : "kfd-schema";
|
|
67
|
-
if (sub === "upstream") return normalizedLeaf ? `kfd-upstream-${normalizedLeaf}` : "kfd-upstream";
|
|
68
|
-
if (/^[1-9][0-9]*$/.test(sub)) return normalizedLeaf ? `kfd-${sub}-${normalizedLeaf}` : `kfd-${sub}`;
|
|
69
|
-
return `kfd-${sub}`;
|
|
70
|
-
}
|
|
71
|
-
if (head === "sample" && sub) return `sample-${sub}`;
|
|
72
|
-
if (head === "badges" && sub) return `badges-${sub}`;
|
|
73
|
-
if (head === "homebrew" && sub) return `homebrew-${sub}`;
|
|
74
|
-
if (head === "release-propagation") return "release-propagation";
|
|
75
|
-
if (head === "publish-source") return "publish-source";
|
|
76
|
-
if (head === "publication-artifact" && sub) return `publication-artifact-${sub}`;
|
|
77
|
-
if (head === "publication-artifact") return "publication-artifact";
|
|
78
|
-
if (head === "publication" && sub) return `publication-artifact-${sub}`;
|
|
79
|
-
if (head === "publication") return "publication-artifact";
|
|
80
|
-
if (head === "paper" && sub === "bootstrap" && normalizedLeaf === "npm") return "paper-bootstrap-npm";
|
|
81
|
-
if (head === "paper" && sub) return `paper-${sub}`;
|
|
82
|
-
if (head === "paper") return "paper";
|
|
83
|
-
if (head === "build-contract") return "build-contract";
|
|
84
|
-
if (head === "infra-contract") return "infra-contract";
|
|
85
|
-
if (head === "web-surface") return "web-surface";
|
|
86
|
-
return head;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function enumerateCliCommandsFromBin({ root = process.cwd(), binPath = "bin/buildchain.mjs" } = {}) {
|
|
90
|
-
const source = readText(root, binPath);
|
|
91
|
-
const dispatchSource = [
|
|
92
|
-
source,
|
|
93
|
-
...listFiles(root, "bin/internal", (name) => name.endsWith(".mjs"))
|
|
94
|
-
.map((relPath) => readText(root, relPath)),
|
|
95
|
-
].join("\n");
|
|
96
|
-
const usageMatch = source.match(/return `Usage:\n([\s\S]*?)`;\n}/);
|
|
97
|
-
const usage = usageMatch?.[1] || "";
|
|
98
|
-
const usageCommands = [];
|
|
99
|
-
for (const line of usage.split(/\r?\n/)) {
|
|
100
|
-
const match = line.trim().match(/^buildchain\s+([^\s]+)(?:\s+([^\s]+))?(?:\s+([^\s]+))?/);
|
|
101
|
-
if (!match) continue;
|
|
102
|
-
usageCommands.push({
|
|
103
|
-
id: commandId(match[1], match[2], match[3]),
|
|
104
|
-
usage: line.trim().replace(/\s+/g, " "),
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
const dispatchCommands = [...dispatchSource.matchAll(/if\s*\(\s*command\s*===\s*"([^"]+)"/g)]
|
|
108
|
-
.map((match) => commandId(match[1]));
|
|
109
|
-
return uniqueSorted([
|
|
110
|
-
...usageCommands.map((entry) => entry.id),
|
|
111
|
-
...dispatchCommands,
|
|
112
|
-
]).map((id) => ({
|
|
113
|
-
id,
|
|
114
|
-
source: "bin/buildchain.mjs",
|
|
115
|
-
usage: usageCommands.find((entry) => entry.id === id)?.usage || `buildchain ${id}`,
|
|
116
|
-
}));
|
|
117
|
-
}
|
|
118
|
-
|
|
119
49
|
function parseYamlWorkflowCall(text) {
|
|
120
50
|
const lines = text.split(/\r?\n/);
|
|
121
51
|
const result = { reusable: false, inputs: [], secrets: [], outputs: [] };
|
|
@@ -333,8 +263,26 @@ export function collectPublicSurfaceReverseAudit({
|
|
|
333
263
|
mode: "closed-world-enumerable",
|
|
334
264
|
scope: "Buildchain CLI usage/dispatch, reusable workflow inputs, action inputs, site pages, and documentation command references",
|
|
335
265
|
residualRisk: [
|
|
336
|
-
|
|
337
|
-
|
|
266
|
+
{
|
|
267
|
+
id: "public-surface-helper-command-enumeration",
|
|
268
|
+
definedBy: "https://kfd.libkungfu.dev/schemas/kfd-2/trust-taxonomy.schema.json#/$defs/residualRisk",
|
|
269
|
+
riskType: "natural-language-semantic-risk",
|
|
270
|
+
trustImpact: "downgrade-warning",
|
|
271
|
+
machineProvability: "not-machine-verifiable",
|
|
272
|
+
agentAction: "semantic-review-required",
|
|
273
|
+
owner: "Buildchain maintainers",
|
|
274
|
+
reason: "Shell commands delegated through helper scripts are counted only when exposed through bin/buildchain.mjs usage or documentation.",
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
id: "public-surface-yaml-enumeration",
|
|
278
|
+
definedBy: "https://kfd.libkungfu.dev/schemas/kfd-2/trust-taxonomy.schema.json#/$defs/residualRisk",
|
|
279
|
+
riskType: "natural-language-semantic-risk",
|
|
280
|
+
trustImpact: "downgrade-warning",
|
|
281
|
+
machineProvability: "not-machine-verifiable",
|
|
282
|
+
agentAction: "semantic-review-required",
|
|
283
|
+
owner: "Buildchain maintainers",
|
|
284
|
+
reason: "YAML enumeration covers first-class action and workflow inputs, not arbitrary step environment variables.",
|
|
285
|
+
},
|
|
338
286
|
],
|
|
339
287
|
},
|
|
340
288
|
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
function readText(root, relPath) {
|
|
5
|
+
return fs.readFileSync(path.join(root, relPath), "utf8");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function listFiles(root, dir, predicate = () => true) {
|
|
9
|
+
const base = path.join(root, dir);
|
|
10
|
+
if (!fs.existsSync(base)) return [];
|
|
11
|
+
return fs
|
|
12
|
+
.readdirSync(base, { withFileTypes: true })
|
|
13
|
+
.filter((entry) => entry.isFile() && predicate(entry.name))
|
|
14
|
+
.map((entry) => `${dir}/${entry.name}`)
|
|
15
|
+
.sort();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function uniqueSorted(values) {
|
|
19
|
+
return [...new Set(values.filter(Boolean))].sort();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function releaseCommandId(sub) {
|
|
23
|
+
if (["--dry-run", "dry-run", "explain"].includes(sub))
|
|
24
|
+
return "release-dry-run";
|
|
25
|
+
if (sub === "line") return "release-line-open";
|
|
26
|
+
return "release-transaction";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function kfdCommandId(sub, leaf) {
|
|
30
|
+
if (!sub || sub === "...") return "kfd";
|
|
31
|
+
if (["schema", "upstream"].includes(sub) || /^[1-9][0-9]*$/.test(sub)) {
|
|
32
|
+
return leaf ? `kfd-${sub}-${leaf}` : `kfd-${sub}`;
|
|
33
|
+
}
|
|
34
|
+
return `kfd-${sub}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function paperCommandId(sub, leaf) {
|
|
38
|
+
if (sub === "bootstrap" && leaf === "npm") return "paper-bootstrap-npm";
|
|
39
|
+
if (["work", "fleet"].includes(sub) && leaf) return `paper-${sub}-${leaf}`;
|
|
40
|
+
return sub ? `paper-${sub}` : "paper";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function commandId(first = "", second = "", third = "") {
|
|
44
|
+
const head = String(first || "").trim();
|
|
45
|
+
const sub = String(second || "").trim();
|
|
46
|
+
const leaf =
|
|
47
|
+
String(third || "").trim() === "..." ? "" : String(third || "").trim();
|
|
48
|
+
const paired = new Set([
|
|
49
|
+
"collect",
|
|
50
|
+
"verify",
|
|
51
|
+
"explain",
|
|
52
|
+
"inspect",
|
|
53
|
+
"npm",
|
|
54
|
+
"diagnostics",
|
|
55
|
+
"sample",
|
|
56
|
+
"badges",
|
|
57
|
+
"homebrew",
|
|
58
|
+
]);
|
|
59
|
+
if (!head) return "";
|
|
60
|
+
if (["-h", "--help", "help"].includes(head)) return "help";
|
|
61
|
+
if (["-v", "--version", "version"].includes(head)) return "version";
|
|
62
|
+
if (head === "release") return releaseCommandId(sub);
|
|
63
|
+
if (head === "transaction") return "transaction-inspect";
|
|
64
|
+
if (paired.has(head) && sub) return `${head}-${sub}`;
|
|
65
|
+
if (head === "lifecycle" && sub) return "lifecycle";
|
|
66
|
+
if (head === "log" && sub) return "logging";
|
|
67
|
+
if (head === "facts" && sub) return "build-facts";
|
|
68
|
+
if (head === "kfd") return kfdCommandId(sub, leaf);
|
|
69
|
+
if (["release-propagation", "publish-source"].includes(head)) return head;
|
|
70
|
+
if (["publication-artifact", "publication"].includes(head)) {
|
|
71
|
+
return sub ? `publication-artifact-${sub}` : "publication-artifact";
|
|
72
|
+
}
|
|
73
|
+
if (head === "paper") return paperCommandId(sub, leaf);
|
|
74
|
+
return head;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function usageCommands(source) {
|
|
78
|
+
const usageMatch = source.match(/`Usage:\n([\s\S]*?)`;/);
|
|
79
|
+
const rows = [];
|
|
80
|
+
for (const line of (usageMatch?.[1] || "").split(/\r?\n/)) {
|
|
81
|
+
const match = line
|
|
82
|
+
.trim()
|
|
83
|
+
.match(/^buildchain\s+([^\s]+)(?:\s+([^\s]+))?(?:\s+([^\s]+))?/);
|
|
84
|
+
if (!match) continue;
|
|
85
|
+
rows.push({
|
|
86
|
+
id: commandId(match[1], match[2], match[3]),
|
|
87
|
+
usage: line.trim().replace(/\s+/g, " "),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return rows;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function enumerateCliCommandsFromBin({
|
|
94
|
+
root = process.cwd(),
|
|
95
|
+
binPath = "bin/buildchain.mjs",
|
|
96
|
+
} = {}) {
|
|
97
|
+
const binSource = readText(root, binPath);
|
|
98
|
+
const helpSource = readText(root, "scripts/buildchain-cli-help.mjs");
|
|
99
|
+
const dispatchSource = [
|
|
100
|
+
binSource,
|
|
101
|
+
...listFiles(root, "bin/internal", (name) => name.endsWith(".mjs")).map(
|
|
102
|
+
(relPath) => readText(root, relPath),
|
|
103
|
+
),
|
|
104
|
+
].join("\n");
|
|
105
|
+
const usage = usageCommands(helpSource);
|
|
106
|
+
const dispatch = [
|
|
107
|
+
...dispatchSource.matchAll(/if\s*\(\s*command\s*===\s*"([^"]+)"/g),
|
|
108
|
+
].map((match) => commandId(match[1]));
|
|
109
|
+
return uniqueSorted([...usage.map((entry) => entry.id), ...dispatch]).map(
|
|
110
|
+
(id) => ({
|
|
111
|
+
id,
|
|
112
|
+
source: "bin/buildchain.mjs",
|
|
113
|
+
usage:
|
|
114
|
+
usage.find((entry) => entry.id === id)?.usage || `buildchain ${id}`,
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
}
|