@kungfu-tech/buildchain 3.0.3-alpha.1 → 3.0.3-alpha.3
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 +286 -0
- package/packages/core/paper-repository.js +211 -0
- package/packages/core/paper-work.js +365 -0
- package/packages/core/paper.js +160 -171
- 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"),
|
|
@@ -238,11 +172,30 @@ function runtimeGitSha(buildchainRoot, buildchainVersion = "") {
|
|
|
238
172
|
return GIT_SHA_PATTERN.test(gitHead) ? gitHead : "";
|
|
239
173
|
}
|
|
240
174
|
|
|
241
|
-
function runtimeAcceptedAt(buildchainRoot, sha) {
|
|
175
|
+
function runtimeAcceptedAt(buildchainRoot, sha, buildchainVersion = "") {
|
|
242
176
|
if (!sha) return "1970-01-01T00:00:00.000Z";
|
|
243
177
|
const value = gitValue(buildchainRoot, ["show", "-s", "--format=%cI", sha]);
|
|
244
|
-
|
|
245
|
-
|
|
178
|
+
let acceptedAt = value;
|
|
179
|
+
if (!acceptedAt && buildchainVersion) {
|
|
180
|
+
const identity = buildchainPackageIdentity(
|
|
181
|
+
buildchainRoot,
|
|
182
|
+
buildchainVersion,
|
|
183
|
+
);
|
|
184
|
+
const observed = commandResult(
|
|
185
|
+
"npm",
|
|
186
|
+
[
|
|
187
|
+
"view",
|
|
188
|
+
`${identity.name}@${identity.version}`,
|
|
189
|
+
"time",
|
|
190
|
+
"--json",
|
|
191
|
+
`--registry=${NPM_REGISTRY}`,
|
|
192
|
+
],
|
|
193
|
+
{ cwd: buildchainRoot },
|
|
194
|
+
);
|
|
195
|
+
const published = observed.ok ? safeParseJson(observed.stdout) : null;
|
|
196
|
+
acceptedAt = String(published?.[identity.version] || "");
|
|
197
|
+
}
|
|
198
|
+
const parsed = new Date(acceptedAt);
|
|
246
199
|
return Number.isNaN(parsed.valueOf())
|
|
247
200
|
? "1970-01-01T00:00:00.000Z"
|
|
248
201
|
: parsed.toISOString();
|
|
@@ -541,16 +494,58 @@ clean:
|
|
|
541
494
|
`;
|
|
542
495
|
}
|
|
543
496
|
|
|
544
|
-
function
|
|
545
|
-
return
|
|
546
|
-
|
|
497
|
+
function paperPackageScripts(current = {}) {
|
|
498
|
+
return {
|
|
499
|
+
...current,
|
|
500
|
+
"buildchain:paper": "buildchain paper",
|
|
501
|
+
"paper:preflight": "buildchain paper preflight --json",
|
|
502
|
+
"paper:work:start": "buildchain paper work start",
|
|
503
|
+
"paper:work:submit": "buildchain paper work submit",
|
|
504
|
+
"paper:status": "buildchain paper status --json",
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function managedPaperPackageJson(current, buildchainVersion) {
|
|
509
|
+
if (!/^3\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(buildchainVersion)) {
|
|
510
|
+
throw new Error(
|
|
511
|
+
"paper repositories require an exact Buildchain v3 version",
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
const packageManager = current.packageManager || "pnpm@11.7.0";
|
|
515
|
+
if (!packageManager.startsWith("pnpm@")) {
|
|
516
|
+
throw new Error("paper repositories require a pnpm packageManager");
|
|
517
|
+
}
|
|
518
|
+
return {
|
|
519
|
+
...current,
|
|
547
520
|
private: true,
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
521
|
+
scripts: paperPackageScripts(current.scripts),
|
|
522
|
+
devDependencies: {
|
|
523
|
+
...(current.devDependencies || {}),
|
|
524
|
+
"@kungfu-tech/buildchain": buildchainVersion,
|
|
552
525
|
},
|
|
553
|
-
|
|
526
|
+
packageManager,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function scaffoldPackageJson({
|
|
531
|
+
name,
|
|
532
|
+
packageName,
|
|
533
|
+
repository,
|
|
534
|
+
buildchainVersion,
|
|
535
|
+
}) {
|
|
536
|
+
return jsonText({
|
|
537
|
+
...managedPaperPackageJson(
|
|
538
|
+
{
|
|
539
|
+
name: packageName,
|
|
540
|
+
description: `${name} publication source repository.`,
|
|
541
|
+
repository: {
|
|
542
|
+
type: "git",
|
|
543
|
+
url: `git+https://github.com/${repository}.git`,
|
|
544
|
+
},
|
|
545
|
+
license: "Apache-2.0",
|
|
546
|
+
},
|
|
547
|
+
buildchainVersion,
|
|
548
|
+
),
|
|
554
549
|
});
|
|
555
550
|
}
|
|
556
551
|
|
|
@@ -562,9 +557,10 @@ This repository is a Buildchain-governed publication artifact source.
|
|
|
562
557
|
## Local workflow
|
|
563
558
|
|
|
564
559
|
\`\`\`sh
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
560
|
+
pnpm paper:preflight
|
|
561
|
+
pnpm paper:work:start -- <topic>
|
|
562
|
+
pnpm paper:work:submit
|
|
563
|
+
make pdf
|
|
568
564
|
\`\`\`
|
|
569
565
|
|
|
570
566
|
The public package identity is \`${packageName}\`. Buildchain owns reproducible
|
|
@@ -636,16 +632,21 @@ function scaffoldFiles({
|
|
|
636
632
|
).value;
|
|
637
633
|
const acceptedAt =
|
|
638
634
|
existingLock?.buildchain?.acceptedAt ||
|
|
639
|
-
runtimeAcceptedAt(buildchainRoot, buildchainSha);
|
|
635
|
+
runtimeAcceptedAt(buildchainRoot, buildchainSha, buildchainVersion);
|
|
640
636
|
const contractLock = createBuildchainContractLock({
|
|
641
|
-
buildchainRef
|
|
637
|
+
buildchainRef,
|
|
642
638
|
resolvedSha: buildchainSha,
|
|
643
639
|
contractWorld,
|
|
644
640
|
acceptedAt,
|
|
645
641
|
});
|
|
646
642
|
const contractLockText = jsonText(contractLock);
|
|
647
|
-
const buildWorkflow = scaffoldBuildWorkflow(buildchainSha
|
|
648
|
-
|
|
643
|
+
const buildWorkflow = scaffoldBuildWorkflow(buildchainSha, {
|
|
644
|
+
artifactName: name,
|
|
645
|
+
});
|
|
646
|
+
const releaseWorkflow = scaffoldReleaseWorkflow(buildchainSha, {
|
|
647
|
+
artifactPaths: "_build/main.pdf",
|
|
648
|
+
releasePassportProductName: title,
|
|
649
|
+
});
|
|
649
650
|
const provisioningAuthority = createPaperProvisioningAuthority({
|
|
650
651
|
repository,
|
|
651
652
|
packageName,
|
|
@@ -673,7 +674,15 @@ function scaffoldFiles({
|
|
|
673
674
|
[PAPER_PATHS.releaseWorkflow, releaseWorkflow],
|
|
674
675
|
[PAPER_PATHS.provisioningAuthority, jsonText(provisioningAuthority)],
|
|
675
676
|
["Makefile", scaffoldMakefile()],
|
|
676
|
-
[
|
|
677
|
+
[
|
|
678
|
+
"package.json",
|
|
679
|
+
scaffoldPackageJson({
|
|
680
|
+
name,
|
|
681
|
+
packageName,
|
|
682
|
+
repository,
|
|
683
|
+
buildchainVersion,
|
|
684
|
+
}),
|
|
685
|
+
],
|
|
677
686
|
["README.md", scaffoldReadme({ title, packageName })],
|
|
678
687
|
["docs/MAP.md", scaffoldMap()],
|
|
679
688
|
["paper/main.tex", scaffoldMainTex(title)],
|
|
@@ -928,13 +937,17 @@ function migrationFiles({
|
|
|
928
937
|
path.resolve(cwd, PAPER_PATHS.contractLock),
|
|
929
938
|
).value;
|
|
930
939
|
const contractLock = createBuildchainContractLock({
|
|
931
|
-
buildchainRef:
|
|
940
|
+
buildchainRef: "v3",
|
|
932
941
|
resolvedSha: runtimeSha,
|
|
933
942
|
contractWorld: runtimeContractWorld(buildchainRoot),
|
|
934
943
|
acceptedAt:
|
|
935
944
|
existingLock?.buildchain?.resolvedSha === runtimeSha
|
|
936
945
|
? existingLock.buildchain.acceptedAt
|
|
937
|
-
: runtimeAcceptedAt(
|
|
946
|
+
: runtimeAcceptedAt(
|
|
947
|
+
buildchainRoot,
|
|
948
|
+
runtimeSha,
|
|
949
|
+
runtimeIdentity.version,
|
|
950
|
+
),
|
|
938
951
|
});
|
|
939
952
|
const contractLockText = jsonText(contractLock);
|
|
940
953
|
const buildWorkflow = scaffoldBuildWorkflow(runtimeSha, {
|
|
@@ -953,13 +966,34 @@ function migrationFiles({
|
|
|
953
966
|
buildWorkflow,
|
|
954
967
|
releaseWorkflow,
|
|
955
968
|
});
|
|
956
|
-
|
|
969
|
+
const currentPackage = readJson(path.resolve(cwd, "package.json"));
|
|
970
|
+
if (!currentPackage.exists || currentPackage.error || !currentPackage.value) {
|
|
971
|
+
throw new Error("paper migration requires a valid package.json");
|
|
972
|
+
}
|
|
973
|
+
const packageJson = managedPaperPackageJson(
|
|
974
|
+
currentPackage.value,
|
|
975
|
+
runtimeIdentity.version,
|
|
976
|
+
);
|
|
977
|
+
const files = new Map([
|
|
957
978
|
[PAPER_PATHS.contractLock, contractLockText],
|
|
958
979
|
[PAPER_PATHS.versionPin, `${runtimeIdentity.version}\n`],
|
|
959
980
|
[PAPER_PATHS.buildWorkflow, buildWorkflow],
|
|
960
981
|
[PAPER_PATHS.releaseWorkflow, releaseWorkflow],
|
|
961
982
|
[PAPER_PATHS.provisioningAuthority, jsonText(provisioningAuthority)],
|
|
983
|
+
["package.json", jsonText(packageJson)],
|
|
962
984
|
]);
|
|
985
|
+
const verifyWorkflowPath = path.resolve(cwd, PAPER_PATHS.verifyWorkflow);
|
|
986
|
+
if (fs.existsSync(verifyWorkflowPath)) {
|
|
987
|
+
const currentVerifyWorkflow = fs.readFileSync(verifyWorkflowPath, "utf8");
|
|
988
|
+
const migratedVerifyWorkflow = currentVerifyWorkflow.replace(
|
|
989
|
+
/(uses:\s*kungfu-systems\/buildchain\/\.github\/workflows\/check\.yml@)[^\s]+/g,
|
|
990
|
+
`$1${runtimeSha}`,
|
|
991
|
+
);
|
|
992
|
+
if (migratedVerifyWorkflow !== currentVerifyWorkflow) {
|
|
993
|
+
files.set(PAPER_PATHS.verifyWorkflow, migratedVerifyWorkflow);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return files;
|
|
963
997
|
}
|
|
964
998
|
|
|
965
999
|
export function planPaperMigration({
|
|
@@ -1063,6 +1097,12 @@ export function planPaperMigration({
|
|
|
1063
1097
|
description:
|
|
1064
1098
|
"Write the reviewed Buildchain-owned control files without changing paper content or publication configuration.",
|
|
1065
1099
|
},
|
|
1100
|
+
{
|
|
1101
|
+
id: "refresh-pnpm-lock",
|
|
1102
|
+
command: "pnpm install --lockfile-only",
|
|
1103
|
+
description:
|
|
1104
|
+
"Bind the exact Buildchain v3 dependency into pnpm-lock.yaml after the reviewed package update.",
|
|
1105
|
+
},
|
|
1066
1106
|
],
|
|
1067
1107
|
};
|
|
1068
1108
|
Object.defineProperty(result, "_plannedFiles", {
|
|
@@ -1133,26 +1173,6 @@ export function writePaperMigration(plan) {
|
|
|
1133
1173
|
};
|
|
1134
1174
|
}
|
|
1135
1175
|
|
|
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
1176
|
function state(id, status, reason, evidence = []) {
|
|
1157
1177
|
return {
|
|
1158
1178
|
id,
|
|
@@ -1591,20 +1611,6 @@ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
|
|
|
1591
1611
|
};
|
|
1592
1612
|
}
|
|
1593
1613
|
|
|
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
1614
|
function validatePaperProvisioningAuthority(cwd) {
|
|
1609
1615
|
const authorityPath = path.resolve(cwd, PAPER_PATHS.provisioningAuthority);
|
|
1610
1616
|
const source = readJson(authorityPath);
|
|
@@ -2527,23 +2533,6 @@ export function createPaperBuildPlan({
|
|
|
2527
2533
|
};
|
|
2528
2534
|
}
|
|
2529
2535
|
|
|
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
2536
|
function resolvePaperChannelRef(cwd, ref) {
|
|
2548
2537
|
const candidates = [
|
|
2549
2538
|
{
|
|
@@ -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
|
};
|