@kungfu-tech/buildchain 4.0.2-alpha.41 → 4.0.2-alpha.42
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/architecture/agent-change-map.md +3 -1
- package/architecture/internal-capabilities.json +4 -1
- package/architecture/maintainability-debt.json +12 -12
- package/architecture/maintainability-policy.json +4 -4
- package/dist/site/buildchain-contract.json +4 -4
- package/dist/site/buildchain-site.json +10 -10
- package/dist/site/kfd-claims.json +2 -2
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +2 -2
- package/dist/site/node-api-registry.json +34 -34
- package/dist/site/page-registry.json +4 -4
- package/dist/site/public-surface-audit.json +2 -2
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +6 -6
- package/dist/site/workflow-registry.json +5 -5
- package/docs/cli.md +8 -2
- package/docs/node-api-reference.md +61 -61
- package/package.json +1 -1
- package/packages/core/paper-agent-entry.js +4 -2
- package/packages/core/paper-fleet.js +1 -1
- package/packages/core/paper-runtime-channels.js +283 -0
- package/packages/core/paper-scaffold-content.js +10 -0
- package/packages/core/paper.js +32 -50
- package/scripts/paper-work-fleet-cli.mjs +2 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
createBuildchainContractLock,
|
|
5
|
+
createBuildchainContractWorld,
|
|
6
|
+
finalizeBuildchainContractWorld,
|
|
7
|
+
readBuildchainContractWorld,
|
|
8
|
+
sha256Json,
|
|
9
|
+
} from "./buildchain-contract.js";
|
|
10
|
+
import {
|
|
11
|
+
gitValue,
|
|
12
|
+
readJson,
|
|
13
|
+
sha256Text,
|
|
14
|
+
stableJson,
|
|
15
|
+
} from "./paper-repository.js";
|
|
16
|
+
|
|
17
|
+
export const PAPER_ALPHA_LOCK = ".buildchain/alpha-contract-lock.json";
|
|
18
|
+
|
|
19
|
+
function fileDigestMatches(cwd, relative, digest) {
|
|
20
|
+
if (!relative || !/^sha256:[0-9a-f]{64}$/.test(digest || "")) return false;
|
|
21
|
+
const absolute = path.resolve(cwd, relative);
|
|
22
|
+
return (
|
|
23
|
+
fs.existsSync(absolute) &&
|
|
24
|
+
sha256Text(fs.readFileSync(absolute, "utf8")) === digest
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function workflowErrors(cwd, authority, workflow) {
|
|
29
|
+
if (!workflow?.path || !workflow?.sourceDigest)
|
|
30
|
+
return ["paper workflow authority is incomplete"];
|
|
31
|
+
if (!fileDigestMatches(cwd, workflow.path, workflow.sourceDigest))
|
|
32
|
+
return [`paper workflow source digest mismatch: ${workflow.path}`];
|
|
33
|
+
const floating = /^v4(?:-alpha)?$/.test(authority.runtime?.ref || "");
|
|
34
|
+
const refs = floating
|
|
35
|
+
? workflow.reusablePath === ".github/workflows/paper-release-sealed.yml"
|
|
36
|
+
? ["v4-alpha", "v4"]
|
|
37
|
+
: ["v4-alpha"]
|
|
38
|
+
: [authority.runtime.resolvedSha];
|
|
39
|
+
const text = fs.readFileSync(path.resolve(cwd, workflow.path), "utf8");
|
|
40
|
+
const errors = [];
|
|
41
|
+
if (
|
|
42
|
+
refs.some(
|
|
43
|
+
(ref) =>
|
|
44
|
+
!text.includes(
|
|
45
|
+
`uses: ${authority.runtime.repository}/${workflow.reusablePath}@${ref}\n`,
|
|
46
|
+
),
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
errors.push(
|
|
50
|
+
`paper workflow reusable source is not exact: ${workflow.path}`,
|
|
51
|
+
);
|
|
52
|
+
if (
|
|
53
|
+
(floating || workflow.reusablePath !== ".github/workflows/check.yml") &&
|
|
54
|
+
refs.some((ref) => !text.includes(`buildchain-ref: ${ref}\n`))
|
|
55
|
+
)
|
|
56
|
+
errors.push(`paper workflow runtime input is not exact: ${workflow.path}`);
|
|
57
|
+
if (
|
|
58
|
+
floating &&
|
|
59
|
+
[
|
|
60
|
+
...text.matchAll(/uses:\s*kungfu-systems\/buildchain\/[^\s@]+@([^\s]+)/g),
|
|
61
|
+
].some((match) => !refs.includes(match[1]))
|
|
62
|
+
)
|
|
63
|
+
errors.push(
|
|
64
|
+
`paper workflow contains an unaccepted Buildchain selector: ${workflow.path}`,
|
|
65
|
+
);
|
|
66
|
+
return errors;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function channelLockMatches(cwd, authority, ref, expectedPath) {
|
|
70
|
+
const channel = authority.admission?.channels?.[ref];
|
|
71
|
+
const lock = readJson(path.resolve(cwd, expectedPath)).value;
|
|
72
|
+
const entry = readJson(
|
|
73
|
+
path.resolve(cwd, ".buildchain/paper/agent-entry.json"),
|
|
74
|
+
).value;
|
|
75
|
+
return (
|
|
76
|
+
channel?.ref === ref &&
|
|
77
|
+
channel?.lockPath === expectedPath &&
|
|
78
|
+
fileDigestMatches(cwd, expectedPath, channel?.lockDigest) &&
|
|
79
|
+
lock?.buildchain?.ref === ref &&
|
|
80
|
+
lock?.buildchain?.resolvedSha === channel?.resolvedSha &&
|
|
81
|
+
lock?.buildchain?.majorLine === "v4" &&
|
|
82
|
+
entry?.runtime?.channels?.[ref] === channel?.resolvedSha
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function paperProvisioningWorkflowErrors(cwd, authority) {
|
|
87
|
+
const errors = [
|
|
88
|
+
authority.workflows?.build,
|
|
89
|
+
authority.workflows?.verify,
|
|
90
|
+
authority.workflows?.release,
|
|
91
|
+
].flatMap((workflow) => workflowErrors(cwd, authority, workflow));
|
|
92
|
+
if (
|
|
93
|
+
!fileDigestMatches(
|
|
94
|
+
cwd,
|
|
95
|
+
authority.admission?.contractLockPath || ".buildchain/contract-lock.json",
|
|
96
|
+
authority.admission?.contractLockDigest,
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
errors.push("paper contract lock bytes differ from provisioning authority");
|
|
100
|
+
if (/^v4(?:-alpha)?$/.test(authority.runtime?.ref || "")) {
|
|
101
|
+
for (const [ref, lockPath] of [
|
|
102
|
+
["v4", ".buildchain/contract-lock.json"],
|
|
103
|
+
["v4-alpha", PAPER_ALPHA_LOCK],
|
|
104
|
+
]) {
|
|
105
|
+
if (!channelLockMatches(cwd, authority, ref, lockPath))
|
|
106
|
+
errors.push(
|
|
107
|
+
`paper ${ref} contract lock is not bound to provisioning authority`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return errors;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function paperRuntimeSourceMatches(runtime, sha, mode, admission) {
|
|
115
|
+
return (
|
|
116
|
+
runtime?.sourceSha === sha ||
|
|
117
|
+
(mode === "ci" &&
|
|
118
|
+
/^4\./.test(runtime?.version || "") &&
|
|
119
|
+
([runtime?.channels?.v4, runtime?.channels?.["v4-alpha"]].includes(sha) ||
|
|
120
|
+
(admission?.compatible === true &&
|
|
121
|
+
admission.sha === sha &&
|
|
122
|
+
/^v4(?:-alpha)?$/.test(admission.ref))))
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function selectPaperRuntime(runtime, authority) {
|
|
127
|
+
const channel = Object.values(authority?.admission?.channels || {}).find(
|
|
128
|
+
(entry) => entry.resolvedSha === runtime.resolvedSha,
|
|
129
|
+
);
|
|
130
|
+
const ref =
|
|
131
|
+
channel?.ref || (runtime.version.includes("-") ? "v4-alpha" : "v4");
|
|
132
|
+
return authority?.admission?.channels?.[ref] ? { ...runtime, ref } : runtime;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function paperRuntimeLockPath(cwd, runtime, authority) {
|
|
136
|
+
return path.resolve(
|
|
137
|
+
cwd,
|
|
138
|
+
authority?.admission?.channels?.[runtime.ref]?.lockPath ||
|
|
139
|
+
".buildchain/contract-lock.json",
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function channelWorld(root, ref, current) {
|
|
144
|
+
if (current) return current;
|
|
145
|
+
const sha = gitValue(root, [
|
|
146
|
+
"rev-parse",
|
|
147
|
+
"--verify",
|
|
148
|
+
`refs/tags/${ref}^{commit}`,
|
|
149
|
+
]);
|
|
150
|
+
const text =
|
|
151
|
+
sha &&
|
|
152
|
+
gitValue(root, ["show", `${sha}:dist/site/buildchain-contract.json`]);
|
|
153
|
+
if (!/^[0-9a-f]{40}$/.test(sha) || !text) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Paper migration needs the fetched ${ref} tag or an explicit ${ref === "v4" ? "stable" : "alpha"} Buildchain root`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
const published = JSON.parse(text);
|
|
159
|
+
const world = Array.isArray(published.compatibilityFacts)
|
|
160
|
+
? finalizeBuildchainContractWorld(published)
|
|
161
|
+
: {
|
|
162
|
+
...published,
|
|
163
|
+
contractDigest: `sha256:${sha256Json({ ...published, contractDigest: undefined, compatibilityDigest: undefined })}`,
|
|
164
|
+
compatibilityDigest: `sha256:${sha256Json({ schemaVersion: published.schemaVersion, contract: published.contract, majorLine: published.majorLine, surfaces: published.surfaces.map(({ id, kind, breakingDigest }) => ({ id, kind, breakingDigest })) })}`,
|
|
165
|
+
};
|
|
166
|
+
if (
|
|
167
|
+
published.contractDigest !== world.contractDigest ||
|
|
168
|
+
published.compatibilityDigest !== world.compatibilityDigest
|
|
169
|
+
) {
|
|
170
|
+
throw new Error(`Paper ${ref} contract digest mismatch`);
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
sha,
|
|
174
|
+
world,
|
|
175
|
+
acceptedAt: gitValue(root, ["show", "-s", "--format=%cI", sha]),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function explicitWorld(root, ref) {
|
|
180
|
+
const sha = gitValue(root, ["rev-parse", "HEAD"]);
|
|
181
|
+
if (!/^[0-9a-f]{40}$/.test(sha))
|
|
182
|
+
throw new Error("Paper channel root must have exact Git provenance");
|
|
183
|
+
if (gitValue(root, ["status", "--porcelain", "--untracked-files=no"]))
|
|
184
|
+
throw new Error("Paper channel root must have committed runtime bytes");
|
|
185
|
+
const pkg = readJson(path.join(root, "package.json")).value;
|
|
186
|
+
if (
|
|
187
|
+
pkg?.name !== "@kungfu-tech/buildchain" ||
|
|
188
|
+
!/^4\./.test(pkg?.version || "") ||
|
|
189
|
+
pkg.version.includes("-") !== (ref === "v4-alpha")
|
|
190
|
+
)
|
|
191
|
+
throw new Error(`Paper channel root does not belong to ${ref}`);
|
|
192
|
+
const contractPath = path.join(root, "dist/site/buildchain-contract.json");
|
|
193
|
+
return {
|
|
194
|
+
sha,
|
|
195
|
+
world: fs.existsSync(contractPath)
|
|
196
|
+
? readBuildchainContractWorld(contractPath)
|
|
197
|
+
: createBuildchainContractWorld({ root }),
|
|
198
|
+
acceptedAt: gitValue(root, ["show", "-s", "--format=%cI", sha]),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function paperV4Channels({
|
|
203
|
+
cwd,
|
|
204
|
+
buildchainRoot,
|
|
205
|
+
buildchainVersion,
|
|
206
|
+
buildchainSha,
|
|
207
|
+
contractWorld,
|
|
208
|
+
acceptedAt,
|
|
209
|
+
stableBuildchainRoot = "",
|
|
210
|
+
alphaBuildchainRoot = "",
|
|
211
|
+
}) {
|
|
212
|
+
const selectedRef = buildchainVersion.includes("-") ? "v4-alpha" : "v4";
|
|
213
|
+
const current = {
|
|
214
|
+
sha: buildchainSha,
|
|
215
|
+
world: contractWorld,
|
|
216
|
+
acceptedAt,
|
|
217
|
+
};
|
|
218
|
+
const channels = {};
|
|
219
|
+
for (const [ref, lockPath, explicitRoot] of [
|
|
220
|
+
["v4", ".buildchain/contract-lock.json", stableBuildchainRoot],
|
|
221
|
+
["v4-alpha", PAPER_ALPHA_LOCK, alphaBuildchainRoot],
|
|
222
|
+
]) {
|
|
223
|
+
const resolved = explicitRoot
|
|
224
|
+
? explicitWorld(explicitRoot, ref)
|
|
225
|
+
: channelWorld(buildchainRoot, ref, ref === selectedRef ? current : null);
|
|
226
|
+
if (resolved.world.majorLine !== "v4")
|
|
227
|
+
throw new Error(`Paper ${ref} contract must belong to v4`);
|
|
228
|
+
const existing = readJson(path.join(cwd, lockPath)).value;
|
|
229
|
+
const lock = createBuildchainContractLock({
|
|
230
|
+
buildchainRef: ref,
|
|
231
|
+
resolvedSha: resolved.sha,
|
|
232
|
+
contractWorld: resolved.world,
|
|
233
|
+
acceptedAt:
|
|
234
|
+
existing?.buildchain?.resolvedSha === resolved.sha
|
|
235
|
+
? existing.buildchain.acceptedAt
|
|
236
|
+
: new Date(resolved.acceptedAt).toISOString(),
|
|
237
|
+
});
|
|
238
|
+
const content = `${JSON.stringify(lock, null, 2)}\n`;
|
|
239
|
+
channels[ref] = {
|
|
240
|
+
ref,
|
|
241
|
+
resolvedSha: resolved.sha,
|
|
242
|
+
lockPath,
|
|
243
|
+
lockDigest: sha256Text(content),
|
|
244
|
+
content,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return { selectedRef, channels };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function bindPaperV4Authority(authority, channelPlan, files) {
|
|
251
|
+
const { selectedRef, channels } = channelPlan;
|
|
252
|
+
const entryPath = authority.agentEntry.policyPath;
|
|
253
|
+
const entry = JSON.parse(files.get(entryPath));
|
|
254
|
+
entry.runtime.channels = Object.fromEntries(
|
|
255
|
+
Object.entries(channels).map(([ref, channel]) => [
|
|
256
|
+
ref,
|
|
257
|
+
channel.resolvedSha,
|
|
258
|
+
]),
|
|
259
|
+
);
|
|
260
|
+
const { entryDigest: _entryDigest, ...entryPayload } = entry;
|
|
261
|
+
entry.entryDigest = sha256Text(stableJson(entryPayload));
|
|
262
|
+
files.set(entryPath, `${JSON.stringify(entry, null, 2)}\n`);
|
|
263
|
+
authority.agentEntry.policyDigest = sha256Text(files.get(entryPath));
|
|
264
|
+
authority.runtime.ref = selectedRef;
|
|
265
|
+
authority.admission.acceptedRef = selectedRef;
|
|
266
|
+
authority.admission.contractLockPath = channels[selectedRef].lockPath;
|
|
267
|
+
authority.admission.contractLockDigest = channels[selectedRef].lockDigest;
|
|
268
|
+
authority.admission.channels = Object.fromEntries(
|
|
269
|
+
Object.entries(channels).map(([ref, { content: _content, ...channel }]) => [
|
|
270
|
+
ref,
|
|
271
|
+
channel,
|
|
272
|
+
]),
|
|
273
|
+
);
|
|
274
|
+
for (const [role, workflow] of Object.entries(authority.workflows)) {
|
|
275
|
+
workflow.reusableRef = role === "release" ? "v4" : "v4-alpha";
|
|
276
|
+
workflow.reusableRefs =
|
|
277
|
+
role === "release" ? ["v4", "v4-alpha"] : ["v4-alpha"];
|
|
278
|
+
workflow.sourceDigest = sha256Text(files.get(workflow.path));
|
|
279
|
+
}
|
|
280
|
+
const { authorityDigest: _authorityDigest, ...payload } = authority;
|
|
281
|
+
authority.authorityDigest = sha256Text(stableJson(payload));
|
|
282
|
+
return authority;
|
|
283
|
+
}
|
|
@@ -10,6 +10,12 @@ function jsonText(value) {
|
|
|
10
10
|
return `${JSON.stringify(value, null, 2)}\n`;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
export function paperDependencyIgnore(current = "") {
|
|
14
|
+
return /^(?:\/)?node_modules\/?\s*$/m.test(current)
|
|
15
|
+
? current
|
|
16
|
+
: `${current}${current.endsWith("\n") || !current ? "" : "\n"}node_modules/\n`;
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
function texEscape(value) {
|
|
14
20
|
return String(value || "")
|
|
15
21
|
.replaceAll("\\", "\\textbackslash{}")
|
|
@@ -380,6 +386,8 @@ function planPaperMigration(
|
|
|
380
386
|
buildchainRoot = process.cwd(),
|
|
381
387
|
buildchainVersion = "",
|
|
382
388
|
buildchainSha = "",
|
|
389
|
+
stableBuildchainRoot = "",
|
|
390
|
+
alphaBuildchainRoot = "",
|
|
383
391
|
} = {},
|
|
384
392
|
) {
|
|
385
393
|
const resolvedCwd = path.resolve(cwd);
|
|
@@ -407,6 +415,8 @@ function planPaperMigration(
|
|
|
407
415
|
buildchainRoot,
|
|
408
416
|
buildchainVersion,
|
|
409
417
|
buildchainSha,
|
|
418
|
+
stableBuildchainRoot,
|
|
419
|
+
alphaBuildchainRoot,
|
|
410
420
|
}),
|
|
411
421
|
].map(([relativePath, content]) => {
|
|
412
422
|
const target = path.resolve(resolvedCwd, relativePath);
|
package/packages/core/paper.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
|
+
import { bindPaperV4Authority, paperV4Channels, selectPaperRuntime, paperRuntimeLockPath, paperProvisioningWorkflowErrors } from "./paper-runtime-channels.js";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import os from "node:os";
|
|
4
5
|
import path from "node:path";
|
|
@@ -40,6 +41,7 @@ import {
|
|
|
40
41
|
import {
|
|
41
42
|
createPaperScaffoldOperations,
|
|
42
43
|
managedPaperPackageJson,
|
|
44
|
+
paperDependencyIgnore,
|
|
43
45
|
scaffoldMainTex,
|
|
44
46
|
scaffoldMakefile,
|
|
45
47
|
scaffoldMap,
|
|
@@ -700,7 +702,7 @@ function scaffoldFiles({
|
|
|
700
702
|
["LICENSE", licenseText],
|
|
701
703
|
[
|
|
702
704
|
".gitignore",
|
|
703
|
-
"
|
|
705
|
+
"node_modules/\n_build/\n.buildchain/publication/\n.buildchain/release-state/\n.buildchain/release-evidence/\n.buildchain/paper/npm-bootstrap.json\n.buildchain/paper/npm-trust.json\n",
|
|
704
706
|
],
|
|
705
707
|
]);
|
|
706
708
|
}
|
|
@@ -710,6 +712,8 @@ function migrationFiles({
|
|
|
710
712
|
buildchainRoot,
|
|
711
713
|
buildchainVersion,
|
|
712
714
|
buildchainSha,
|
|
715
|
+
stableBuildchainRoot,
|
|
716
|
+
alphaBuildchainRoot,
|
|
713
717
|
}) {
|
|
714
718
|
const configResult = paperConfig(cwd);
|
|
715
719
|
if (configResult.error) {
|
|
@@ -817,7 +821,26 @@ function migrationFiles({
|
|
|
817
821
|
[PAPER_PATHS.provisioningAuthority, jsonText(provisioningAuthority)],
|
|
818
822
|
...agentEntry,
|
|
819
823
|
["package.json", jsonText(packageJson)],
|
|
824
|
+
[".gitignore", paperDependencyIgnore(fs.existsSync(path.join(cwd, ".gitignore")) ? fs.readFileSync(path.join(cwd, ".gitignore"), "utf8") : "")],
|
|
820
825
|
]);
|
|
826
|
+
if (runtimeIdentity.version.startsWith("4.")) {
|
|
827
|
+
const channelPlan = paperV4Channels({
|
|
828
|
+
cwd, buildchainRoot, buildchainVersion: runtimeIdentity.version,
|
|
829
|
+
buildchainSha: runtimeSha, contractWorld: runtimeContractWorld(buildchainRoot),
|
|
830
|
+
acceptedAt: contractLock.buildchain.acceptedAt,
|
|
831
|
+
stableBuildchainRoot, alphaBuildchainRoot,
|
|
832
|
+
});
|
|
833
|
+
for (const channel of Object.values(channelPlan.channels)) files.set(channel.lockPath, channel.content);
|
|
834
|
+
for (const workflowPath of [PAPER_PATHS.buildWorkflow, PAPER_PATHS.verifyWorkflow]) {
|
|
835
|
+
files.set(workflowPath, files.get(workflowPath).replaceAll(runtimeSha, "v4-alpha").replaceAll(".buildchain/contract-lock.json", ".buildchain/alpha-contract-lock.json"));
|
|
836
|
+
}
|
|
837
|
+
const releaseStart = releaseWorkflow.indexOf(" paper-release:\n");
|
|
838
|
+
const releaseJob = releaseWorkflow.slice(releaseStart);
|
|
839
|
+
const alphaJob = releaseJob.replace(" paper-release:\n", " paper-release-alpha:\n if: ${{ startsWith(github.ref_name, 'alpha/') }}\n").replaceAll(runtimeSha, "v4-alpha").replaceAll(".buildchain/contract-lock.json", ".buildchain/alpha-contract-lock.json");
|
|
840
|
+
const stableJob = releaseJob.replace(" paper-release:\n", " paper-release:\n if: ${{ startsWith(github.ref_name, 'release/') }}\n").replaceAll(runtimeSha, "v4");
|
|
841
|
+
files.set(PAPER_PATHS.releaseWorkflow, `${releaseWorkflow.slice(0, releaseStart)}${alphaJob}\n${stableJob}`);
|
|
842
|
+
files.set(PAPER_PATHS.provisioningAuthority, jsonText(bindPaperV4Authority(provisioningAuthority, channelPlan, files)));
|
|
843
|
+
}
|
|
821
844
|
return files;
|
|
822
845
|
}
|
|
823
846
|
|
|
@@ -1326,6 +1349,7 @@ function validatePaperProvisioningAuthority(cwd) {
|
|
|
1326
1349
|
}
|
|
1327
1350
|
const value = source.value;
|
|
1328
1351
|
const errors = [];
|
|
1352
|
+
const floating = /^v4(?:-alpha)?$/.test(value.runtime?.ref || "");
|
|
1329
1353
|
if (value.contract !== PAPER_PROVISIONING_CONTRACT) {
|
|
1330
1354
|
errors.push("paper provisioning authority contract mismatch");
|
|
1331
1355
|
}
|
|
@@ -1338,8 +1362,8 @@ function validatePaperProvisioningAuthority(cwd) {
|
|
|
1338
1362
|
}
|
|
1339
1363
|
if (
|
|
1340
1364
|
!GIT_SHA_PATTERN.test(String(value.runtime?.resolvedSha || "")) ||
|
|
1341
|
-
value.runtime?.ref !== value.runtime?.resolvedSha ||
|
|
1342
|
-
value.admission?.acceptedRef !== value.runtime?.
|
|
1365
|
+
(!floating && value.runtime?.ref !== value.runtime?.resolvedSha) ||
|
|
1366
|
+
value.admission?.acceptedRef !== value.runtime?.ref ||
|
|
1343
1367
|
value.admission?.acceptedSha !== value.runtime?.resolvedSha
|
|
1344
1368
|
) {
|
|
1345
1369
|
errors.push("paper runtime and admission are not bound to one exact SHA");
|
|
@@ -1397,49 +1421,7 @@ function validatePaperProvisioningAuthority(cwd) {
|
|
|
1397
1421
|
errors.push(`paper agent-entry source digest mismatch: ${entryPath}`);
|
|
1398
1422
|
}
|
|
1399
1423
|
}
|
|
1400
|
-
|
|
1401
|
-
value.workflows?.build,
|
|
1402
|
-
value.workflows?.verify,
|
|
1403
|
-
value.workflows?.release,
|
|
1404
|
-
]) {
|
|
1405
|
-
if (!workflow?.path || !workflow?.sourceDigest) {
|
|
1406
|
-
errors.push("paper workflow authority is incomplete");
|
|
1407
|
-
continue;
|
|
1408
|
-
}
|
|
1409
|
-
const absolute = path.resolve(cwd, workflow.path);
|
|
1410
|
-
if (
|
|
1411
|
-
!fs.existsSync(absolute) ||
|
|
1412
|
-
sha256File(absolute) !== workflow.sourceDigest
|
|
1413
|
-
) {
|
|
1414
|
-
errors.push(`paper workflow source digest mismatch: ${workflow.path}`);
|
|
1415
|
-
continue;
|
|
1416
|
-
}
|
|
1417
|
-
const text = fs.readFileSync(absolute, "utf8");
|
|
1418
|
-
const expectedUse = `${value.runtime.repository}/${workflow.reusablePath}@${value.runtime.resolvedSha}`;
|
|
1419
|
-
if (!text.includes(`uses: ${expectedUse}`)) {
|
|
1420
|
-
errors.push(
|
|
1421
|
-
`paper workflow reusable source is not exact: ${workflow.path}`,
|
|
1422
|
-
);
|
|
1423
|
-
}
|
|
1424
|
-
if (
|
|
1425
|
-
workflow.reusablePath !== ".github/workflows/check.yml" &&
|
|
1426
|
-
!text.includes(`buildchain-ref: ${value.runtime.resolvedSha}`)
|
|
1427
|
-
) {
|
|
1428
|
-
errors.push(
|
|
1429
|
-
`paper workflow runtime input is not exact: ${workflow.path}`,
|
|
1430
|
-
);
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
const lockPath = path.resolve(
|
|
1434
|
-
cwd,
|
|
1435
|
-
value.admission?.contractLockPath || PAPER_PATHS.contractLock,
|
|
1436
|
-
);
|
|
1437
|
-
if (
|
|
1438
|
-
!fs.existsSync(lockPath) ||
|
|
1439
|
-
sha256File(lockPath) !== value.admission?.contractLockDigest
|
|
1440
|
-
) {
|
|
1441
|
-
errors.push("paper contract lock bytes differ from provisioning authority");
|
|
1442
|
-
}
|
|
1424
|
+
errors.push(...paperProvisioningWorkflowErrors(cwd, value));
|
|
1443
1425
|
return {
|
|
1444
1426
|
exists: true,
|
|
1445
1427
|
valid: errors.length === 0,
|
|
@@ -2077,12 +2059,11 @@ export function collectPaperPreflight({
|
|
|
2077
2059
|
} catch (error) {
|
|
2078
2060
|
validationError = error.message;
|
|
2079
2061
|
}
|
|
2080
|
-
const runtime = runtimeFacts({
|
|
2062
|
+
const runtime = selectPaperRuntime(runtimeFacts({
|
|
2081
2063
|
buildchainRoot, buildchainVersion,
|
|
2082
2064
|
buildchainRef: provisioning.value?.runtime?.ref || buildchainRef, buildchainSha,
|
|
2083
|
-
});
|
|
2084
|
-
const
|
|
2085
|
-
const lockPath = path.resolve(resolvedCwd, PAPER_PATHS.contractLock);
|
|
2065
|
+
}), provisioning.value);
|
|
2066
|
+
const lockPath = paperRuntimeLockPath(resolvedCwd, runtime, provisioning.value);
|
|
2086
2067
|
let lockEvaluation = {
|
|
2087
2068
|
status: "missing-lock",
|
|
2088
2069
|
compatible: false,
|
|
@@ -2109,6 +2090,7 @@ export function collectPaperPreflight({
|
|
|
2109
2090
|
reasons: [error.message],
|
|
2110
2091
|
};
|
|
2111
2092
|
}
|
|
2093
|
+
const agentEntry = collectPaperAgentEntry({ cwd: resolvedCwd, buildchainSha: runtime.resolvedSha, mode: agentEntryMode, runtimeAdmission: { compatible: lockEvaluation.compatible, sha: runtime.resolvedSha, ref: runtime.ref } });
|
|
2112
2094
|
const source = {
|
|
2113
2095
|
repositoryRoot: gitValue(resolvedCwd, ["rev-parse", "--show-toplevel"]),
|
|
2114
2096
|
head: gitValue(resolvedCwd, ["rev-parse", "HEAD"]),
|
|
@@ -418,6 +418,8 @@ function runMigration(options) {
|
|
|
418
418
|
options.buildchainVersion,
|
|
419
419
|
),
|
|
420
420
|
buildchainSha: options.buildchainSha,
|
|
421
|
+
stableBuildchainRoot: readFlag(options.args, "stable-buildchain-root", ""),
|
|
422
|
+
alphaBuildchainRoot: readFlag(options.args, "alpha-buildchain-root", ""),
|
|
421
423
|
});
|
|
422
424
|
return options.args.some((entry) => ["--write", "--execute"].includes(entry))
|
|
423
425
|
? writePaperMigration(plan)
|