@kungfu-tech/buildchain 3.0.4-alpha.1 → 3.0.4-alpha.11
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 +35 -24
- 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 +346 -671
- 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/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
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
scaffoldPackageJson,
|
|
46
46
|
scaffoldReadme,
|
|
47
47
|
} from "./paper-scaffold-content.js";
|
|
48
|
+
import { executePaperNpmBootstrap as executePaperNpmBootstrapOperation } from "./paper-npm-bootstrap.js";
|
|
48
49
|
|
|
49
50
|
export { PAPER_PATHS, resolvePaperRepository } from "./paper-repository.js";
|
|
50
51
|
export {
|
|
@@ -73,6 +74,7 @@ export {
|
|
|
73
74
|
collectPaperFleetAudit,
|
|
74
75
|
discoverPaperFleet,
|
|
75
76
|
planPaperFleetUpdate,
|
|
77
|
+
paperFleetTransitionWorkspace,
|
|
76
78
|
writePaperFleetUpdate,
|
|
77
79
|
} from "./paper-fleet.js";
|
|
78
80
|
|
|
@@ -112,6 +114,7 @@ const PAPER_SCAFFOLD_PATHS = Object.freeze([
|
|
|
112
114
|
PAPER_PATHS.buildWorkflow,
|
|
113
115
|
PAPER_PATHS.verifyWorkflow,
|
|
114
116
|
PAPER_PATHS.releaseWorkflow,
|
|
117
|
+
PAPER_PATHS.pnpmWorkspace,
|
|
115
118
|
PAPER_PATHS.provisioningAuthority,
|
|
116
119
|
"Makefile",
|
|
117
120
|
"package.json",
|
|
@@ -133,6 +136,7 @@ const DEFAULT_TOOLCHAIN_COMMAND = "latexmk -pdf -outdir=_build paper/main.tex";
|
|
|
133
136
|
const SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/i;
|
|
134
137
|
const GIT_SHA_PATTERN = /^[0-9a-f]{40}$/i;
|
|
135
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";
|
|
136
140
|
|
|
137
141
|
function toPosix(value) {
|
|
138
142
|
return String(value || "")
|
|
@@ -148,6 +152,66 @@ function jsonText(value) {
|
|
|
148
152
|
return `${JSON.stringify(value, null, 2)}\n`;
|
|
149
153
|
}
|
|
150
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
|
+
|
|
151
215
|
function existingFileFact(cwd, relativePath) {
|
|
152
216
|
const filePath = path.resolve(cwd, relativePath);
|
|
153
217
|
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
|
|
@@ -420,6 +484,7 @@ ${passportInput} secrets:
|
|
|
420
484
|
BUILDCHAIN_GENERATED_WRITE_APP_CLIENT_ID: \${{ secrets.BUILDCHAIN_GENERATED_WRITE_APP_CLIENT_ID }}
|
|
421
485
|
BUILDCHAIN_GENERATED_WRITE_APP_PRIVATE_KEY: \${{ secrets.BUILDCHAIN_GENERATED_WRITE_APP_PRIVATE_KEY }}
|
|
422
486
|
BUILDCHAIN_GENERATED_WRITE_TOKEN: \${{ secrets.BUILDCHAIN_GENERATED_WRITE_TOKEN }}
|
|
487
|
+
BUILDCHAIN_PROMOTION_TOKEN: \${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}
|
|
423
488
|
`;
|
|
424
489
|
}
|
|
425
490
|
|
|
@@ -602,6 +667,7 @@ function scaffoldFiles({
|
|
|
602
667
|
[PAPER_PATHS.buildWorkflow, buildWorkflow],
|
|
603
668
|
[PAPER_PATHS.verifyWorkflow, verifyWorkflow],
|
|
604
669
|
[PAPER_PATHS.releaseWorkflow, releaseWorkflow],
|
|
670
|
+
[PAPER_PATHS.pnpmWorkspace, paperPnpmWorkspace("", buildchainVersion)],
|
|
605
671
|
[PAPER_PATHS.provisioningAuthority, jsonText(provisioningAuthority)],
|
|
606
672
|
...agentEntry,
|
|
607
673
|
[
|
|
@@ -923,12 +989,20 @@ function migrationFiles({
|
|
|
923
989
|
currentPackage.value,
|
|
924
990
|
runtimeIdentity.version,
|
|
925
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
|
+
);
|
|
926
999
|
const files = new Map([
|
|
927
1000
|
[PAPER_PATHS.contractLock, contractLockText],
|
|
928
1001
|
[PAPER_PATHS.versionPin, `${runtimeIdentity.version}\n`],
|
|
929
1002
|
[PAPER_PATHS.buildWorkflow, buildWorkflow],
|
|
930
1003
|
[PAPER_PATHS.verifyWorkflow, verifyWorkflow],
|
|
931
1004
|
[PAPER_PATHS.releaseWorkflow, releaseWorkflow],
|
|
1005
|
+
[PAPER_PATHS.pnpmWorkspace, pnpmWorkspace],
|
|
932
1006
|
[PAPER_PATHS.provisioningAuthority, jsonText(provisioningAuthority)],
|
|
933
1007
|
...agentEntry,
|
|
934
1008
|
["package.json", jsonText(packageJson)],
|
|
@@ -1230,8 +1304,7 @@ function releaseTransactionFacts(candidates, version) {
|
|
|
1230
1304
|
};
|
|
1231
1305
|
}
|
|
1232
1306
|
|
|
1233
|
-
|
|
1234
|
-
const resolvedCwd = path.resolve(cwd);
|
|
1307
|
+
function collectPaperGovernanceFacts(resolvedCwd) {
|
|
1235
1308
|
const configResult = paperConfig(resolvedCwd);
|
|
1236
1309
|
const loaded = configResult.loaded;
|
|
1237
1310
|
const publication = loaded?.config?.publication;
|
|
@@ -1272,7 +1345,13 @@ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
|
|
|
1272
1345
|
gitRepo.ok &&
|
|
1273
1346
|
gitRepo.stdout === "true",
|
|
1274
1347
|
);
|
|
1348
|
+
return {
|
|
1349
|
+
configResult, loaded, publication, packageName, version, configFact,
|
|
1350
|
+
scaffoldFacts, contractLockError, scaffoldOk, governedOk,
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1275
1353
|
|
|
1354
|
+
function collectPaperEvidenceFacts(resolvedCwd, publication, version) {
|
|
1276
1355
|
const admissionInputs = [PAPER_PATHS.capability, PAPER_PATHS.admission].map(
|
|
1277
1356
|
(relativePath) => ({
|
|
1278
1357
|
relativePath,
|
|
@@ -1330,6 +1409,82 @@ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
|
|
|
1330
1409
|
visibility.value,
|
|
1331
1410
|
"production",
|
|
1332
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;
|
|
1333
1488
|
|
|
1334
1489
|
const states = [
|
|
1335
1490
|
state(
|
|
@@ -1439,54 +1594,7 @@ export function collectPaperStatus({ cwd = process.cwd() } = {}) {
|
|
|
1439
1594
|
const highestEvidenceState =
|
|
1440
1595
|
[...states].reverse().find((entry) => entry.satisfied)?.id || "none";
|
|
1441
1596
|
const stateMap = Object.fromEntries(states.map((entry) => [entry.id, entry]));
|
|
1442
|
-
const blockingNextActions =
|
|
1443
|
-
if (!stateMap.scaffolded.satisfied) {
|
|
1444
|
-
blockingNextActions.push({
|
|
1445
|
-
id: "scaffold-paper",
|
|
1446
|
-
command: "buildchain paper scaffold --help",
|
|
1447
|
-
description: "Plan a no-overwrite Buildchain paper scaffold.",
|
|
1448
|
-
});
|
|
1449
|
-
} else if (!stateMap.governed.satisfied) {
|
|
1450
|
-
blockingNextActions.push({
|
|
1451
|
-
id: "restore-governance",
|
|
1452
|
-
command: "buildchain paper preflight --json",
|
|
1453
|
-
description:
|
|
1454
|
-
"Repair the Git/contract-lock governance checks reported by preflight.",
|
|
1455
|
-
});
|
|
1456
|
-
} else if (!stateMap["content-ready"].satisfied) {
|
|
1457
|
-
blockingNextActions.push({
|
|
1458
|
-
id: "complete-paper-content",
|
|
1459
|
-
command: "make check",
|
|
1460
|
-
description: "Add every declared paper source and metadata input.",
|
|
1461
|
-
});
|
|
1462
|
-
} else if (!deterministic) {
|
|
1463
|
-
blockingNextActions.push({
|
|
1464
|
-
id: "build-reproducible-artifact",
|
|
1465
|
-
command: "buildchain paper build --execute --json",
|
|
1466
|
-
description:
|
|
1467
|
-
"Run the existing two-clean-build reproducibility gate and promote exact bytes.",
|
|
1468
|
-
});
|
|
1469
|
-
} else if (
|
|
1470
|
-
!stateMap.bootstrapped.satisfied ||
|
|
1471
|
-
!stateMap["trust-bound"].satisfied
|
|
1472
|
-
) {
|
|
1473
|
-
blockingNextActions.push({
|
|
1474
|
-
id: "bootstrap-npm",
|
|
1475
|
-
command: "buildchain paper bootstrap npm --json",
|
|
1476
|
-
description:
|
|
1477
|
-
"Inspect the dry-run npm bootstrap and Trusted Publishing handoff.",
|
|
1478
|
-
});
|
|
1479
|
-
} else if (!stateMap["alpha-complete"].satisfied) {
|
|
1480
|
-
blockingNextActions.push({
|
|
1481
|
-
id: transactionFacts.selected ? "resume-alpha" : "start-alpha",
|
|
1482
|
-
command: transactionFacts.selected
|
|
1483
|
-
? "buildchain paper resume --json"
|
|
1484
|
-
: "buildchain paper alpha --json",
|
|
1485
|
-
description: transactionFacts.selected
|
|
1486
|
-
? "Resume the exact sealed release transaction."
|
|
1487
|
-
: "Plan the protected dev-to-alpha publication PR.",
|
|
1488
|
-
});
|
|
1489
|
-
}
|
|
1597
|
+
const blockingNextActions = paperStatusBlockingNextActions({ stateMap, deterministic, transactionFacts });
|
|
1490
1598
|
return {
|
|
1491
1599
|
schemaVersion: 1,
|
|
1492
1600
|
contract: PAPER_STATUS_CONTRACT,
|
|
@@ -2050,146 +2158,8 @@ function localToolchainObservation(cwd, publication) {
|
|
|
2050
2158
|
};
|
|
2051
2159
|
}
|
|
2052
2160
|
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
buildchainRoot = process.cwd(),
|
|
2056
|
-
buildchainVersion = "",
|
|
2057
|
-
buildchainRef = "v3",
|
|
2058
|
-
buildchainSha = "",
|
|
2059
|
-
registry = NPM_REGISTRY,
|
|
2060
|
-
offline = false,
|
|
2061
|
-
agentEntryMode = "contract",
|
|
2062
|
-
} = {}) {
|
|
2063
|
-
const resolvedCwd = path.resolve(cwd);
|
|
2064
|
-
const status = collectPaperStatus({ cwd: resolvedCwd });
|
|
2065
|
-
const provisioning = validatePaperProvisioningAuthority(resolvedCwd);
|
|
2066
|
-
const configResult = paperConfig(resolvedCwd);
|
|
2067
|
-
let validation;
|
|
2068
|
-
let validationError = "";
|
|
2069
|
-
try {
|
|
2070
|
-
validation = validateBuildchainConfig(resolvedCwd, {
|
|
2071
|
-
requireLifecycleStages: ["verify"],
|
|
2072
|
-
});
|
|
2073
|
-
} catch (error) {
|
|
2074
|
-
validationError = error.message;
|
|
2075
|
-
}
|
|
2076
|
-
const runtime = runtimeFacts({
|
|
2077
|
-
buildchainRoot,
|
|
2078
|
-
buildchainVersion,
|
|
2079
|
-
buildchainRef: provisioning.value?.runtime?.ref || buildchainRef,
|
|
2080
|
-
buildchainSha,
|
|
2081
|
-
});
|
|
2082
|
-
const agentEntry = collectPaperAgentEntry({
|
|
2083
|
-
cwd: resolvedCwd,
|
|
2084
|
-
buildchainSha: runtime.resolvedSha,
|
|
2085
|
-
mode: agentEntryMode,
|
|
2086
|
-
});
|
|
2087
|
-
const lockPath = path.resolve(resolvedCwd, PAPER_PATHS.contractLock);
|
|
2088
|
-
let lockEvaluation = {
|
|
2089
|
-
status: "missing-lock",
|
|
2090
|
-
compatible: false,
|
|
2091
|
-
drift: false,
|
|
2092
|
-
reasons: ["Buildchain contract lock is missing"],
|
|
2093
|
-
};
|
|
2094
|
-
try {
|
|
2095
|
-
const lock = readBuildchainContractLock(lockPath);
|
|
2096
|
-
if (lock) {
|
|
2097
|
-
const current = runtimeContractWorld(buildchainRoot);
|
|
2098
|
-
lockEvaluation = evaluateBuildchainContractLock({
|
|
2099
|
-
lock,
|
|
2100
|
-
current,
|
|
2101
|
-
runtimeRef: runtime.ref,
|
|
2102
|
-
runtimeSha: runtime.resolvedSha,
|
|
2103
|
-
runtimeClass: /alpha/i.test(runtime.ref) ? "alpha" : "stable",
|
|
2104
|
-
});
|
|
2105
|
-
}
|
|
2106
|
-
} catch (error) {
|
|
2107
|
-
lockEvaluation = {
|
|
2108
|
-
status: "invalid-lock",
|
|
2109
|
-
compatible: false,
|
|
2110
|
-
drift: false,
|
|
2111
|
-
reasons: [error.message],
|
|
2112
|
-
};
|
|
2113
|
-
}
|
|
2114
|
-
const source = {
|
|
2115
|
-
repositoryRoot: gitValue(resolvedCwd, ["rev-parse", "--show-toplevel"]),
|
|
2116
|
-
head: gitValue(resolvedCwd, ["rev-parse", "HEAD"]),
|
|
2117
|
-
tree: gitValue(resolvedCwd, ["rev-parse", "HEAD^{tree}"]),
|
|
2118
|
-
branch: gitValue(resolvedCwd, ["branch", "--show-current"]),
|
|
2119
|
-
clean: gitResult(resolvedCwd, ["status", "--porcelain"]).stdout === "",
|
|
2120
|
-
};
|
|
2121
|
-
const packageName = status.identity.package;
|
|
2122
|
-
const repository = status.identity.repository;
|
|
2123
|
-
const expectedPublisher = expectedPaperTrustedPublisher(provisioning.value, {
|
|
2124
|
-
repository,
|
|
2125
|
-
workflow: path.posix.basename(PAPER_PATHS.releaseWorkflow),
|
|
2126
|
-
});
|
|
2127
|
-
const npm =
|
|
2128
|
-
offline || !packageName
|
|
2129
|
-
? {
|
|
2130
|
-
package: {
|
|
2131
|
-
status: "unknown",
|
|
2132
|
-
exists: null,
|
|
2133
|
-
version: "",
|
|
2134
|
-
registry,
|
|
2135
|
-
errorCode: offline ? "offline" : "package-unresolved",
|
|
2136
|
-
},
|
|
2137
|
-
auth: {
|
|
2138
|
-
status: "unknown",
|
|
2139
|
-
authenticated: null,
|
|
2140
|
-
identity: "",
|
|
2141
|
-
errorCode: offline ? "offline" : "package-unresolved",
|
|
2142
|
-
},
|
|
2143
|
-
trust: {
|
|
2144
|
-
status: "unknown",
|
|
2145
|
-
configured: null,
|
|
2146
|
-
publishers: [],
|
|
2147
|
-
errorCode: offline ? "offline" : "package-unresolved",
|
|
2148
|
-
},
|
|
2149
|
-
}
|
|
2150
|
-
: {
|
|
2151
|
-
package: liveNpmPackageObservation(
|
|
2152
|
-
packageName,
|
|
2153
|
-
registry,
|
|
2154
|
-
resolvedCwd,
|
|
2155
|
-
),
|
|
2156
|
-
auth: liveNpmAuthObservation(registry, resolvedCwd),
|
|
2157
|
-
trust: liveNpmTrustObservation(
|
|
2158
|
-
packageName,
|
|
2159
|
-
registry,
|
|
2160
|
-
resolvedCwd,
|
|
2161
|
-
expectedPublisher,
|
|
2162
|
-
),
|
|
2163
|
-
};
|
|
2164
|
-
const repositoryPermissions = offline
|
|
2165
|
-
? {
|
|
2166
|
-
status: "unknown",
|
|
2167
|
-
repository,
|
|
2168
|
-
canWrite: null,
|
|
2169
|
-
errorCode: "offline",
|
|
2170
|
-
}
|
|
2171
|
-
: liveRepositoryPermissionObservation(repository, resolvedCwd);
|
|
2172
|
-
const repositoryActions = offline
|
|
2173
|
-
? {
|
|
2174
|
-
status: "unknown",
|
|
2175
|
-
defaultWorkflowPermissions: "",
|
|
2176
|
-
canApprovePullRequestReviews: null,
|
|
2177
|
-
errorCode: "offline",
|
|
2178
|
-
}
|
|
2179
|
-
: liveRepositoryActionsPolicyObservation(repository, resolvedCwd);
|
|
2180
|
-
const generatedWriteAuthority = offline
|
|
2181
|
-
? {
|
|
2182
|
-
status: "unknown",
|
|
2183
|
-
configured: null,
|
|
2184
|
-
mode: "",
|
|
2185
|
-
errorCode: "offline",
|
|
2186
|
-
}
|
|
2187
|
-
: liveGeneratedWriteAuthorityObservation(repository, resolvedCwd);
|
|
2188
|
-
const toolchain = localToolchainObservation(
|
|
2189
|
-
resolvedCwd,
|
|
2190
|
-
configResult.loaded?.config?.publication,
|
|
2191
|
-
);
|
|
2192
|
-
const checks = [
|
|
2161
|
+
function paperPreflightLocalChecks({ agentEntry, provisioning, validationError, source, toolchain, runtime, lockEvaluation }) {
|
|
2162
|
+
return [
|
|
2193
2163
|
...agentEntry.checks.map((entry) => ({
|
|
2194
2164
|
...entry,
|
|
2195
2165
|
blocking: true,
|
|
@@ -2256,6 +2226,11 @@ export function collectPaperPreflight({
|
|
|
2256
2226
|
? `Buildchain runtime contract is ${lockEvaluation.status}.`
|
|
2257
2227
|
: `Buildchain runtime contract is not admitted: ${(lockEvaluation.reasons || []).join("; ")}`,
|
|
2258
2228
|
},
|
|
2229
|
+
];
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
function paperPreflightRepositoryChecks({ repositoryPermissions, repositoryActions, generatedWriteAuthority }) {
|
|
2233
|
+
return [
|
|
2259
2234
|
{
|
|
2260
2235
|
id: "repository.write-permission",
|
|
2261
2236
|
status:
|
|
@@ -2309,6 +2284,11 @@ export function collectPaperPreflight({
|
|
|
2309
2284
|
? "No GitHub App or compatible narrow generated-write credential is configured."
|
|
2310
2285
|
: `Generated-write authority is unknown (${generatedWriteAuthority.errorCode}).`,
|
|
2311
2286
|
},
|
|
2287
|
+
];
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
function paperPreflightPublicationChecks({ npm, status }) {
|
|
2291
|
+
return [
|
|
2312
2292
|
{
|
|
2313
2293
|
id: "npm.package",
|
|
2314
2294
|
status:
|
|
@@ -2365,24 +2345,19 @@ export function collectPaperPreflight({
|
|
|
2365
2345
|
: `${status.conflicts.length} release-state conflict(s) require repair.`,
|
|
2366
2346
|
},
|
|
2367
2347
|
];
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
const
|
|
2372
|
-
(entry) =>
|
|
2373
|
-
entry.scope === "external-mutation" &&
|
|
2374
|
-
["fail", "pending", "unknown"].includes(entry.status),
|
|
2375
|
-
);
|
|
2376
|
-
const nextActions = [];
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
function paperPreflightNextActions({ validationError, source, lockEvaluation, npm, repositoryActions, generatedWriteAuthority, status }) {
|
|
2351
|
+
const actions = [];
|
|
2377
2352
|
if (validationError) {
|
|
2378
|
-
|
|
2353
|
+
actions.push({
|
|
2379
2354
|
id: "repair-config",
|
|
2380
2355
|
command: "buildchain validate --require-lifecycle-stages verify",
|
|
2381
2356
|
description: validationError,
|
|
2382
2357
|
});
|
|
2383
2358
|
}
|
|
2384
2359
|
if (!source.clean) {
|
|
2385
|
-
|
|
2360
|
+
actions.push({
|
|
2386
2361
|
id: "commit-source",
|
|
2387
2362
|
command: "git status --short",
|
|
2388
2363
|
description:
|
|
@@ -2390,7 +2365,7 @@ export function collectPaperPreflight({
|
|
|
2390
2365
|
});
|
|
2391
2366
|
}
|
|
2392
2367
|
if (!lockEvaluation.compatible) {
|
|
2393
|
-
|
|
2368
|
+
actions.push({
|
|
2394
2369
|
id: "refresh-contract-lock",
|
|
2395
2370
|
command: "buildchain paper scaffold --json",
|
|
2396
2371
|
description:
|
|
@@ -2398,7 +2373,7 @@ export function collectPaperPreflight({
|
|
|
2398
2373
|
});
|
|
2399
2374
|
}
|
|
2400
2375
|
if (npm.package.exists === false || npm.trust.configured === false) {
|
|
2401
|
-
|
|
2376
|
+
actions.push({
|
|
2402
2377
|
id: "bootstrap-npm",
|
|
2403
2378
|
command: "buildchain paper bootstrap npm --json",
|
|
2404
2379
|
description:
|
|
@@ -2410,7 +2385,7 @@ export function collectPaperPreflight({
|
|
|
2410
2385
|
(repositoryActions.defaultWorkflowPermissions !== "read" ||
|
|
2411
2386
|
repositoryActions.canApprovePullRequestReviews !== false)
|
|
2412
2387
|
) {
|
|
2413
|
-
|
|
2388
|
+
actions.push({
|
|
2414
2389
|
id: "constrain-repository-actions",
|
|
2415
2390
|
command: "",
|
|
2416
2391
|
description:
|
|
@@ -2418,7 +2393,7 @@ export function collectPaperPreflight({
|
|
|
2418
2393
|
});
|
|
2419
2394
|
}
|
|
2420
2395
|
if (generatedWriteAuthority.configured === false) {
|
|
2421
|
-
|
|
2396
|
+
actions.push({
|
|
2422
2397
|
id: "configure-generated-write-authority",
|
|
2423
2398
|
command: "",
|
|
2424
2399
|
description:
|
|
@@ -2426,14 +2401,151 @@ export function collectPaperPreflight({
|
|
|
2426
2401
|
});
|
|
2427
2402
|
}
|
|
2428
2403
|
if (status.deterministicBuild.status !== "qualifying") {
|
|
2429
|
-
|
|
2404
|
+
actions.push({
|
|
2430
2405
|
id: "build-paper",
|
|
2431
2406
|
command: "buildchain paper build --execute --json",
|
|
2432
2407
|
description: "Run the existing qualifying two-clean-build gate.",
|
|
2433
2408
|
});
|
|
2434
2409
|
}
|
|
2435
|
-
return
|
|
2436
|
-
|
|
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
|
+
});
|
|
2547
|
+
return {
|
|
2548
|
+
schemaVersion: 1,
|
|
2437
2549
|
contract: PAPER_PREFLIGHT_CONTRACT,
|
|
2438
2550
|
ok: blockingChecks.length === 0,
|
|
2439
2551
|
localReady: blockingChecks.length === 0,
|
|
@@ -2725,467 +2837,30 @@ function bootstrapPackageShape({ packageName, version }) {
|
|
|
2725
2837
|
};
|
|
2726
2838
|
}
|
|
2727
2839
|
|
|
2728
|
-
export function executePaperNpmBootstrap({
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
const name = normalizePackageName(
|
|
2755
|
-
packageName ||
|
|
2756
|
-
configResult.loaded.config.publish?.package ||
|
|
2757
|
-
configResult.loaded.config.publish?.mainPackage,
|
|
2758
|
-
);
|
|
2759
|
-
const repo = normalizeRepository(
|
|
2760
|
-
repository || resolvePaperRepository(resolvedCwd),
|
|
2761
|
-
);
|
|
2762
|
-
const provisioning = validatePaperProvisioningAuthority(resolvedCwd);
|
|
2763
|
-
if (!provisioning.valid) {
|
|
2764
|
-
throw new Error(
|
|
2765
|
-
`paper npm bootstrap requires a valid provisioning authority: ${provisioning.errors.join("; ")}`,
|
|
2766
|
-
);
|
|
2767
|
-
}
|
|
2768
|
-
const expectedPublisher = expectedPaperTrustedPublisher(provisioning.value);
|
|
2769
|
-
if (
|
|
2770
|
-
provisioning.value.package?.name !== name ||
|
|
2771
|
-
expectedPublisher.repository !== repo ||
|
|
2772
|
-
expectedPublisher.workflow !== toPosix(workflow).replace(/^\/+/, "") ||
|
|
2773
|
-
expectedPublisher.environment !== String(environment || "")
|
|
2774
|
-
) {
|
|
2775
|
-
throw new Error(
|
|
2776
|
-
"paper npm bootstrap coordinates differ from the exact provisioning authority",
|
|
2777
|
-
);
|
|
2778
|
-
}
|
|
2779
|
-
if (execute && confirmedPackage !== name) {
|
|
2780
|
-
throw new Error(
|
|
2781
|
-
`real npm bootstrap requires --confirm-public-package ${name}`,
|
|
2782
|
-
);
|
|
2783
|
-
}
|
|
2784
|
-
if (execute && offline) {
|
|
2785
|
-
throw new Error("real npm bootstrap cannot run with --offline");
|
|
2786
|
-
}
|
|
2787
|
-
if (execute && (!repo || !workflow)) {
|
|
2788
|
-
throw new Error(
|
|
2789
|
-
"real npm bootstrap requires GitHub repository and workflow coordinates",
|
|
2790
|
-
);
|
|
2791
|
-
}
|
|
2792
|
-
const packageObservation = offline
|
|
2793
|
-
? {
|
|
2794
|
-
status: "unknown",
|
|
2795
|
-
exists: null,
|
|
2796
|
-
version: "",
|
|
2797
|
-
registry,
|
|
2798
|
-
errorCode: "offline",
|
|
2799
|
-
}
|
|
2800
|
-
: liveNpmPackageObservation(name, registry, resolvedCwd);
|
|
2801
|
-
const auth = offline
|
|
2802
|
-
? {
|
|
2803
|
-
status: "unknown",
|
|
2804
|
-
authenticated: null,
|
|
2805
|
-
identity: "",
|
|
2806
|
-
errorCode: "offline",
|
|
2807
|
-
}
|
|
2808
|
-
: liveNpmAuthObservation(registry, resolvedCwd);
|
|
2809
|
-
const plan = {
|
|
2810
|
-
schemaVersion: 1,
|
|
2811
|
-
contract: PAPER_NPM_BOOTSTRAP_CONTRACT,
|
|
2812
|
-
ok: true,
|
|
2813
|
-
dryRun: !execute,
|
|
2814
|
-
externalMutation: execute,
|
|
2815
|
-
package: {
|
|
2816
|
-
name,
|
|
2817
|
-
bootstrapVersion,
|
|
2818
|
-
registry,
|
|
2819
|
-
existsBefore: packageObservation.exists,
|
|
2820
|
-
observedVersion: packageObservation.version,
|
|
2821
|
-
},
|
|
2822
|
-
repository: {
|
|
2823
|
-
github: repo,
|
|
2824
|
-
workflow,
|
|
2825
|
-
environment,
|
|
2826
|
-
},
|
|
2827
|
-
authority: {
|
|
2828
|
-
digest: provisioning.value.authorityDigest,
|
|
2829
|
-
policyDigest: provisioning.value.policy.policyDigest,
|
|
2830
|
-
},
|
|
2831
|
-
auth: {
|
|
2832
|
-
authenticated: auth.authenticated,
|
|
2833
|
-
identity: auth.identity,
|
|
2834
|
-
errorCode: auth.errorCode,
|
|
2835
|
-
},
|
|
2836
|
-
publish: {
|
|
2837
|
-
status: packageObservation.exists === true ? "existing" : "planned",
|
|
2838
|
-
distTag: "bootstrap",
|
|
2839
|
-
access: "public",
|
|
2840
|
-
},
|
|
2841
|
-
trust: {
|
|
2842
|
-
status: "planned",
|
|
2843
|
-
urls: [],
|
|
2844
|
-
},
|
|
2845
|
-
receipts: [],
|
|
2846
|
-
nextActions: execute
|
|
2847
|
-
? []
|
|
2848
|
-
: [
|
|
2849
|
-
{
|
|
2850
|
-
id: "confirm-public-bootstrap",
|
|
2851
|
-
command: `buildchain paper bootstrap npm --execute --confirm-public-package ${name} --json`,
|
|
2852
|
-
description:
|
|
2853
|
-
"After reviewing this dry-run, bootstrap the exact public package and configure GitHub Trusted Publishing.",
|
|
2854
|
-
},
|
|
2855
|
-
],
|
|
2856
|
-
};
|
|
2857
|
-
if (!execute) {
|
|
2858
|
-
const dryRunRoot = fs.mkdtempSync(
|
|
2859
|
-
path.join(os.tmpdir(), "buildchain-paper-bootstrap-dry-run-"),
|
|
2860
|
-
);
|
|
2861
|
-
try {
|
|
2862
|
-
fs.writeFileSync(
|
|
2863
|
-
path.join(dryRunRoot, "package.json"),
|
|
2864
|
-
jsonText(
|
|
2865
|
-
bootstrapPackageShape({
|
|
2866
|
-
packageName: name,
|
|
2867
|
-
version: bootstrapVersion,
|
|
2868
|
-
}),
|
|
2869
|
-
),
|
|
2870
|
-
);
|
|
2871
|
-
const configArgs = userconfig ? ["--userconfig", userconfig] : [];
|
|
2872
|
-
const pack = commandResult(
|
|
2873
|
-
"npm",
|
|
2874
|
-
[
|
|
2875
|
-
"pack",
|
|
2876
|
-
"--dry-run",
|
|
2877
|
-
"--json",
|
|
2878
|
-
"--ignore-scripts",
|
|
2879
|
-
`--registry=${registry}`,
|
|
2880
|
-
...configArgs,
|
|
2881
|
-
],
|
|
2882
|
-
{ cwd: dryRunRoot },
|
|
2883
|
-
);
|
|
2884
|
-
const publish = commandResult(
|
|
2885
|
-
"npm",
|
|
2886
|
-
[
|
|
2887
|
-
"publish",
|
|
2888
|
-
"--dry-run",
|
|
2889
|
-
"--ignore-scripts",
|
|
2890
|
-
"--access",
|
|
2891
|
-
"public",
|
|
2892
|
-
"--tag",
|
|
2893
|
-
"bootstrap",
|
|
2894
|
-
`--registry=${registry}`,
|
|
2895
|
-
...configArgs,
|
|
2896
|
-
],
|
|
2897
|
-
{ cwd: dryRunRoot },
|
|
2898
|
-
);
|
|
2899
|
-
return {
|
|
2900
|
-
...plan,
|
|
2901
|
-
ok: pack.ok && publish.ok,
|
|
2902
|
-
errorCode: !pack.ok
|
|
2903
|
-
? "npm-pack-dry-run-failed"
|
|
2904
|
-
: !publish.ok
|
|
2905
|
-
? "npm-publish-dry-run-failed"
|
|
2906
|
-
: "",
|
|
2907
|
-
dryRunChecks: {
|
|
2908
|
-
minimalPackageOnly: true,
|
|
2909
|
-
pack: {
|
|
2910
|
-
status: pack.ok ? "pass" : "fail",
|
|
2911
|
-
entryCount: (() => {
|
|
2912
|
-
const parsed = safeParseJson(pack.stdout);
|
|
2913
|
-
const value = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
2914
|
-
return Array.isArray(value?.files) ? value.files.length : 0;
|
|
2915
|
-
})(),
|
|
2916
|
-
},
|
|
2917
|
-
publish: {
|
|
2918
|
-
status: publish.ok ? "pass" : "fail",
|
|
2919
|
-
registry,
|
|
2920
|
-
access: "public",
|
|
2921
|
-
distTag: "bootstrap",
|
|
2922
|
-
},
|
|
2923
|
-
},
|
|
2924
|
-
};
|
|
2925
|
-
} finally {
|
|
2926
|
-
fs.rmSync(dryRunRoot, { recursive: true, force: true });
|
|
2927
|
-
}
|
|
2928
|
-
}
|
|
2929
|
-
if (packageObservation.exists === null) {
|
|
2930
|
-
return {
|
|
2931
|
-
...plan,
|
|
2932
|
-
ok: false,
|
|
2933
|
-
errorCode: "npm-package-status-unknown",
|
|
2934
|
-
publish: {
|
|
2935
|
-
...plan.publish,
|
|
2936
|
-
status: "blocked",
|
|
2937
|
-
},
|
|
2938
|
-
trust: {
|
|
2939
|
-
status: "blocked",
|
|
2940
|
-
urls: [],
|
|
2941
|
-
},
|
|
2942
|
-
nextActions: [
|
|
2943
|
-
{
|
|
2944
|
-
id: "verify-npm-package",
|
|
2945
|
-
command: `npm view ${name} version --json --registry=${registry}`,
|
|
2946
|
-
description:
|
|
2947
|
-
"Resolve whether the exact public package exists before any publish mutation.",
|
|
2948
|
-
},
|
|
2949
|
-
],
|
|
2950
|
-
};
|
|
2951
|
-
}
|
|
2952
|
-
if (auth.authenticated !== true) {
|
|
2953
|
-
return {
|
|
2954
|
-
...plan,
|
|
2955
|
-
ok: false,
|
|
2956
|
-
errorCode: "npm-auth-required",
|
|
2957
|
-
publish: {
|
|
2958
|
-
...plan.publish,
|
|
2959
|
-
status: "blocked",
|
|
2960
|
-
},
|
|
2961
|
-
trust: {
|
|
2962
|
-
status: "blocked",
|
|
2963
|
-
urls: [],
|
|
2964
|
-
},
|
|
2965
|
-
nextActions: [
|
|
2966
|
-
{
|
|
2967
|
-
id: "authenticate-npm",
|
|
2968
|
-
command: `npm whoami --registry=${registry}`,
|
|
2969
|
-
description:
|
|
2970
|
-
"Authenticate npm without sharing token contents, then rerun the exact bootstrap command.",
|
|
2971
|
-
},
|
|
2972
|
-
],
|
|
2973
|
-
};
|
|
2974
|
-
}
|
|
2975
|
-
const tempRoot = fs.mkdtempSync(
|
|
2976
|
-
path.join(os.tmpdir(), "buildchain-paper-bootstrap-"),
|
|
2977
|
-
);
|
|
2978
|
-
try {
|
|
2979
|
-
fs.writeFileSync(
|
|
2980
|
-
path.join(tempRoot, "package.json"),
|
|
2981
|
-
jsonText(
|
|
2982
|
-
bootstrapPackageShape({ packageName: name, version: bootstrapVersion }),
|
|
2983
|
-
),
|
|
2984
|
-
);
|
|
2985
|
-
const configArgs = userconfig ? ["--userconfig", userconfig] : [];
|
|
2986
|
-
const pack = commandResult(
|
|
2987
|
-
"npm",
|
|
2988
|
-
[
|
|
2989
|
-
"pack",
|
|
2990
|
-
"--dry-run",
|
|
2991
|
-
"--json",
|
|
2992
|
-
"--ignore-scripts",
|
|
2993
|
-
`--registry=${registry}`,
|
|
2994
|
-
...configArgs,
|
|
2995
|
-
],
|
|
2996
|
-
{ cwd: tempRoot },
|
|
2997
|
-
);
|
|
2998
|
-
if (!pack.ok) {
|
|
2999
|
-
return {
|
|
3000
|
-
...plan,
|
|
3001
|
-
ok: false,
|
|
3002
|
-
errorCode: "npm-pack-dry-run-failed",
|
|
3003
|
-
publish: { ...plan.publish, status: "blocked" },
|
|
3004
|
-
trust: { status: "blocked", urls: [] },
|
|
3005
|
-
};
|
|
3006
|
-
}
|
|
3007
|
-
const publishDryRun = commandResult(
|
|
3008
|
-
"npm",
|
|
3009
|
-
[
|
|
3010
|
-
"publish",
|
|
3011
|
-
"--dry-run",
|
|
3012
|
-
"--ignore-scripts",
|
|
3013
|
-
"--access",
|
|
3014
|
-
"public",
|
|
3015
|
-
"--tag",
|
|
3016
|
-
"bootstrap",
|
|
3017
|
-
`--registry=${registry}`,
|
|
3018
|
-
...configArgs,
|
|
3019
|
-
],
|
|
3020
|
-
{ cwd: tempRoot },
|
|
3021
|
-
);
|
|
3022
|
-
if (!publishDryRun.ok) {
|
|
3023
|
-
return {
|
|
3024
|
-
...plan,
|
|
3025
|
-
ok: false,
|
|
3026
|
-
errorCode: "npm-publish-dry-run-failed",
|
|
3027
|
-
publish: { ...plan.publish, status: "blocked" },
|
|
3028
|
-
trust: { status: "blocked", urls: [] },
|
|
3029
|
-
};
|
|
3030
|
-
}
|
|
3031
|
-
let publishStatus =
|
|
3032
|
-
packageObservation.exists === true ? "existing" : "published";
|
|
3033
|
-
if (packageObservation.exists !== true) {
|
|
3034
|
-
const published = commandResult(
|
|
3035
|
-
"npm",
|
|
3036
|
-
[
|
|
3037
|
-
"publish",
|
|
3038
|
-
"--ignore-scripts",
|
|
3039
|
-
"--access",
|
|
3040
|
-
"public",
|
|
3041
|
-
"--tag",
|
|
3042
|
-
"bootstrap",
|
|
3043
|
-
`--registry=${registry}`,
|
|
3044
|
-
...configArgs,
|
|
3045
|
-
],
|
|
3046
|
-
{ cwd: tempRoot, timeout: 60000 },
|
|
3047
|
-
);
|
|
3048
|
-
if (!published.ok) {
|
|
3049
|
-
const failure = {
|
|
3050
|
-
...plan,
|
|
3051
|
-
ok: false,
|
|
3052
|
-
errorCode: "npm-publish-failed",
|
|
3053
|
-
publish: { ...plan.publish, status: "failed" },
|
|
3054
|
-
trust: { status: "blocked", urls: [] },
|
|
3055
|
-
};
|
|
3056
|
-
failure.receipts = [
|
|
3057
|
-
writePaperReceipt(resolvedCwd, PAPER_PATHS.npmBootstrap, failure),
|
|
3058
|
-
];
|
|
3059
|
-
return failure;
|
|
3060
|
-
}
|
|
3061
|
-
}
|
|
3062
|
-
const trustArgs = [
|
|
3063
|
-
"trust",
|
|
3064
|
-
"github",
|
|
3065
|
-
name,
|
|
3066
|
-
"--repo",
|
|
3067
|
-
repo,
|
|
3068
|
-
"--file",
|
|
3069
|
-
workflow,
|
|
3070
|
-
...(environment ? ["--env", environment] : []),
|
|
3071
|
-
"--allow-publish",
|
|
3072
|
-
"--yes",
|
|
3073
|
-
"--json",
|
|
3074
|
-
`--registry=${registry}`,
|
|
3075
|
-
...configArgs,
|
|
3076
|
-
];
|
|
3077
|
-
const trustCommand = commandResult("npm", trustArgs, {
|
|
3078
|
-
cwd: resolvedCwd,
|
|
3079
|
-
timeout: 60000,
|
|
3080
|
-
});
|
|
3081
|
-
const parsedTrust = safeParseJson(trustCommand.stdout);
|
|
3082
|
-
const urls = [
|
|
3083
|
-
...extractUrls(
|
|
3084
|
-
parsedTrust || `${trustCommand.stdout}\n${trustCommand.stderr}`,
|
|
3085
|
-
),
|
|
3086
|
-
];
|
|
3087
|
-
const trustList =
|
|
3088
|
-
trustCommand.ok && urls.length === 0
|
|
3089
|
-
? liveNpmTrustObservation(
|
|
3090
|
-
name,
|
|
3091
|
-
registry,
|
|
3092
|
-
resolvedCwd,
|
|
3093
|
-
expectedPublisher,
|
|
3094
|
-
)
|
|
3095
|
-
: {
|
|
3096
|
-
status: "unknown",
|
|
3097
|
-
configured: null,
|
|
3098
|
-
publishers: [],
|
|
3099
|
-
errorCode:
|
|
3100
|
-
urls.length > 0 ? "web-action-required" : "npm-trust-failed",
|
|
3101
|
-
};
|
|
3102
|
-
const trustStatus =
|
|
3103
|
-
trustCommand.ok && trustList.configured === true
|
|
3104
|
-
? "configured"
|
|
3105
|
-
: urls.length > 0
|
|
3106
|
-
? "action-required"
|
|
3107
|
-
: "failed";
|
|
3108
|
-
const packageAfter = liveNpmPackageObservation(name, registry, resolvedCwd);
|
|
3109
|
-
const packageVerified = packageAfter.exists === true;
|
|
3110
|
-
const receipt = {
|
|
3111
|
-
...plan,
|
|
3112
|
-
ok: trustStatus !== "failed" && packageVerified,
|
|
3113
|
-
dryRun: false,
|
|
3114
|
-
externalMutation: true,
|
|
3115
|
-
errorCode:
|
|
3116
|
-
trustStatus === "failed"
|
|
3117
|
-
? "npm-trust-failed"
|
|
3118
|
-
: packageVerified
|
|
3119
|
-
? ""
|
|
3120
|
-
: "npm-package-readback-failed",
|
|
3121
|
-
package: {
|
|
3122
|
-
...plan.package,
|
|
3123
|
-
existsAfter: packageAfter.exists,
|
|
3124
|
-
observedVersionAfter: packageAfter.version,
|
|
3125
|
-
readbackStatus: packageAfter.status,
|
|
3126
|
-
},
|
|
3127
|
-
publish: {
|
|
3128
|
-
...plan.publish,
|
|
3129
|
-
status: publishStatus,
|
|
3130
|
-
},
|
|
3131
|
-
trust: {
|
|
3132
|
-
status: trustStatus,
|
|
3133
|
-
urls,
|
|
3134
|
-
expectedPublisher,
|
|
3135
|
-
exactBinding: trustList.exactBinding === true,
|
|
3136
|
-
publishers: trustList.publishers,
|
|
3137
|
-
},
|
|
3138
|
-
nextActions:
|
|
3139
|
-
trustStatus === "configured" && packageVerified
|
|
3140
|
-
? [
|
|
3141
|
-
{
|
|
3142
|
-
id: "paper-preflight",
|
|
3143
|
-
command: "buildchain paper preflight --json",
|
|
3144
|
-
description:
|
|
3145
|
-
"Verify package existence and Trusted Publisher binding from live read-only sources.",
|
|
3146
|
-
},
|
|
3147
|
-
]
|
|
3148
|
-
: trustStatus === "action-required" && packageVerified
|
|
3149
|
-
? [
|
|
3150
|
-
{
|
|
3151
|
-
id: "complete-npm-web-action",
|
|
3152
|
-
command: "",
|
|
3153
|
-
description:
|
|
3154
|
-
"Open the exact npm URL returned below, complete the web step, then rerun preflight.",
|
|
3155
|
-
urls,
|
|
3156
|
-
},
|
|
3157
|
-
]
|
|
3158
|
-
: !packageVerified
|
|
3159
|
-
? [
|
|
3160
|
-
{
|
|
3161
|
-
id: "verify-npm-package-readback",
|
|
3162
|
-
command: `npm view ${name} version --json --registry=${registry}`,
|
|
3163
|
-
description:
|
|
3164
|
-
"The mutation returned but the official registry did not prove package existence; do not infer success.",
|
|
3165
|
-
},
|
|
3166
|
-
]
|
|
3167
|
-
: [
|
|
3168
|
-
{
|
|
3169
|
-
id: "retry-npm-trust",
|
|
3170
|
-
command: `buildchain paper bootstrap npm --execute --confirm-public-package ${name} --json`,
|
|
3171
|
-
description:
|
|
3172
|
-
"The package exists; retry only the idempotent Trusted Publisher configuration path.",
|
|
3173
|
-
},
|
|
3174
|
-
],
|
|
3175
|
-
};
|
|
3176
|
-
receipt.receipts = [
|
|
3177
|
-
writePaperReceipt(resolvedCwd, PAPER_PATHS.npmBootstrap, receipt),
|
|
3178
|
-
];
|
|
3179
|
-
if (
|
|
3180
|
-
packageVerified &&
|
|
3181
|
-
(trustStatus === "configured" || trustStatus === "action-required")
|
|
3182
|
-
) {
|
|
3183
|
-
receipt.receipts.push(
|
|
3184
|
-
writePaperReceipt(resolvedCwd, PAPER_PATHS.npmTrust, receipt),
|
|
3185
|
-
);
|
|
3186
|
-
}
|
|
3187
|
-
return receipt;
|
|
3188
|
-
} finally {
|
|
3189
|
-
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
3190
|
-
}
|
|
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
|
+
});
|
|
3191
2866
|
}
|