@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.
- package/AGENTS.md +14 -5
- package/README.md +18 -0
- package/bin/buildchain.mjs +136 -73
- package/bin/internal/trust-release-cli.mjs +15 -537
- package/bin/internal/trust-release-command-handlers.mjs +14 -0
- package/bin/internal/trust-release-inspection-handlers.mjs +175 -0
- package/bin/internal/trust-release-release-handlers.mjs +317 -0
- package/bin/internal/trust-release-verification-handlers.mjs +306 -0
- package/dist/site/buildchain-contract.json +36 -25
- package/dist/site/buildchain-site.json +1474 -61
- package/dist/site/capability-registry.json +8 -5
- package/dist/site/cli-registry.json +1869 -0
- package/dist/site/controller-registry.json +16 -3
- package/dist/site/kfd-claims.json +84 -10
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +49 -6
- package/dist/site/node-api-registry.json +17802 -11
- package/dist/site/page-registry.json +1441 -59
- package/dist/site/public-surface-audit.json +2863 -358
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-model.json +7 -0
- package/dist/site/site-manifest.json +34 -10
- package/dist/site/workflow-registry.json +13 -5
- package/docs/MAP.md +21 -6
- package/docs/cli-reference.md +1936 -0
- package/docs/cli.md +15 -1
- package/docs/getting-started.md +167 -0
- package/docs/install.md +7 -8
- package/docs/node-api-reference.md +1952 -0
- package/docs/release-propagation.md +166 -8
- package/docs/site-bundle-contract.md +10 -4
- package/docs/versioning.md +5 -4
- package/package.json +8 -5
- package/packages/core/buildchain-agent-manuals.js +37 -0
- package/packages/core/buildchain-kfd-claims.js +4 -37
- package/packages/core/controller-evidence.js +8 -1
- package/packages/core/index.js +1 -13
- package/packages/core/paper-agent-entry.js +14 -11
- package/packages/core/paper-fleet.js +34 -7
- package/packages/core/paper-npm-bootstrap.js +492 -0
- package/packages/core/paper-repository.js +1 -0
- package/packages/core/paper.js +356 -674
- package/packages/core/public-surface-cli.js +12 -1
- package/packages/core/publication-package.js +7 -0
- package/packages/core/release-passport.js +67 -71
- package/packages/core/release-propagation-common.js +64 -0
- package/packages/core/release-propagation-execution-profile.js +59 -0
- package/packages/core/release-propagation-release.js +196 -0
- package/packages/core/release-propagation-stage-evidence.js +364 -0
- package/packages/core/release-propagation-work-capture.js +64 -0
- package/packages/core/release-propagation-work-constants.js +34 -0
- package/packages/core/release-propagation-work-control.js +203 -0
- package/packages/core/release-propagation-work-transitions.js +145 -0
- package/packages/core/release-propagation-work.js +517 -0
- package/packages/core/release-propagation.js +34 -158
- package/scripts/aggregate-build-summary.mjs +67 -4
- package/scripts/auditable-demo-renditions.mjs +131 -0
- package/scripts/auditable-demo.mjs +88 -91
- package/scripts/aws-windows-jit-controller-core.mjs +269 -0
- package/scripts/aws-windows-jit-controller.mjs +502 -0
- package/scripts/check-internal-architecture.mjs +178 -52
- package/scripts/check-inventory.mjs +2 -2
- package/scripts/check-maintainability.mjs +76 -17
- package/scripts/generate-public-reference.mjs +68 -0
- package/scripts/generate-site-bundle.mjs +35 -41
- package/scripts/maintainability-metrics.mjs +24 -4
- package/scripts/paper-work-fleet-cli.mjs +22 -3
- package/scripts/public-reference.mjs +557 -0
- package/scripts/release-propagation.mjs +126 -0
- package/scripts/resolve-artifact-transfer-mode.mjs +117 -0
- package/scripts/site-reference-registry.mjs +174 -0
- package/scripts/verify-golden-path.mjs +169 -0
- package/scripts/web-surface-core.mjs +46 -207
- package/scripts/web-surface-routing.mjs +286 -0
package/packages/core/paper.js
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
PAPER_AGENT_ENTRY_CONTRACT,
|
|
36
36
|
collectPaperAgentEntry,
|
|
37
37
|
paperAgentEntryFiles,
|
|
38
|
+
resolvePaperBuildchainSha,
|
|
38
39
|
} from "./paper-agent-entry.js";
|
|
39
40
|
import {
|
|
40
41
|
managedPaperPackageJson,
|
|
@@ -44,6 +45,7 @@ import {
|
|
|
44
45
|
scaffoldPackageJson,
|
|
45
46
|
scaffoldReadme,
|
|
46
47
|
} from "./paper-scaffold-content.js";
|
|
48
|
+
import { executePaperNpmBootstrap as executePaperNpmBootstrapOperation } from "./paper-npm-bootstrap.js";
|
|
47
49
|
|
|
48
50
|
export { PAPER_PATHS, resolvePaperRepository } from "./paper-repository.js";
|
|
49
51
|
export {
|
|
@@ -72,6 +74,7 @@ export {
|
|
|
72
74
|
collectPaperFleetAudit,
|
|
73
75
|
discoverPaperFleet,
|
|
74
76
|
planPaperFleetUpdate,
|
|
77
|
+
paperFleetTransitionWorkspace,
|
|
75
78
|
writePaperFleetUpdate,
|
|
76
79
|
} from "./paper-fleet.js";
|
|
77
80
|
|
|
@@ -111,6 +114,7 @@ const PAPER_SCAFFOLD_PATHS = Object.freeze([
|
|
|
111
114
|
PAPER_PATHS.buildWorkflow,
|
|
112
115
|
PAPER_PATHS.verifyWorkflow,
|
|
113
116
|
PAPER_PATHS.releaseWorkflow,
|
|
117
|
+
PAPER_PATHS.pnpmWorkspace,
|
|
114
118
|
PAPER_PATHS.provisioningAuthority,
|
|
115
119
|
"Makefile",
|
|
116
120
|
"package.json",
|
|
@@ -132,6 +136,7 @@ const DEFAULT_TOOLCHAIN_COMMAND = "latexmk -pdf -outdir=_build paper/main.tex";
|
|
|
132
136
|
const SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/i;
|
|
133
137
|
const GIT_SHA_PATTERN = /^[0-9a-f]{40}$/i;
|
|
134
138
|
const PACKAGE_PATTERN = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i;
|
|
139
|
+
const BUILDCHAIN_PACKAGE_NAME = "@kungfu-tech/buildchain";
|
|
135
140
|
|
|
136
141
|
function toPosix(value) {
|
|
137
142
|
return String(value || "")
|
|
@@ -147,6 +152,66 @@ function jsonText(value) {
|
|
|
147
152
|
return `${JSON.stringify(value, null, 2)}\n`;
|
|
148
153
|
}
|
|
149
154
|
|
|
155
|
+
function paperPnpmWorkspace(current, buildchainVersion) {
|
|
156
|
+
const entry = `${BUILDCHAIN_PACKAGE_NAME}@${buildchainVersion}`;
|
|
157
|
+
const source = String(current || "");
|
|
158
|
+
const lines = source ? source.replace(/\r\n/g, "\n").split("\n") : [];
|
|
159
|
+
if (lines.at(-1) === "") lines.pop();
|
|
160
|
+
const keyLines = lines
|
|
161
|
+
.map((line, index) =>
|
|
162
|
+
/^minimumReleaseAgeExclude:\s*(?:#.*)?$/.test(line) ? index : -1,
|
|
163
|
+
)
|
|
164
|
+
.filter((index) => index >= 0);
|
|
165
|
+
const unsupportedKey = lines.some(
|
|
166
|
+
(line) =>
|
|
167
|
+
/^minimumReleaseAgeExclude\s*:/.test(line) &&
|
|
168
|
+
!/^minimumReleaseAgeExclude:\s*(?:#.*)?$/.test(line),
|
|
169
|
+
);
|
|
170
|
+
if (unsupportedKey || keyLines.length > 1) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
"paper migration requires minimumReleaseAgeExclude to be one top-level block sequence",
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
if (keyLines.length === 0) {
|
|
176
|
+
const prefix = lines.length > 0 ? [...lines, ""] : [];
|
|
177
|
+
return `${[...prefix, "minimumReleaseAgeExclude:", ` - '${entry}'`].join(
|
|
178
|
+
"\n",
|
|
179
|
+
)}\n`;
|
|
180
|
+
}
|
|
181
|
+
const keyIndex = keyLines[0];
|
|
182
|
+
let blockEnd = lines.length;
|
|
183
|
+
for (let index = keyIndex + 1; index < lines.length; index += 1) {
|
|
184
|
+
if (/^[^\s#]/.test(lines[index])) {
|
|
185
|
+
blockEnd = index;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const retained = [];
|
|
190
|
+
for (const line of lines.slice(keyIndex + 1, blockEnd)) {
|
|
191
|
+
if (!line.trim() || /^\s*#/.test(line)) {
|
|
192
|
+
retained.push(line);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const item = line.match(/^\s*-\s+(.+?)\s*(?:#.*)?$/);
|
|
196
|
+
if (!item) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
"paper migration requires minimumReleaseAgeExclude to contain scalar package entries",
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
const value = item[1]
|
|
202
|
+
.trim()
|
|
203
|
+
.replace(/^'(.*)'$/, "$1")
|
|
204
|
+
.replace(/^"(.*)"$/, "$1");
|
|
205
|
+
if (!value.startsWith(`${BUILDCHAIN_PACKAGE_NAME}@`)) retained.push(line);
|
|
206
|
+
}
|
|
207
|
+
return `${[
|
|
208
|
+
...lines.slice(0, keyIndex + 1),
|
|
209
|
+
...retained,
|
|
210
|
+
` - '${entry}'`,
|
|
211
|
+
...lines.slice(blockEnd),
|
|
212
|
+
].join("\n")}\n`;
|
|
213
|
+
}
|
|
214
|
+
|
|
150
215
|
function existingFileFact(cwd, relativePath) {
|
|
151
216
|
const filePath = path.resolve(cwd, relativePath);
|
|
152
217
|
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
|
|
@@ -177,8 +242,11 @@ function buildchainPackageIdentity(buildchainRoot, explicitVersion = "") {
|
|
|
177
242
|
};
|
|
178
243
|
}
|
|
179
244
|
|
|
180
|
-
function
|
|
181
|
-
|
|
245
|
+
export function resolvePaperRuntimeGitSha(
|
|
246
|
+
buildchainRoot,
|
|
247
|
+
buildchainVersion = "",
|
|
248
|
+
) {
|
|
249
|
+
const value = resolvePaperBuildchainSha(buildchainRoot);
|
|
182
250
|
if (GIT_SHA_PATTERN.test(value)) return value;
|
|
183
251
|
const identity = buildchainPackageIdentity(buildchainRoot, buildchainVersion);
|
|
184
252
|
if (!identity.version) return "";
|
|
@@ -416,6 +484,7 @@ ${passportInput} secrets:
|
|
|
416
484
|
BUILDCHAIN_GENERATED_WRITE_APP_CLIENT_ID: \${{ secrets.BUILDCHAIN_GENERATED_WRITE_APP_CLIENT_ID }}
|
|
417
485
|
BUILDCHAIN_GENERATED_WRITE_APP_PRIVATE_KEY: \${{ secrets.BUILDCHAIN_GENERATED_WRITE_APP_PRIVATE_KEY }}
|
|
418
486
|
BUILDCHAIN_GENERATED_WRITE_TOKEN: \${{ secrets.BUILDCHAIN_GENERATED_WRITE_TOKEN }}
|
|
487
|
+
BUILDCHAIN_PROMOTION_TOKEN: \${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}
|
|
419
488
|
`;
|
|
420
489
|
}
|
|
421
490
|
|
|
@@ -598,6 +667,7 @@ function scaffoldFiles({
|
|
|
598
667
|
[PAPER_PATHS.buildWorkflow, buildWorkflow],
|
|
599
668
|
[PAPER_PATHS.verifyWorkflow, verifyWorkflow],
|
|
600
669
|
[PAPER_PATHS.releaseWorkflow, releaseWorkflow],
|
|
670
|
+
[PAPER_PATHS.pnpmWorkspace, paperPnpmWorkspace("", buildchainVersion)],
|
|
601
671
|
[PAPER_PATHS.provisioningAuthority, jsonText(provisioningAuthority)],
|
|
602
672
|
...agentEntry,
|
|
603
673
|
[
|
|
@@ -661,7 +731,8 @@ export function planPaperScaffold({
|
|
|
661
731
|
buildchainVersion,
|
|
662
732
|
);
|
|
663
733
|
const runtimeSha =
|
|
664
|
-
buildchainSha ||
|
|
734
|
+
buildchainSha ||
|
|
735
|
+
resolvePaperRuntimeGitSha(buildchainRoot, runtimeIdentity.version);
|
|
665
736
|
if (!GIT_SHA_PATTERN.test(runtimeSha)) {
|
|
666
737
|
throw new Error(
|
|
667
738
|
"paper scaffold cannot resolve the exact Buildchain runtime SHA; pass buildchainSha or use a published Buildchain package with npm gitHead provenance",
|
|
@@ -854,7 +925,8 @@ function migrationFiles({
|
|
|
854
925
|
"paper publish package",
|
|
855
926
|
);
|
|
856
927
|
const runtimeSha =
|
|
857
|
-
buildchainSha ||
|
|
928
|
+
buildchainSha ||
|
|
929
|
+
resolvePaperRuntimeGitSha(buildchainRoot, buildchainVersion);
|
|
858
930
|
if (!GIT_SHA_PATTERN.test(runtimeSha)) {
|
|
859
931
|
throw new Error("paper migration requires an exact Buildchain source SHA");
|
|
860
932
|
}
|
|
@@ -917,12 +989,20 @@ function migrationFiles({
|
|
|
917
989
|
currentPackage.value,
|
|
918
990
|
runtimeIdentity.version,
|
|
919
991
|
);
|
|
992
|
+
const pnpmWorkspacePath = path.resolve(cwd, PAPER_PATHS.pnpmWorkspace);
|
|
993
|
+
const pnpmWorkspace = paperPnpmWorkspace(
|
|
994
|
+
fs.existsSync(pnpmWorkspacePath)
|
|
995
|
+
? fs.readFileSync(pnpmWorkspacePath, "utf8")
|
|
996
|
+
: "",
|
|
997
|
+
runtimeIdentity.version,
|
|
998
|
+
);
|
|
920
999
|
const files = new Map([
|
|
921
1000
|
[PAPER_PATHS.contractLock, contractLockText],
|
|
922
1001
|
[PAPER_PATHS.versionPin, `${runtimeIdentity.version}\n`],
|
|
923
1002
|
[PAPER_PATHS.buildWorkflow, buildWorkflow],
|
|
924
1003
|
[PAPER_PATHS.verifyWorkflow, verifyWorkflow],
|
|
925
1004
|
[PAPER_PATHS.releaseWorkflow, releaseWorkflow],
|
|
1005
|
+
[PAPER_PATHS.pnpmWorkspace, pnpmWorkspace],
|
|
926
1006
|
[PAPER_PATHS.provisioningAuthority, jsonText(provisioningAuthority)],
|
|
927
1007
|
...agentEntry,
|
|
928
1008
|
["package.json", jsonText(packageJson)],
|
|
@@ -1224,8 +1304,7 @@ function releaseTransactionFacts(candidates, version) {
|
|
|
1224
1304
|
};
|
|
1225
1305
|
}
|
|
1226
1306
|
|
|
1227
|
-
|
|
1228
|
-
const resolvedCwd = path.resolve(cwd);
|
|
1307
|
+
function collectPaperGovernanceFacts(resolvedCwd) {
|
|
1229
1308
|
const configResult = paperConfig(resolvedCwd);
|
|
1230
1309
|
const loaded = configResult.loaded;
|
|
1231
1310
|
const publication = loaded?.config?.publication;
|
|
@@ -1266,7 +1345,13 @@ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
|
|
|
1266
1345
|
gitRepo.ok &&
|
|
1267
1346
|
gitRepo.stdout === "true",
|
|
1268
1347
|
);
|
|
1348
|
+
return {
|
|
1349
|
+
configResult, loaded, publication, packageName, version, configFact,
|
|
1350
|
+
scaffoldFacts, contractLockError, scaffoldOk, governedOk,
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1269
1353
|
|
|
1354
|
+
function collectPaperEvidenceFacts(resolvedCwd, publication, version) {
|
|
1270
1355
|
const admissionInputs = [PAPER_PATHS.capability, PAPER_PATHS.admission].map(
|
|
1271
1356
|
(relativePath) => ({
|
|
1272
1357
|
relativePath,
|
|
@@ -1324,6 +1409,82 @@ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
|
|
|
1324
1409
|
visibility.value,
|
|
1325
1410
|
"production",
|
|
1326
1411
|
);
|
|
1412
|
+
return {
|
|
1413
|
+
admitted, bootstrapValue, trustValue, content, reproducibility, deterministic,
|
|
1414
|
+
sealedOk, sealedError, candidates, transactionFacts, packagePublished,
|
|
1415
|
+
alphaComplete, stagingVisible, productionVisible,
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
function paperStatusBlockingNextActions({ stateMap, deterministic, transactionFacts }) {
|
|
1420
|
+
const actions = [];
|
|
1421
|
+
if (!stateMap.scaffolded.satisfied) {
|
|
1422
|
+
actions.push({
|
|
1423
|
+
id: "scaffold-paper",
|
|
1424
|
+
command: "buildchain paper scaffold --help",
|
|
1425
|
+
description: "Plan a no-overwrite Buildchain paper scaffold.",
|
|
1426
|
+
});
|
|
1427
|
+
} else if (!stateMap.governed.satisfied) {
|
|
1428
|
+
actions.push({
|
|
1429
|
+
id: "restore-governance",
|
|
1430
|
+
command: "buildchain paper preflight --json",
|
|
1431
|
+
description:
|
|
1432
|
+
"Repair the Git/contract-lock governance checks reported by preflight.",
|
|
1433
|
+
});
|
|
1434
|
+
} else if (!stateMap["content-ready"].satisfied) {
|
|
1435
|
+
actions.push({
|
|
1436
|
+
id: "complete-paper-content",
|
|
1437
|
+
command: "make check",
|
|
1438
|
+
description: "Add every declared paper source and metadata input.",
|
|
1439
|
+
});
|
|
1440
|
+
} else if (!deterministic) {
|
|
1441
|
+
actions.push({
|
|
1442
|
+
id: "build-reproducible-artifact",
|
|
1443
|
+
command: "buildchain paper build --execute --json",
|
|
1444
|
+
description:
|
|
1445
|
+
"Run the existing two-clean-build reproducibility gate and promote exact bytes.",
|
|
1446
|
+
});
|
|
1447
|
+
} else if (
|
|
1448
|
+
!stateMap.bootstrapped.satisfied ||
|
|
1449
|
+
!stateMap["trust-bound"].satisfied
|
|
1450
|
+
) {
|
|
1451
|
+
actions.push({
|
|
1452
|
+
id: "bootstrap-npm",
|
|
1453
|
+
command: "buildchain paper bootstrap npm --json",
|
|
1454
|
+
description:
|
|
1455
|
+
"Inspect the dry-run npm bootstrap and Trusted Publishing handoff.",
|
|
1456
|
+
});
|
|
1457
|
+
} else if (!stateMap["alpha-complete"].satisfied) {
|
|
1458
|
+
actions.push({
|
|
1459
|
+
id: transactionFacts.selected ? "resume-alpha" : "start-alpha",
|
|
1460
|
+
command: transactionFacts.selected
|
|
1461
|
+
? "buildchain paper resume --json"
|
|
1462
|
+
: "buildchain paper alpha --json",
|
|
1463
|
+
description: transactionFacts.selected
|
|
1464
|
+
? "Resume the exact sealed release transaction."
|
|
1465
|
+
: "Plan the protected dev-to-alpha publication PR.",
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1468
|
+
return actions;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
export function collectPaperStatus({ cwd = process.cwd() } = {}) {
|
|
1472
|
+
const resolvedCwd = path.resolve(cwd);
|
|
1473
|
+
const governance = collectPaperGovernanceFacts(resolvedCwd);
|
|
1474
|
+
const evidence = collectPaperEvidenceFacts(
|
|
1475
|
+
resolvedCwd,
|
|
1476
|
+
governance.publication,
|
|
1477
|
+
governance.version,
|
|
1478
|
+
);
|
|
1479
|
+
const {
|
|
1480
|
+
configResult, loaded, publication, packageName, version, configFact,
|
|
1481
|
+
scaffoldFacts, contractLockError, scaffoldOk, governedOk,
|
|
1482
|
+
} = governance;
|
|
1483
|
+
const {
|
|
1484
|
+
admitted, bootstrapValue, trustValue, content, reproducibility, deterministic,
|
|
1485
|
+
sealedOk, sealedError, candidates, transactionFacts, packagePublished,
|
|
1486
|
+
alphaComplete, stagingVisible, productionVisible,
|
|
1487
|
+
} = evidence;
|
|
1327
1488
|
|
|
1328
1489
|
const states = [
|
|
1329
1490
|
state(
|
|
@@ -1433,54 +1594,7 @@ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
|
|
|
1433
1594
|
const highestEvidenceState =
|
|
1434
1595
|
[...states].reverse().find((entry) => entry.satisfied)?.id || "none";
|
|
1435
1596
|
const stateMap = Object.fromEntries(states.map((entry) => [entry.id, entry]));
|
|
1436
|
-
const blockingNextActions =
|
|
1437
|
-
if (!stateMap.scaffolded.satisfied) {
|
|
1438
|
-
blockingNextActions.push({
|
|
1439
|
-
id: "scaffold-paper",
|
|
1440
|
-
command: "buildchain paper scaffold --help",
|
|
1441
|
-
description: "Plan a no-overwrite Buildchain paper scaffold.",
|
|
1442
|
-
});
|
|
1443
|
-
} else if (!stateMap.governed.satisfied) {
|
|
1444
|
-
blockingNextActions.push({
|
|
1445
|
-
id: "restore-governance",
|
|
1446
|
-
command: "buildchain paper preflight --json",
|
|
1447
|
-
description:
|
|
1448
|
-
"Repair the Git/contract-lock governance checks reported by preflight.",
|
|
1449
|
-
});
|
|
1450
|
-
} else if (!stateMap["content-ready"].satisfied) {
|
|
1451
|
-
blockingNextActions.push({
|
|
1452
|
-
id: "complete-paper-content",
|
|
1453
|
-
command: "make check",
|
|
1454
|
-
description: "Add every declared paper source and metadata input.",
|
|
1455
|
-
});
|
|
1456
|
-
} else if (!deterministic) {
|
|
1457
|
-
blockingNextActions.push({
|
|
1458
|
-
id: "build-reproducible-artifact",
|
|
1459
|
-
command: "buildchain paper build --execute --json",
|
|
1460
|
-
description:
|
|
1461
|
-
"Run the existing two-clean-build reproducibility gate and promote exact bytes.",
|
|
1462
|
-
});
|
|
1463
|
-
} else if (
|
|
1464
|
-
!stateMap.bootstrapped.satisfied ||
|
|
1465
|
-
!stateMap["trust-bound"].satisfied
|
|
1466
|
-
) {
|
|
1467
|
-
blockingNextActions.push({
|
|
1468
|
-
id: "bootstrap-npm",
|
|
1469
|
-
command: "buildchain paper bootstrap npm --json",
|
|
1470
|
-
description:
|
|
1471
|
-
"Inspect the dry-run npm bootstrap and Trusted Publishing handoff.",
|
|
1472
|
-
});
|
|
1473
|
-
} else if (!stateMap["alpha-complete"].satisfied) {
|
|
1474
|
-
blockingNextActions.push({
|
|
1475
|
-
id: transactionFacts.selected ? "resume-alpha" : "start-alpha",
|
|
1476
|
-
command: transactionFacts.selected
|
|
1477
|
-
? "buildchain paper resume --json"
|
|
1478
|
-
: "buildchain paper alpha --json",
|
|
1479
|
-
description: transactionFacts.selected
|
|
1480
|
-
? "Resume the exact sealed release transaction."
|
|
1481
|
-
: "Plan the protected dev-to-alpha publication PR.",
|
|
1482
|
-
});
|
|
1483
|
-
}
|
|
1597
|
+
const blockingNextActions = paperStatusBlockingNextActions({ stateMap, deterministic, transactionFacts });
|
|
1484
1598
|
return {
|
|
1485
1599
|
schemaVersion: 1,
|
|
1486
1600
|
contract: PAPER_STATUS_CONTRACT,
|
|
@@ -1734,7 +1848,8 @@ function runtimeFacts({
|
|
|
1734
1848
|
version: identity.version,
|
|
1735
1849
|
ref: buildchainRef,
|
|
1736
1850
|
resolvedSha:
|
|
1737
|
-
buildchainSha ||
|
|
1851
|
+
buildchainSha ||
|
|
1852
|
+
resolvePaperRuntimeGitSha(buildchainRoot, identity.version),
|
|
1738
1853
|
contract: contractWorld.contract,
|
|
1739
1854
|
contractDigest: contractWorld.contractDigest,
|
|
1740
1855
|
compatibilityDigest: contractWorld.compatibilityDigest,
|
|
@@ -2043,146 +2158,8 @@ function localToolchainObservation(cwd, publication) {
|
|
|
2043
2158
|
};
|
|
2044
2159
|
}
|
|
2045
2160
|
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
buildchainRoot = process.cwd(),
|
|
2049
|
-
buildchainVersion = "",
|
|
2050
|
-
buildchainRef = "v3",
|
|
2051
|
-
buildchainSha = "",
|
|
2052
|
-
registry = NPM_REGISTRY,
|
|
2053
|
-
offline = false,
|
|
2054
|
-
agentEntryMode = "contract",
|
|
2055
|
-
} = {}) {
|
|
2056
|
-
const resolvedCwd = path.resolve(cwd);
|
|
2057
|
-
const status = collectPaperStatus({ cwd: resolvedCwd });
|
|
2058
|
-
const provisioning = validatePaperProvisioningAuthority(resolvedCwd);
|
|
2059
|
-
const configResult = paperConfig(resolvedCwd);
|
|
2060
|
-
let validation;
|
|
2061
|
-
let validationError = "";
|
|
2062
|
-
try {
|
|
2063
|
-
validation = validateBuildchainConfig(resolvedCwd, {
|
|
2064
|
-
requireLifecycleStages: ["verify"],
|
|
2065
|
-
});
|
|
2066
|
-
} catch (error) {
|
|
2067
|
-
validationError = error.message;
|
|
2068
|
-
}
|
|
2069
|
-
const runtime = runtimeFacts({
|
|
2070
|
-
buildchainRoot,
|
|
2071
|
-
buildchainVersion,
|
|
2072
|
-
buildchainRef: provisioning.value?.runtime?.ref || buildchainRef,
|
|
2073
|
-
buildchainSha,
|
|
2074
|
-
});
|
|
2075
|
-
const agentEntry = collectPaperAgentEntry({
|
|
2076
|
-
cwd: resolvedCwd,
|
|
2077
|
-
buildchainSha: runtime.resolvedSha,
|
|
2078
|
-
mode: agentEntryMode,
|
|
2079
|
-
});
|
|
2080
|
-
const lockPath = path.resolve(resolvedCwd, PAPER_PATHS.contractLock);
|
|
2081
|
-
let lockEvaluation = {
|
|
2082
|
-
status: "missing-lock",
|
|
2083
|
-
compatible: false,
|
|
2084
|
-
drift: false,
|
|
2085
|
-
reasons: ["Buildchain contract lock is missing"],
|
|
2086
|
-
};
|
|
2087
|
-
try {
|
|
2088
|
-
const lock = readBuildchainContractLock(lockPath);
|
|
2089
|
-
if (lock) {
|
|
2090
|
-
const current = runtimeContractWorld(buildchainRoot);
|
|
2091
|
-
lockEvaluation = evaluateBuildchainContractLock({
|
|
2092
|
-
lock,
|
|
2093
|
-
current,
|
|
2094
|
-
runtimeRef: runtime.ref,
|
|
2095
|
-
runtimeSha: runtime.resolvedSha,
|
|
2096
|
-
runtimeClass: /alpha/i.test(runtime.ref) ? "alpha" : "stable",
|
|
2097
|
-
});
|
|
2098
|
-
}
|
|
2099
|
-
} catch (error) {
|
|
2100
|
-
lockEvaluation = {
|
|
2101
|
-
status: "invalid-lock",
|
|
2102
|
-
compatible: false,
|
|
2103
|
-
drift: false,
|
|
2104
|
-
reasons: [error.message],
|
|
2105
|
-
};
|
|
2106
|
-
}
|
|
2107
|
-
const source = {
|
|
2108
|
-
repositoryRoot: gitValue(resolvedCwd, ["rev-parse", "--show-toplevel"]),
|
|
2109
|
-
head: gitValue(resolvedCwd, ["rev-parse", "HEAD"]),
|
|
2110
|
-
tree: gitValue(resolvedCwd, ["rev-parse", "HEAD^{tree}"]),
|
|
2111
|
-
branch: gitValue(resolvedCwd, ["branch", "--show-current"]),
|
|
2112
|
-
clean: gitResult(resolvedCwd, ["status", "--porcelain"]).stdout === "",
|
|
2113
|
-
};
|
|
2114
|
-
const packageName = status.identity.package;
|
|
2115
|
-
const repository = status.identity.repository;
|
|
2116
|
-
const expectedPublisher = expectedPaperTrustedPublisher(provisioning.value, {
|
|
2117
|
-
repository,
|
|
2118
|
-
workflow: path.posix.basename(PAPER_PATHS.releaseWorkflow),
|
|
2119
|
-
});
|
|
2120
|
-
const npm =
|
|
2121
|
-
offline || !packageName
|
|
2122
|
-
? {
|
|
2123
|
-
package: {
|
|
2124
|
-
status: "unknown",
|
|
2125
|
-
exists: null,
|
|
2126
|
-
version: "",
|
|
2127
|
-
registry,
|
|
2128
|
-
errorCode: offline ? "offline" : "package-unresolved",
|
|
2129
|
-
},
|
|
2130
|
-
auth: {
|
|
2131
|
-
status: "unknown",
|
|
2132
|
-
authenticated: null,
|
|
2133
|
-
identity: "",
|
|
2134
|
-
errorCode: offline ? "offline" : "package-unresolved",
|
|
2135
|
-
},
|
|
2136
|
-
trust: {
|
|
2137
|
-
status: "unknown",
|
|
2138
|
-
configured: null,
|
|
2139
|
-
publishers: [],
|
|
2140
|
-
errorCode: offline ? "offline" : "package-unresolved",
|
|
2141
|
-
},
|
|
2142
|
-
}
|
|
2143
|
-
: {
|
|
2144
|
-
package: liveNpmPackageObservation(
|
|
2145
|
-
packageName,
|
|
2146
|
-
registry,
|
|
2147
|
-
resolvedCwd,
|
|
2148
|
-
),
|
|
2149
|
-
auth: liveNpmAuthObservation(registry, resolvedCwd),
|
|
2150
|
-
trust: liveNpmTrustObservation(
|
|
2151
|
-
packageName,
|
|
2152
|
-
registry,
|
|
2153
|
-
resolvedCwd,
|
|
2154
|
-
expectedPublisher,
|
|
2155
|
-
),
|
|
2156
|
-
};
|
|
2157
|
-
const repositoryPermissions = offline
|
|
2158
|
-
? {
|
|
2159
|
-
status: "unknown",
|
|
2160
|
-
repository,
|
|
2161
|
-
canWrite: null,
|
|
2162
|
-
errorCode: "offline",
|
|
2163
|
-
}
|
|
2164
|
-
: liveRepositoryPermissionObservation(repository, resolvedCwd);
|
|
2165
|
-
const repositoryActions = offline
|
|
2166
|
-
? {
|
|
2167
|
-
status: "unknown",
|
|
2168
|
-
defaultWorkflowPermissions: "",
|
|
2169
|
-
canApprovePullRequestReviews: null,
|
|
2170
|
-
errorCode: "offline",
|
|
2171
|
-
}
|
|
2172
|
-
: liveRepositoryActionsPolicyObservation(repository, resolvedCwd);
|
|
2173
|
-
const generatedWriteAuthority = offline
|
|
2174
|
-
? {
|
|
2175
|
-
status: "unknown",
|
|
2176
|
-
configured: null,
|
|
2177
|
-
mode: "",
|
|
2178
|
-
errorCode: "offline",
|
|
2179
|
-
}
|
|
2180
|
-
: liveGeneratedWriteAuthorityObservation(repository, resolvedCwd);
|
|
2181
|
-
const toolchain = localToolchainObservation(
|
|
2182
|
-
resolvedCwd,
|
|
2183
|
-
configResult.loaded?.config?.publication,
|
|
2184
|
-
);
|
|
2185
|
-
const checks = [
|
|
2161
|
+
function paperPreflightLocalChecks({ agentEntry, provisioning, validationError, source, toolchain, runtime, lockEvaluation }) {
|
|
2162
|
+
return [
|
|
2186
2163
|
...agentEntry.checks.map((entry) => ({
|
|
2187
2164
|
...entry,
|
|
2188
2165
|
blocking: true,
|
|
@@ -2249,6 +2226,11 @@ export function collectPaperPreflight({
|
|
|
2249
2226
|
? `Buildchain runtime contract is ${lockEvaluation.status}.`
|
|
2250
2227
|
: `Buildchain runtime contract is not admitted: ${(lockEvaluation.reasons || []).join("; ")}`,
|
|
2251
2228
|
},
|
|
2229
|
+
];
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
function paperPreflightRepositoryChecks({ repositoryPermissions, repositoryActions, generatedWriteAuthority }) {
|
|
2233
|
+
return [
|
|
2252
2234
|
{
|
|
2253
2235
|
id: "repository.write-permission",
|
|
2254
2236
|
status:
|
|
@@ -2302,6 +2284,11 @@ export function collectPaperPreflight({
|
|
|
2302
2284
|
? "No GitHub App or compatible narrow generated-write credential is configured."
|
|
2303
2285
|
: `Generated-write authority is unknown (${generatedWriteAuthority.errorCode}).`,
|
|
2304
2286
|
},
|
|
2287
|
+
];
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
function paperPreflightPublicationChecks({ npm, status }) {
|
|
2291
|
+
return [
|
|
2305
2292
|
{
|
|
2306
2293
|
id: "npm.package",
|
|
2307
2294
|
status:
|
|
@@ -2358,24 +2345,19 @@ export function collectPaperPreflight({
|
|
|
2358
2345
|
: `${status.conflicts.length} release-state conflict(s) require repair.`,
|
|
2359
2346
|
},
|
|
2360
2347
|
];
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
const
|
|
2365
|
-
(entry) =>
|
|
2366
|
-
entry.scope === "external-mutation" &&
|
|
2367
|
-
["fail", "pending", "unknown"].includes(entry.status),
|
|
2368
|
-
);
|
|
2369
|
-
const nextActions = [];
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
function paperPreflightNextActions({ validationError, source, lockEvaluation, npm, repositoryActions, generatedWriteAuthority, status }) {
|
|
2351
|
+
const actions = [];
|
|
2370
2352
|
if (validationError) {
|
|
2371
|
-
|
|
2353
|
+
actions.push({
|
|
2372
2354
|
id: "repair-config",
|
|
2373
2355
|
command: "buildchain validate --require-lifecycle-stages verify",
|
|
2374
2356
|
description: validationError,
|
|
2375
2357
|
});
|
|
2376
2358
|
}
|
|
2377
2359
|
if (!source.clean) {
|
|
2378
|
-
|
|
2360
|
+
actions.push({
|
|
2379
2361
|
id: "commit-source",
|
|
2380
2362
|
command: "git status --short",
|
|
2381
2363
|
description:
|
|
@@ -2383,7 +2365,7 @@ export function collectPaperPreflight({
|
|
|
2383
2365
|
});
|
|
2384
2366
|
}
|
|
2385
2367
|
if (!lockEvaluation.compatible) {
|
|
2386
|
-
|
|
2368
|
+
actions.push({
|
|
2387
2369
|
id: "refresh-contract-lock",
|
|
2388
2370
|
command: "buildchain paper scaffold --json",
|
|
2389
2371
|
description:
|
|
@@ -2391,7 +2373,7 @@ export function collectPaperPreflight({
|
|
|
2391
2373
|
});
|
|
2392
2374
|
}
|
|
2393
2375
|
if (npm.package.exists === false || npm.trust.configured === false) {
|
|
2394
|
-
|
|
2376
|
+
actions.push({
|
|
2395
2377
|
id: "bootstrap-npm",
|
|
2396
2378
|
command: "buildchain paper bootstrap npm --json",
|
|
2397
2379
|
description:
|
|
@@ -2403,7 +2385,7 @@ export function collectPaperPreflight({
|
|
|
2403
2385
|
(repositoryActions.defaultWorkflowPermissions !== "read" ||
|
|
2404
2386
|
repositoryActions.canApprovePullRequestReviews !== false)
|
|
2405
2387
|
) {
|
|
2406
|
-
|
|
2388
|
+
actions.push({
|
|
2407
2389
|
id: "constrain-repository-actions",
|
|
2408
2390
|
command: "",
|
|
2409
2391
|
description:
|
|
@@ -2411,7 +2393,7 @@ export function collectPaperPreflight({
|
|
|
2411
2393
|
});
|
|
2412
2394
|
}
|
|
2413
2395
|
if (generatedWriteAuthority.configured === false) {
|
|
2414
|
-
|
|
2396
|
+
actions.push({
|
|
2415
2397
|
id: "configure-generated-write-authority",
|
|
2416
2398
|
command: "",
|
|
2417
2399
|
description:
|
|
@@ -2419,12 +2401,149 @@ export function collectPaperPreflight({
|
|
|
2419
2401
|
});
|
|
2420
2402
|
}
|
|
2421
2403
|
if (status.deterministicBuild.status !== "qualifying") {
|
|
2422
|
-
|
|
2404
|
+
actions.push({
|
|
2423
2405
|
id: "build-paper",
|
|
2424
2406
|
command: "buildchain paper build --execute --json",
|
|
2425
2407
|
description: "Run the existing qualifying two-clean-build gate.",
|
|
2426
2408
|
});
|
|
2427
2409
|
}
|
|
2410
|
+
return actions;
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
export function collectPaperPreflight({
|
|
2414
|
+
cwd = process.cwd(),
|
|
2415
|
+
buildchainRoot = process.cwd(),
|
|
2416
|
+
buildchainVersion = "",
|
|
2417
|
+
buildchainRef = "v3",
|
|
2418
|
+
buildchainSha = "",
|
|
2419
|
+
registry = NPM_REGISTRY,
|
|
2420
|
+
offline = false,
|
|
2421
|
+
agentEntryMode = "contract",
|
|
2422
|
+
} = {}) {
|
|
2423
|
+
const resolvedCwd = path.resolve(cwd);
|
|
2424
|
+
const status = collectPaperStatus({ cwd: resolvedCwd });
|
|
2425
|
+
const provisioning = validatePaperProvisioningAuthority(resolvedCwd);
|
|
2426
|
+
const configResult = paperConfig(resolvedCwd);
|
|
2427
|
+
let validation;
|
|
2428
|
+
let validationError = "";
|
|
2429
|
+
try {
|
|
2430
|
+
validation = validateBuildchainConfig(resolvedCwd, { requireLifecycleStages: ["verify"] });
|
|
2431
|
+
} catch (error) {
|
|
2432
|
+
validationError = error.message;
|
|
2433
|
+
}
|
|
2434
|
+
const runtime = runtimeFacts({
|
|
2435
|
+
buildchainRoot, buildchainVersion,
|
|
2436
|
+
buildchainRef: provisioning.value?.runtime?.ref || buildchainRef, buildchainSha,
|
|
2437
|
+
});
|
|
2438
|
+
const agentEntry = collectPaperAgentEntry({ cwd: resolvedCwd, buildchainSha: runtime.resolvedSha, mode: agentEntryMode });
|
|
2439
|
+
const lockPath = path.resolve(resolvedCwd, PAPER_PATHS.contractLock);
|
|
2440
|
+
let lockEvaluation = {
|
|
2441
|
+
status: "missing-lock",
|
|
2442
|
+
compatible: false,
|
|
2443
|
+
drift: false,
|
|
2444
|
+
reasons: ["Buildchain contract lock is missing"],
|
|
2445
|
+
};
|
|
2446
|
+
try {
|
|
2447
|
+
const lock = readBuildchainContractLock(lockPath);
|
|
2448
|
+
if (lock) {
|
|
2449
|
+
const current = runtimeContractWorld(buildchainRoot);
|
|
2450
|
+
lockEvaluation = evaluateBuildchainContractLock({
|
|
2451
|
+
lock,
|
|
2452
|
+
current,
|
|
2453
|
+
runtimeRef: runtime.ref,
|
|
2454
|
+
runtimeSha: runtime.resolvedSha,
|
|
2455
|
+
runtimeClass: /alpha/i.test(runtime.ref) ? "alpha" : "stable",
|
|
2456
|
+
});
|
|
2457
|
+
}
|
|
2458
|
+
} catch (error) {
|
|
2459
|
+
lockEvaluation = {
|
|
2460
|
+
status: "invalid-lock",
|
|
2461
|
+
compatible: false,
|
|
2462
|
+
drift: false,
|
|
2463
|
+
reasons: [error.message],
|
|
2464
|
+
};
|
|
2465
|
+
}
|
|
2466
|
+
const source = {
|
|
2467
|
+
repositoryRoot: gitValue(resolvedCwd, ["rev-parse", "--show-toplevel"]),
|
|
2468
|
+
head: gitValue(resolvedCwd, ["rev-parse", "HEAD"]),
|
|
2469
|
+
tree: gitValue(resolvedCwd, ["rev-parse", "HEAD^{tree}"]),
|
|
2470
|
+
branch: gitValue(resolvedCwd, ["branch", "--show-current"]),
|
|
2471
|
+
clean: gitResult(resolvedCwd, ["status", "--porcelain"]).stdout === "",
|
|
2472
|
+
};
|
|
2473
|
+
const packageName = status.identity.package;
|
|
2474
|
+
const repository = status.identity.repository;
|
|
2475
|
+
const expectedPublisher = expectedPaperTrustedPublisher(provisioning.value, {
|
|
2476
|
+
repository, workflow: path.posix.basename(PAPER_PATHS.releaseWorkflow),
|
|
2477
|
+
});
|
|
2478
|
+
const npm =
|
|
2479
|
+
offline || !packageName
|
|
2480
|
+
? {
|
|
2481
|
+
package: {
|
|
2482
|
+
status: "unknown",
|
|
2483
|
+
exists: null,
|
|
2484
|
+
version: "",
|
|
2485
|
+
registry,
|
|
2486
|
+
errorCode: offline ? "offline" : "package-unresolved",
|
|
2487
|
+
},
|
|
2488
|
+
auth: {
|
|
2489
|
+
status: "unknown",
|
|
2490
|
+
authenticated: null,
|
|
2491
|
+
identity: "",
|
|
2492
|
+
errorCode: offline ? "offline" : "package-unresolved",
|
|
2493
|
+
},
|
|
2494
|
+
trust: {
|
|
2495
|
+
status: "unknown",
|
|
2496
|
+
configured: null,
|
|
2497
|
+
publishers: [],
|
|
2498
|
+
errorCode: offline ? "offline" : "package-unresolved",
|
|
2499
|
+
},
|
|
2500
|
+
}
|
|
2501
|
+
: {
|
|
2502
|
+
package: liveNpmPackageObservation(packageName, registry, resolvedCwd),
|
|
2503
|
+
auth: liveNpmAuthObservation(registry, resolvedCwd),
|
|
2504
|
+
trust: liveNpmTrustObservation(packageName, registry, resolvedCwd, expectedPublisher),
|
|
2505
|
+
};
|
|
2506
|
+
const repositoryPermissions = offline
|
|
2507
|
+
? {
|
|
2508
|
+
status: "unknown",
|
|
2509
|
+
repository,
|
|
2510
|
+
canWrite: null,
|
|
2511
|
+
errorCode: "offline",
|
|
2512
|
+
}
|
|
2513
|
+
: liveRepositoryPermissionObservation(repository, resolvedCwd);
|
|
2514
|
+
const repositoryActions = offline
|
|
2515
|
+
? {
|
|
2516
|
+
status: "unknown",
|
|
2517
|
+
defaultWorkflowPermissions: "",
|
|
2518
|
+
canApprovePullRequestReviews: null,
|
|
2519
|
+
errorCode: "offline",
|
|
2520
|
+
}
|
|
2521
|
+
: liveRepositoryActionsPolicyObservation(repository, resolvedCwd);
|
|
2522
|
+
const generatedWriteAuthority = offline
|
|
2523
|
+
? {
|
|
2524
|
+
status: "unknown",
|
|
2525
|
+
configured: null,
|
|
2526
|
+
mode: "",
|
|
2527
|
+
errorCode: "offline",
|
|
2528
|
+
}
|
|
2529
|
+
: liveGeneratedWriteAuthorityObservation(repository, resolvedCwd);
|
|
2530
|
+
const toolchain = localToolchainObservation(resolvedCwd, configResult.loaded?.config?.publication);
|
|
2531
|
+
const checks = [
|
|
2532
|
+
...paperPreflightLocalChecks({ agentEntry, provisioning, validationError, source, toolchain, runtime, lockEvaluation }),
|
|
2533
|
+
...paperPreflightRepositoryChecks({ repositoryPermissions, repositoryActions, generatedWriteAuthority }),
|
|
2534
|
+
...paperPreflightPublicationChecks({ npm, status }),
|
|
2535
|
+
];
|
|
2536
|
+
const blockingChecks = checks.filter(
|
|
2537
|
+
(entry) => entry.blocking && ["fail", "unknown"].includes(entry.status),
|
|
2538
|
+
);
|
|
2539
|
+
const externalMutationChecks = checks.filter(
|
|
2540
|
+
(entry) =>
|
|
2541
|
+
entry.scope === "external-mutation" &&
|
|
2542
|
+
["fail", "pending", "unknown"].includes(entry.status),
|
|
2543
|
+
);
|
|
2544
|
+
const nextActions = paperPreflightNextActions({
|
|
2545
|
+
validationError, source, lockEvaluation, npm, repositoryActions, generatedWriteAuthority, status,
|
|
2546
|
+
});
|
|
2428
2547
|
return {
|
|
2429
2548
|
schemaVersion: 1,
|
|
2430
2549
|
contract: PAPER_PREFLIGHT_CONTRACT,
|
|
@@ -2718,467 +2837,30 @@ function bootstrapPackageShape({ packageName, version }) {
|
|
|
2718
2837
|
};
|
|
2719
2838
|
}
|
|
2720
2839
|
|
|
2721
|
-
export function executePaperNpmBootstrap({
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
const name = normalizePackageName(
|
|
2748
|
-
packageName ||
|
|
2749
|
-
configResult.loaded.config.publish?.package ||
|
|
2750
|
-
configResult.loaded.config.publish?.mainPackage,
|
|
2751
|
-
);
|
|
2752
|
-
const repo = normalizeRepository(
|
|
2753
|
-
repository || resolvePaperRepository(resolvedCwd),
|
|
2754
|
-
);
|
|
2755
|
-
const provisioning = validatePaperProvisioningAuthority(resolvedCwd);
|
|
2756
|
-
if (!provisioning.valid) {
|
|
2757
|
-
throw new Error(
|
|
2758
|
-
`paper npm bootstrap requires a valid provisioning authority: ${provisioning.errors.join("; ")}`,
|
|
2759
|
-
);
|
|
2760
|
-
}
|
|
2761
|
-
const expectedPublisher = expectedPaperTrustedPublisher(provisioning.value);
|
|
2762
|
-
if (
|
|
2763
|
-
provisioning.value.package?.name !== name ||
|
|
2764
|
-
expectedPublisher.repository !== repo ||
|
|
2765
|
-
expectedPublisher.workflow !== toPosix(workflow).replace(/^\/+/, "") ||
|
|
2766
|
-
expectedPublisher.environment !== String(environment || "")
|
|
2767
|
-
) {
|
|
2768
|
-
throw new Error(
|
|
2769
|
-
"paper npm bootstrap coordinates differ from the exact provisioning authority",
|
|
2770
|
-
);
|
|
2771
|
-
}
|
|
2772
|
-
if (execute && confirmedPackage !== name) {
|
|
2773
|
-
throw new Error(
|
|
2774
|
-
`real npm bootstrap requires --confirm-public-package ${name}`,
|
|
2775
|
-
);
|
|
2776
|
-
}
|
|
2777
|
-
if (execute && offline) {
|
|
2778
|
-
throw new Error("real npm bootstrap cannot run with --offline");
|
|
2779
|
-
}
|
|
2780
|
-
if (execute && (!repo || !workflow)) {
|
|
2781
|
-
throw new Error(
|
|
2782
|
-
"real npm bootstrap requires GitHub repository and workflow coordinates",
|
|
2783
|
-
);
|
|
2784
|
-
}
|
|
2785
|
-
const packageObservation = offline
|
|
2786
|
-
? {
|
|
2787
|
-
status: "unknown",
|
|
2788
|
-
exists: null,
|
|
2789
|
-
version: "",
|
|
2790
|
-
registry,
|
|
2791
|
-
errorCode: "offline",
|
|
2792
|
-
}
|
|
2793
|
-
: liveNpmPackageObservation(name, registry, resolvedCwd);
|
|
2794
|
-
const auth = offline
|
|
2795
|
-
? {
|
|
2796
|
-
status: "unknown",
|
|
2797
|
-
authenticated: null,
|
|
2798
|
-
identity: "",
|
|
2799
|
-
errorCode: "offline",
|
|
2800
|
-
}
|
|
2801
|
-
: liveNpmAuthObservation(registry, resolvedCwd);
|
|
2802
|
-
const plan = {
|
|
2803
|
-
schemaVersion: 1,
|
|
2804
|
-
contract: PAPER_NPM_BOOTSTRAP_CONTRACT,
|
|
2805
|
-
ok: true,
|
|
2806
|
-
dryRun: !execute,
|
|
2807
|
-
externalMutation: execute,
|
|
2808
|
-
package: {
|
|
2809
|
-
name,
|
|
2810
|
-
bootstrapVersion,
|
|
2811
|
-
registry,
|
|
2812
|
-
existsBefore: packageObservation.exists,
|
|
2813
|
-
observedVersion: packageObservation.version,
|
|
2814
|
-
},
|
|
2815
|
-
repository: {
|
|
2816
|
-
github: repo,
|
|
2817
|
-
workflow,
|
|
2818
|
-
environment,
|
|
2819
|
-
},
|
|
2820
|
-
authority: {
|
|
2821
|
-
digest: provisioning.value.authorityDigest,
|
|
2822
|
-
policyDigest: provisioning.value.policy.policyDigest,
|
|
2823
|
-
},
|
|
2824
|
-
auth: {
|
|
2825
|
-
authenticated: auth.authenticated,
|
|
2826
|
-
identity: auth.identity,
|
|
2827
|
-
errorCode: auth.errorCode,
|
|
2828
|
-
},
|
|
2829
|
-
publish: {
|
|
2830
|
-
status: packageObservation.exists === true ? "existing" : "planned",
|
|
2831
|
-
distTag: "bootstrap",
|
|
2832
|
-
access: "public",
|
|
2833
|
-
},
|
|
2834
|
-
trust: {
|
|
2835
|
-
status: "planned",
|
|
2836
|
-
urls: [],
|
|
2837
|
-
},
|
|
2838
|
-
receipts: [],
|
|
2839
|
-
nextActions: execute
|
|
2840
|
-
? []
|
|
2841
|
-
: [
|
|
2842
|
-
{
|
|
2843
|
-
id: "confirm-public-bootstrap",
|
|
2844
|
-
command: `buildchain paper bootstrap npm --execute --confirm-public-package ${name} --json`,
|
|
2845
|
-
description:
|
|
2846
|
-
"After reviewing this dry-run, bootstrap the exact public package and configure GitHub Trusted Publishing.",
|
|
2847
|
-
},
|
|
2848
|
-
],
|
|
2849
|
-
};
|
|
2850
|
-
if (!execute) {
|
|
2851
|
-
const dryRunRoot = fs.mkdtempSync(
|
|
2852
|
-
path.join(os.tmpdir(), "buildchain-paper-bootstrap-dry-run-"),
|
|
2853
|
-
);
|
|
2854
|
-
try {
|
|
2855
|
-
fs.writeFileSync(
|
|
2856
|
-
path.join(dryRunRoot, "package.json"),
|
|
2857
|
-
jsonText(
|
|
2858
|
-
bootstrapPackageShape({
|
|
2859
|
-
packageName: name,
|
|
2860
|
-
version: bootstrapVersion,
|
|
2861
|
-
}),
|
|
2862
|
-
),
|
|
2863
|
-
);
|
|
2864
|
-
const configArgs = userconfig ? ["--userconfig", userconfig] : [];
|
|
2865
|
-
const pack = commandResult(
|
|
2866
|
-
"npm",
|
|
2867
|
-
[
|
|
2868
|
-
"pack",
|
|
2869
|
-
"--dry-run",
|
|
2870
|
-
"--json",
|
|
2871
|
-
"--ignore-scripts",
|
|
2872
|
-
`--registry=${registry}`,
|
|
2873
|
-
...configArgs,
|
|
2874
|
-
],
|
|
2875
|
-
{ cwd: dryRunRoot },
|
|
2876
|
-
);
|
|
2877
|
-
const publish = commandResult(
|
|
2878
|
-
"npm",
|
|
2879
|
-
[
|
|
2880
|
-
"publish",
|
|
2881
|
-
"--dry-run",
|
|
2882
|
-
"--ignore-scripts",
|
|
2883
|
-
"--access",
|
|
2884
|
-
"public",
|
|
2885
|
-
"--tag",
|
|
2886
|
-
"bootstrap",
|
|
2887
|
-
`--registry=${registry}`,
|
|
2888
|
-
...configArgs,
|
|
2889
|
-
],
|
|
2890
|
-
{ cwd: dryRunRoot },
|
|
2891
|
-
);
|
|
2892
|
-
return {
|
|
2893
|
-
...plan,
|
|
2894
|
-
ok: pack.ok && publish.ok,
|
|
2895
|
-
errorCode: !pack.ok
|
|
2896
|
-
? "npm-pack-dry-run-failed"
|
|
2897
|
-
: !publish.ok
|
|
2898
|
-
? "npm-publish-dry-run-failed"
|
|
2899
|
-
: "",
|
|
2900
|
-
dryRunChecks: {
|
|
2901
|
-
minimalPackageOnly: true,
|
|
2902
|
-
pack: {
|
|
2903
|
-
status: pack.ok ? "pass" : "fail",
|
|
2904
|
-
entryCount: (() => {
|
|
2905
|
-
const parsed = safeParseJson(pack.stdout);
|
|
2906
|
-
const value = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
2907
|
-
return Array.isArray(value?.files) ? value.files.length : 0;
|
|
2908
|
-
})(),
|
|
2909
|
-
},
|
|
2910
|
-
publish: {
|
|
2911
|
-
status: publish.ok ? "pass" : "fail",
|
|
2912
|
-
registry,
|
|
2913
|
-
access: "public",
|
|
2914
|
-
distTag: "bootstrap",
|
|
2915
|
-
},
|
|
2916
|
-
},
|
|
2917
|
-
};
|
|
2918
|
-
} finally {
|
|
2919
|
-
fs.rmSync(dryRunRoot, { recursive: true, force: true });
|
|
2920
|
-
}
|
|
2921
|
-
}
|
|
2922
|
-
if (packageObservation.exists === null) {
|
|
2923
|
-
return {
|
|
2924
|
-
...plan,
|
|
2925
|
-
ok: false,
|
|
2926
|
-
errorCode: "npm-package-status-unknown",
|
|
2927
|
-
publish: {
|
|
2928
|
-
...plan.publish,
|
|
2929
|
-
status: "blocked",
|
|
2930
|
-
},
|
|
2931
|
-
trust: {
|
|
2932
|
-
status: "blocked",
|
|
2933
|
-
urls: [],
|
|
2934
|
-
},
|
|
2935
|
-
nextActions: [
|
|
2936
|
-
{
|
|
2937
|
-
id: "verify-npm-package",
|
|
2938
|
-
command: `npm view ${name} version --json --registry=${registry}`,
|
|
2939
|
-
description:
|
|
2940
|
-
"Resolve whether the exact public package exists before any publish mutation.",
|
|
2941
|
-
},
|
|
2942
|
-
],
|
|
2943
|
-
};
|
|
2944
|
-
}
|
|
2945
|
-
if (auth.authenticated !== true) {
|
|
2946
|
-
return {
|
|
2947
|
-
...plan,
|
|
2948
|
-
ok: false,
|
|
2949
|
-
errorCode: "npm-auth-required",
|
|
2950
|
-
publish: {
|
|
2951
|
-
...plan.publish,
|
|
2952
|
-
status: "blocked",
|
|
2953
|
-
},
|
|
2954
|
-
trust: {
|
|
2955
|
-
status: "blocked",
|
|
2956
|
-
urls: [],
|
|
2957
|
-
},
|
|
2958
|
-
nextActions: [
|
|
2959
|
-
{
|
|
2960
|
-
id: "authenticate-npm",
|
|
2961
|
-
command: `npm whoami --registry=${registry}`,
|
|
2962
|
-
description:
|
|
2963
|
-
"Authenticate npm without sharing token contents, then rerun the exact bootstrap command.",
|
|
2964
|
-
},
|
|
2965
|
-
],
|
|
2966
|
-
};
|
|
2967
|
-
}
|
|
2968
|
-
const tempRoot = fs.mkdtempSync(
|
|
2969
|
-
path.join(os.tmpdir(), "buildchain-paper-bootstrap-"),
|
|
2970
|
-
);
|
|
2971
|
-
try {
|
|
2972
|
-
fs.writeFileSync(
|
|
2973
|
-
path.join(tempRoot, "package.json"),
|
|
2974
|
-
jsonText(
|
|
2975
|
-
bootstrapPackageShape({ packageName: name, version: bootstrapVersion }),
|
|
2976
|
-
),
|
|
2977
|
-
);
|
|
2978
|
-
const configArgs = userconfig ? ["--userconfig", userconfig] : [];
|
|
2979
|
-
const pack = commandResult(
|
|
2980
|
-
"npm",
|
|
2981
|
-
[
|
|
2982
|
-
"pack",
|
|
2983
|
-
"--dry-run",
|
|
2984
|
-
"--json",
|
|
2985
|
-
"--ignore-scripts",
|
|
2986
|
-
`--registry=${registry}`,
|
|
2987
|
-
...configArgs,
|
|
2988
|
-
],
|
|
2989
|
-
{ cwd: tempRoot },
|
|
2990
|
-
);
|
|
2991
|
-
if (!pack.ok) {
|
|
2992
|
-
return {
|
|
2993
|
-
...plan,
|
|
2994
|
-
ok: false,
|
|
2995
|
-
errorCode: "npm-pack-dry-run-failed",
|
|
2996
|
-
publish: { ...plan.publish, status: "blocked" },
|
|
2997
|
-
trust: { status: "blocked", urls: [] },
|
|
2998
|
-
};
|
|
2999
|
-
}
|
|
3000
|
-
const publishDryRun = commandResult(
|
|
3001
|
-
"npm",
|
|
3002
|
-
[
|
|
3003
|
-
"publish",
|
|
3004
|
-
"--dry-run",
|
|
3005
|
-
"--ignore-scripts",
|
|
3006
|
-
"--access",
|
|
3007
|
-
"public",
|
|
3008
|
-
"--tag",
|
|
3009
|
-
"bootstrap",
|
|
3010
|
-
`--registry=${registry}`,
|
|
3011
|
-
...configArgs,
|
|
3012
|
-
],
|
|
3013
|
-
{ cwd: tempRoot },
|
|
3014
|
-
);
|
|
3015
|
-
if (!publishDryRun.ok) {
|
|
3016
|
-
return {
|
|
3017
|
-
...plan,
|
|
3018
|
-
ok: false,
|
|
3019
|
-
errorCode: "npm-publish-dry-run-failed",
|
|
3020
|
-
publish: { ...plan.publish, status: "blocked" },
|
|
3021
|
-
trust: { status: "blocked", urls: [] },
|
|
3022
|
-
};
|
|
3023
|
-
}
|
|
3024
|
-
let publishStatus =
|
|
3025
|
-
packageObservation.exists === true ? "existing" : "published";
|
|
3026
|
-
if (packageObservation.exists !== true) {
|
|
3027
|
-
const published = commandResult(
|
|
3028
|
-
"npm",
|
|
3029
|
-
[
|
|
3030
|
-
"publish",
|
|
3031
|
-
"--ignore-scripts",
|
|
3032
|
-
"--access",
|
|
3033
|
-
"public",
|
|
3034
|
-
"--tag",
|
|
3035
|
-
"bootstrap",
|
|
3036
|
-
`--registry=${registry}`,
|
|
3037
|
-
...configArgs,
|
|
3038
|
-
],
|
|
3039
|
-
{ cwd: tempRoot, timeout: 60000 },
|
|
3040
|
-
);
|
|
3041
|
-
if (!published.ok) {
|
|
3042
|
-
const failure = {
|
|
3043
|
-
...plan,
|
|
3044
|
-
ok: false,
|
|
3045
|
-
errorCode: "npm-publish-failed",
|
|
3046
|
-
publish: { ...plan.publish, status: "failed" },
|
|
3047
|
-
trust: { status: "blocked", urls: [] },
|
|
3048
|
-
};
|
|
3049
|
-
failure.receipts = [
|
|
3050
|
-
writePaperReceipt(resolvedCwd, PAPER_PATHS.npmBootstrap, failure),
|
|
3051
|
-
];
|
|
3052
|
-
return failure;
|
|
3053
|
-
}
|
|
3054
|
-
}
|
|
3055
|
-
const trustArgs = [
|
|
3056
|
-
"trust",
|
|
3057
|
-
"github",
|
|
3058
|
-
name,
|
|
3059
|
-
"--repo",
|
|
3060
|
-
repo,
|
|
3061
|
-
"--file",
|
|
3062
|
-
workflow,
|
|
3063
|
-
...(environment ? ["--env", environment] : []),
|
|
3064
|
-
"--allow-publish",
|
|
3065
|
-
"--yes",
|
|
3066
|
-
"--json",
|
|
3067
|
-
`--registry=${registry}`,
|
|
3068
|
-
...configArgs,
|
|
3069
|
-
];
|
|
3070
|
-
const trustCommand = commandResult("npm", trustArgs, {
|
|
3071
|
-
cwd: resolvedCwd,
|
|
3072
|
-
timeout: 60000,
|
|
3073
|
-
});
|
|
3074
|
-
const parsedTrust = safeParseJson(trustCommand.stdout);
|
|
3075
|
-
const urls = [
|
|
3076
|
-
...extractUrls(
|
|
3077
|
-
parsedTrust || `${trustCommand.stdout}\n${trustCommand.stderr}`,
|
|
3078
|
-
),
|
|
3079
|
-
];
|
|
3080
|
-
const trustList =
|
|
3081
|
-
trustCommand.ok && urls.length === 0
|
|
3082
|
-
? liveNpmTrustObservation(
|
|
3083
|
-
name,
|
|
3084
|
-
registry,
|
|
3085
|
-
resolvedCwd,
|
|
3086
|
-
expectedPublisher,
|
|
3087
|
-
)
|
|
3088
|
-
: {
|
|
3089
|
-
status: "unknown",
|
|
3090
|
-
configured: null,
|
|
3091
|
-
publishers: [],
|
|
3092
|
-
errorCode:
|
|
3093
|
-
urls.length > 0 ? "web-action-required" : "npm-trust-failed",
|
|
3094
|
-
};
|
|
3095
|
-
const trustStatus =
|
|
3096
|
-
trustCommand.ok && trustList.configured === true
|
|
3097
|
-
? "configured"
|
|
3098
|
-
: urls.length > 0
|
|
3099
|
-
? "action-required"
|
|
3100
|
-
: "failed";
|
|
3101
|
-
const packageAfter = liveNpmPackageObservation(name, registry, resolvedCwd);
|
|
3102
|
-
const packageVerified = packageAfter.exists === true;
|
|
3103
|
-
const receipt = {
|
|
3104
|
-
...plan,
|
|
3105
|
-
ok: trustStatus !== "failed" && packageVerified,
|
|
3106
|
-
dryRun: false,
|
|
3107
|
-
externalMutation: true,
|
|
3108
|
-
errorCode:
|
|
3109
|
-
trustStatus === "failed"
|
|
3110
|
-
? "npm-trust-failed"
|
|
3111
|
-
: packageVerified
|
|
3112
|
-
? ""
|
|
3113
|
-
: "npm-package-readback-failed",
|
|
3114
|
-
package: {
|
|
3115
|
-
...plan.package,
|
|
3116
|
-
existsAfter: packageAfter.exists,
|
|
3117
|
-
observedVersionAfter: packageAfter.version,
|
|
3118
|
-
readbackStatus: packageAfter.status,
|
|
3119
|
-
},
|
|
3120
|
-
publish: {
|
|
3121
|
-
...plan.publish,
|
|
3122
|
-
status: publishStatus,
|
|
3123
|
-
},
|
|
3124
|
-
trust: {
|
|
3125
|
-
status: trustStatus,
|
|
3126
|
-
urls,
|
|
3127
|
-
expectedPublisher,
|
|
3128
|
-
exactBinding: trustList.exactBinding === true,
|
|
3129
|
-
publishers: trustList.publishers,
|
|
3130
|
-
},
|
|
3131
|
-
nextActions:
|
|
3132
|
-
trustStatus === "configured" && packageVerified
|
|
3133
|
-
? [
|
|
3134
|
-
{
|
|
3135
|
-
id: "paper-preflight",
|
|
3136
|
-
command: "buildchain paper preflight --json",
|
|
3137
|
-
description:
|
|
3138
|
-
"Verify package existence and Trusted Publisher binding from live read-only sources.",
|
|
3139
|
-
},
|
|
3140
|
-
]
|
|
3141
|
-
: trustStatus === "action-required" && packageVerified
|
|
3142
|
-
? [
|
|
3143
|
-
{
|
|
3144
|
-
id: "complete-npm-web-action",
|
|
3145
|
-
command: "",
|
|
3146
|
-
description:
|
|
3147
|
-
"Open the exact npm URL returned below, complete the web step, then rerun preflight.",
|
|
3148
|
-
urls,
|
|
3149
|
-
},
|
|
3150
|
-
]
|
|
3151
|
-
: !packageVerified
|
|
3152
|
-
? [
|
|
3153
|
-
{
|
|
3154
|
-
id: "verify-npm-package-readback",
|
|
3155
|
-
command: `npm view ${name} version --json --registry=${registry}`,
|
|
3156
|
-
description:
|
|
3157
|
-
"The mutation returned but the official registry did not prove package existence; do not infer success.",
|
|
3158
|
-
},
|
|
3159
|
-
]
|
|
3160
|
-
: [
|
|
3161
|
-
{
|
|
3162
|
-
id: "retry-npm-trust",
|
|
3163
|
-
command: `buildchain paper bootstrap npm --execute --confirm-public-package ${name} --json`,
|
|
3164
|
-
description:
|
|
3165
|
-
"The package exists; retry only the idempotent Trusted Publisher configuration path.",
|
|
3166
|
-
},
|
|
3167
|
-
],
|
|
3168
|
-
};
|
|
3169
|
-
receipt.receipts = [
|
|
3170
|
-
writePaperReceipt(resolvedCwd, PAPER_PATHS.npmBootstrap, receipt),
|
|
3171
|
-
];
|
|
3172
|
-
if (
|
|
3173
|
-
packageVerified &&
|
|
3174
|
-
(trustStatus === "configured" || trustStatus === "action-required")
|
|
3175
|
-
) {
|
|
3176
|
-
receipt.receipts.push(
|
|
3177
|
-
writePaperReceipt(resolvedCwd, PAPER_PATHS.npmTrust, receipt),
|
|
3178
|
-
);
|
|
3179
|
-
}
|
|
3180
|
-
return receipt;
|
|
3181
|
-
} finally {
|
|
3182
|
-
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
3183
|
-
}
|
|
2840
|
+
export function executePaperNpmBootstrap(options = {}) {
|
|
2841
|
+
return executePaperNpmBootstrapOperation(options, {
|
|
2842
|
+
DEFAULT_BOOTSTRAP_VERSION,
|
|
2843
|
+
NPM_REGISTRY,
|
|
2844
|
+
PAPER_NPM_BOOTSTRAP_CONTRACT,
|
|
2845
|
+
PAPER_PATHS,
|
|
2846
|
+
bootstrapPackageShape,
|
|
2847
|
+
commandResult,
|
|
2848
|
+
expectedPaperTrustedPublisher,
|
|
2849
|
+
extractUrls,
|
|
2850
|
+
fs,
|
|
2851
|
+
jsonText,
|
|
2852
|
+
liveNpmAuthObservation,
|
|
2853
|
+
liveNpmPackageObservation,
|
|
2854
|
+
liveNpmTrustObservation,
|
|
2855
|
+
normalizePackageName,
|
|
2856
|
+
normalizeRepository,
|
|
2857
|
+
os,
|
|
2858
|
+
paperConfig,
|
|
2859
|
+
path,
|
|
2860
|
+
resolvePaperRepository,
|
|
2861
|
+
safeParseJson,
|
|
2862
|
+
toPosix,
|
|
2863
|
+
validatePaperProvisioningAuthority,
|
|
2864
|
+
writePaperReceipt,
|
|
2865
|
+
});
|
|
3184
2866
|
}
|