@kungfu-tech/buildchain 4.0.2-alpha.8 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +10 -1
- package/CONTRIBUTING.md +24 -0
- package/README.md +2 -2
- package/actions/promote-buildchain-ref/README.md +1 -1
- package/actions/v4-release-candidate-promote/README.md +28 -0
- package/architecture/agent-change-map.md +64 -7
- package/architecture/ci-lane-change-budget.json +366 -0
- package/architecture/internal-capabilities.json +53 -8
- package/architecture/maintainability-debt.json +147 -93
- package/architecture/maintainability-policy.json +32 -28
- package/architecture/release-tail-contract-inventory.json +6 -6
- package/architecture/v4-architecture-constitution.md +23 -20
- package/architecture/v4-cross-platform-adopter-qualification.json +1 -1
- package/architecture/v4-delivery-authority-parity.json +2 -2
- package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.json +1 -1
- package/architecture/v4-floating-consumer-policy.json +2 -2
- package/architecture/v4-next-development-parity.json +5 -5
- package/architecture/v4-partial-mutation-recovery-qualification.json +4 -1
- package/architecture/v4-platform-stage-checkpoints.json +1 -1
- package/architecture/v4-provider-operation-journal-contract.json +5 -2
- package/architecture/v4-publication-facade-patches.json +64 -0
- package/architecture/v4-release-activation-shadow-domain.json +5 -2
- package/architecture/v4-release-topology.json +74 -39
- package/architecture/v4-rust-wasm-production-authority.json +58 -0
- package/architecture/v4-stable-publication-fence.json +5 -2
- package/architecture/v4-stage-capsule-qualification.json +2 -2
- package/architecture/v4-tail-reseal-parity.json +8 -6
- package/architecture/v4-universal-workflow-bootstrap.json +171 -0
- package/architecture/v4-universal-workflow-fault-campaign.json +72 -0
- package/architecture/v4-universal-workflow-train-admission.json +40 -0
- package/architecture/workflow-taxonomy.json +1451 -0
- package/bin/buildchain.mjs +7 -7
- package/contracts/buildchain-v2-residuals-v1.json +57 -48
- package/contracts/dev-delivery-authority-v2.schema.json +16 -2
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
- package/contracts/v4-canonical-contracts-v1.schema.json +1 -0
- package/dist/site/buildchain-contract.json +93 -48
- package/dist/site/buildchain-site.json +222 -69
- package/dist/site/capability-registry.json +4 -4
- package/dist/site/controller-registry.json +50 -13
- package/dist/site/kfd-claims.json +2585 -1000
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +13 -13
- package/dist/site/node-api-registry.json +120 -115
- package/dist/site/page-registry.json +204 -51
- package/dist/site/public-surface-audit.json +3205 -1219
- package/dist/site/publication-authority-registry.json +1124 -362
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/schemas/dev-delivery-authority-v2.schema.json +16 -2
- package/dist/site/site-manifest.json +17 -17
- package/dist/site/workflow-registry.json +5061 -1659
- package/docs/MAP.md +1 -0
- package/docs/auditable-demo.md +1 -1
- package/docs/cli.md +21 -8
- package/docs/dev-delivery-warrant.md +1 -1
- package/docs/engineering-housekeeper.md +3 -3
- package/docs/kfd-support.md +26 -2
- package/docs/next-development-transition.md +2 -2
- package/docs/node-api-reference.md +190 -190
- package/docs/publication-authority.md +2 -2
- package/docs/release-candidate.md +3 -3
- package/docs/release-flow.md +67 -19
- package/docs/release-governance.md +8 -8
- package/docs/release-passport.md +27 -2
- package/docs/reusable-build-surface.md +16 -0
- package/docs/v4-adopter-delivery.md +2 -2
- package/docs/v4-production-release.md +71 -8
- package/docs/v4-rust-wasm-production-authority.md +58 -0
- package/docs/v4-stage-capsule.md +2 -2
- package/docs/v4-tail-reseal.md +1 -1
- package/docs/workflow-catalog.md +154 -0
- package/docs/workflow-path-migration.md +109 -0
- package/package.json +10 -6
- package/packages/core/artifact-verification-envelope.js +5 -5
- package/packages/core/buildchain-contract.js +1 -1
- package/packages/core/buildchain-kfd-claims.js +2 -2
- package/packages/core/buildchain-publication-authority.js +42 -26
- package/packages/core/buildchain-v4-domain.wasm +0 -0
- package/packages/core/ci-lane-change-budget.js +14 -1
- package/packages/core/controller-evidence.js +4 -1
- package/packages/core/dev-delivery-candidate-identity.js +40 -6
- package/packages/core/dev-delivery-warrant-state.js +3 -4
- package/packages/core/dev-delivery-warrant.js +12 -2
- package/packages/core/github-governance-authority.js +4 -4
- package/packages/core/kfd-adopter-manifest.js +46 -47
- package/packages/core/next-development-projection.js +2 -2
- package/packages/core/paper-agent-entry.js +30 -4
- package/packages/core/paper-fleet.js +1 -1
- package/packages/core/paper-runtime-channels.js +348 -0
- package/packages/core/paper-scaffold-content.js +10 -0
- package/packages/core/paper.js +37 -71
- package/packages/core/release-line-dry-run.js +1 -1
- package/packages/core/release-passport-contract.js +5 -5
- package/packages/core/release-passport.js +5 -4
- package/packages/core/release-tail-provider-plane.js +68 -1048
- package/packages/core/v4-canonical-contracts.js +10 -75
- package/packages/core/v4-domain-wasm-artifact.js +7 -0
- package/packages/core/v4-domain-wasm.js +216 -0
- package/packages/core/v4-floating-consumer-evidence.js +1 -1
- package/packages/core/v4-floating-consumer-policy.js +1 -1
- package/packages/core/v4-partial-mutation-recovery-qualification.js +9 -386
- package/packages/core/v4-product-publication.js +19 -349
- package/packages/core/v4-protected-publication-source.js +19 -21
- package/packages/core/v4-provider-operation-journal.js +18 -487
- package/packages/core/v4-provider-readback-idempotency.js +12 -97
- package/packages/core/v4-release-activation-shadow.js +13 -500
- package/packages/core/v4-release-invocation.js +28 -389
- package/packages/core/v4-stable-publication-fence.js +13 -342
- package/packages/core/v4-universal-workflow-bootstrap.js +586 -0
- package/scripts/binary-publication-evidence.mjs +135 -0
- package/scripts/build-contract-core.mjs +1 -14
- package/scripts/build-v4-domain-wasm.mjs +189 -0
- package/scripts/check-action-bundles.mjs +12 -2
- package/scripts/check-ci-lane-change-budget.mjs +4 -1
- package/scripts/check-core-mechanism-inventory.mjs +4 -3
- package/scripts/check-inventory.mjs +35 -44
- package/scripts/check-maintainability.mjs +16 -113
- package/scripts/check-v4-floating-consumer-policy-contract.mjs +2 -2
- package/scripts/check-v4-public-dogfood-contract.mjs +22 -9
- package/scripts/check-v4-release-topology.mjs +90 -33
- package/scripts/check-v4-runtime-semantic-closure.mjs +4 -1
- package/scripts/check-v4-universal-workflow-bootstrap.mjs +266 -0
- package/scripts/check-workflow-taxonomy.mjs +6 -0
- package/scripts/copy-v4-domain-wasm.mjs +14 -0
- package/scripts/dev-delivery-authority-command-adapters.mjs +1 -1
- package/scripts/dev-delivery-authority.mjs +8 -8
- package/scripts/dev-delivery-source-proof-reuse.mjs +2 -1
- package/scripts/dev-delivery-warrant-options.mjs +14 -9
- package/scripts/dev-delivery-warrant-store.mjs +73 -7
- package/scripts/dev-delivery-warrant-transition.mjs +109 -0
- package/scripts/dev-delivery-warrant.mjs +76 -99
- package/scripts/dev-pr-auto-merge.mjs +15 -15
- package/scripts/generate-channel-build-workflow.mjs +7 -2
- package/scripts/generate-channel-promotion-workflow.mjs +7 -5
- package/scripts/generate-site-bundle.mjs +11 -11
- package/scripts/generate-v4-universal-workflow-facades.mjs +458 -0
- package/scripts/generate-workflow-taxonomy.mjs +25 -0
- package/scripts/github-output.mjs +31 -0
- package/scripts/maintainability-metrics.mjs +16 -5
- package/scripts/maintainability-public-surface.mjs +115 -0
- package/scripts/next-development-review.mjs +307 -0
- package/scripts/paper-work-fleet-cli.mjs +2 -0
- package/scripts/release-asset-client.mjs +91 -0
- package/scripts/release-candidate-resolver.mjs +9 -9
- package/scripts/source-verification-evidence.mjs +344 -0
- package/scripts/stable-candidate-qualification.mjs +1 -1
- package/scripts/universal-facade-maintainability.mjs +82 -0
- package/scripts/v4-product-publication-intent.mjs +13 -6
- package/scripts/v4-publication-settlement.mjs +211 -0
- package/scripts/v4-universal-workflow-backflow.mjs +212 -0
- package/scripts/v4-universal-workflow-engine.mjs +618 -0
- package/scripts/v4-universal-workflow-self-dogfood.mjs +205 -0
- package/scripts/verify-version-state-delta.mjs +173 -0
- package/scripts/workflow-taxonomy.mjs +507 -0
- package/templates/native-dev-delivery.yml +4 -7
- package/templates/universal-buildchain-bootstrap-recovery.yml +282 -0
- package/templates/universal-buildchain-bootstrap.yml +28 -0
|
@@ -0,0 +1,348 @@
|
|
|
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
|
+
commandResult,
|
|
12
|
+
gitValue,
|
|
13
|
+
readJson,
|
|
14
|
+
sha256Text,
|
|
15
|
+
stableJson,
|
|
16
|
+
} from "./paper-repository.js";
|
|
17
|
+
|
|
18
|
+
export const PAPER_ALPHA_LOCK = ".buildchain/alpha-contract-lock.json";
|
|
19
|
+
|
|
20
|
+
export function resolvePaperNpmRuntimeSha(cwd, identity) {
|
|
21
|
+
if (!identity.version) return "";
|
|
22
|
+
const observed = commandResult(
|
|
23
|
+
"npm",
|
|
24
|
+
[
|
|
25
|
+
"view",
|
|
26
|
+
`${identity.name}@${identity.version}`,
|
|
27
|
+
"gitHead",
|
|
28
|
+
"--json",
|
|
29
|
+
"--registry=https://registry.npmjs.org",
|
|
30
|
+
],
|
|
31
|
+
{ cwd },
|
|
32
|
+
);
|
|
33
|
+
if (!observed.ok) return "";
|
|
34
|
+
let parsed;
|
|
35
|
+
try {
|
|
36
|
+
parsed = JSON.parse(observed.stdout || "null");
|
|
37
|
+
} catch {
|
|
38
|
+
return "";
|
|
39
|
+
}
|
|
40
|
+
const gitHead =
|
|
41
|
+
typeof parsed === "string" ? parsed : String(parsed?.gitHead || "");
|
|
42
|
+
return /^[0-9a-f]{40}$/i.test(gitHead)
|
|
43
|
+
? gitHead
|
|
44
|
+
: resolvePaperPublishedTagSha(cwd, identity);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function resolvePaperPublishedTagSha(cwd, identity) {
|
|
48
|
+
if (
|
|
49
|
+
identity.name !== "@kungfu-tech/buildchain" ||
|
|
50
|
+
!/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.test(
|
|
51
|
+
identity.version,
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
return "";
|
|
55
|
+
const ref = `refs/tags/v${identity.version}`;
|
|
56
|
+
const observed = commandResult(
|
|
57
|
+
"git",
|
|
58
|
+
[
|
|
59
|
+
"ls-remote",
|
|
60
|
+
"--exit-code",
|
|
61
|
+
"https://github.com/kungfu-systems/buildchain.git",
|
|
62
|
+
ref,
|
|
63
|
+
`${ref}^{}`,
|
|
64
|
+
],
|
|
65
|
+
{ cwd },
|
|
66
|
+
);
|
|
67
|
+
if (!observed.ok || !observed.stdout) return "";
|
|
68
|
+
const refs = new Map();
|
|
69
|
+
for (const line of observed.stdout.split(/\r?\n/)) {
|
|
70
|
+
const [sha, name, extra] = line.split(/\s+/);
|
|
71
|
+
if (
|
|
72
|
+
!/^[0-9a-f]{40}$/.test(sha) ||
|
|
73
|
+
![ref, `${ref}^{}`].includes(name) ||
|
|
74
|
+
extra ||
|
|
75
|
+
refs.has(name)
|
|
76
|
+
)
|
|
77
|
+
return "";
|
|
78
|
+
refs.set(name, sha);
|
|
79
|
+
}
|
|
80
|
+
if (!refs.has(ref)) return "";
|
|
81
|
+
return refs.get(`${ref}^{}`) || refs.get(ref);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function fileDigestMatches(cwd, relative, digest) {
|
|
85
|
+
if (!relative || !/^sha256:[0-9a-f]{64}$/.test(digest || "")) return false;
|
|
86
|
+
const absolute = path.resolve(cwd, relative);
|
|
87
|
+
return (
|
|
88
|
+
fs.existsSync(absolute) &&
|
|
89
|
+
sha256Text(fs.readFileSync(absolute, "utf8")) === digest
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function workflowErrors(cwd, authority, workflow) {
|
|
94
|
+
if (!workflow?.path || !workflow?.sourceDigest)
|
|
95
|
+
return ["paper workflow authority is incomplete"];
|
|
96
|
+
if (!fileDigestMatches(cwd, workflow.path, workflow.sourceDigest))
|
|
97
|
+
return [`paper workflow source digest mismatch: ${workflow.path}`];
|
|
98
|
+
const floating = /^v4(?:-alpha)?$/.test(authority.runtime?.ref || "");
|
|
99
|
+
const refs = floating
|
|
100
|
+
? workflow.reusablePath === ".github/workflows/paper-release-sealed.yml"
|
|
101
|
+
? ["v4-alpha", "v4"]
|
|
102
|
+
: ["v4-alpha"]
|
|
103
|
+
: [authority.runtime.resolvedSha];
|
|
104
|
+
const text = fs.readFileSync(path.resolve(cwd, workflow.path), "utf8");
|
|
105
|
+
const errors = [];
|
|
106
|
+
if (
|
|
107
|
+
refs.some(
|
|
108
|
+
(ref) =>
|
|
109
|
+
!text.includes(
|
|
110
|
+
`uses: ${authority.runtime.repository}/${workflow.reusablePath}@${ref}\n`,
|
|
111
|
+
),
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
errors.push(
|
|
115
|
+
`paper workflow reusable source is not exact: ${workflow.path}`,
|
|
116
|
+
);
|
|
117
|
+
if (
|
|
118
|
+
(floating || workflow.reusablePath !== ".github/workflows/check.yml") &&
|
|
119
|
+
refs.some((ref) => !text.includes(`buildchain-ref: ${ref}\n`))
|
|
120
|
+
)
|
|
121
|
+
errors.push(`paper workflow runtime input is not exact: ${workflow.path}`);
|
|
122
|
+
if (
|
|
123
|
+
floating &&
|
|
124
|
+
[
|
|
125
|
+
...text.matchAll(/uses:\s*kungfu-systems\/buildchain\/[^\s@]+@([^\s]+)/g),
|
|
126
|
+
].some((match) => !refs.includes(match[1]))
|
|
127
|
+
)
|
|
128
|
+
errors.push(
|
|
129
|
+
`paper workflow contains an unaccepted Buildchain selector: ${workflow.path}`,
|
|
130
|
+
);
|
|
131
|
+
return errors;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function channelLockMatches(cwd, authority, ref, expectedPath) {
|
|
135
|
+
const channel = authority.admission?.channels?.[ref];
|
|
136
|
+
const lock = readJson(path.resolve(cwd, expectedPath)).value;
|
|
137
|
+
const entry = readJson(
|
|
138
|
+
path.resolve(cwd, ".buildchain/paper/agent-entry.json"),
|
|
139
|
+
).value;
|
|
140
|
+
return (
|
|
141
|
+
channel?.ref === ref &&
|
|
142
|
+
channel?.lockPath === expectedPath &&
|
|
143
|
+
fileDigestMatches(cwd, expectedPath, channel?.lockDigest) &&
|
|
144
|
+
lock?.buildchain?.ref === ref &&
|
|
145
|
+
lock?.buildchain?.resolvedSha === channel?.resolvedSha &&
|
|
146
|
+
lock?.buildchain?.majorLine === "v4" &&
|
|
147
|
+
entry?.runtime?.channels?.[ref] === channel?.resolvedSha
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function paperProvisioningWorkflowErrors(cwd, authority) {
|
|
152
|
+
const errors = [
|
|
153
|
+
authority.workflows?.build,
|
|
154
|
+
authority.workflows?.verify,
|
|
155
|
+
authority.workflows?.release,
|
|
156
|
+
].flatMap((workflow) => workflowErrors(cwd, authority, workflow));
|
|
157
|
+
if (
|
|
158
|
+
!fileDigestMatches(
|
|
159
|
+
cwd,
|
|
160
|
+
authority.admission?.contractLockPath || ".buildchain/contract-lock.json",
|
|
161
|
+
authority.admission?.contractLockDigest,
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
errors.push("paper contract lock bytes differ from provisioning authority");
|
|
165
|
+
if (/^v4(?:-alpha)?$/.test(authority.runtime?.ref || "")) {
|
|
166
|
+
for (const [ref, lockPath] of [
|
|
167
|
+
["v4", ".buildchain/contract-lock.json"],
|
|
168
|
+
["v4-alpha", PAPER_ALPHA_LOCK],
|
|
169
|
+
]) {
|
|
170
|
+
if (!channelLockMatches(cwd, authority, ref, lockPath))
|
|
171
|
+
errors.push(
|
|
172
|
+
`paper ${ref} contract lock is not bound to provisioning authority`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return errors;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function paperRuntimeSourceMatches(runtime, sha, mode, admission) {
|
|
180
|
+
return (
|
|
181
|
+
runtime?.sourceSha === sha ||
|
|
182
|
+
(mode === "ci" &&
|
|
183
|
+
/^4\./.test(runtime?.version || "") &&
|
|
184
|
+
([runtime?.channels?.v4, runtime?.channels?.["v4-alpha"]].includes(sha) ||
|
|
185
|
+
(admission?.compatible === true &&
|
|
186
|
+
admission.sha === sha &&
|
|
187
|
+
/^v4(?:-alpha)?$/.test(admission.ref))))
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function selectPaperRuntime(runtime, authority) {
|
|
192
|
+
const channel = Object.values(authority?.admission?.channels || {}).find(
|
|
193
|
+
(entry) => entry.resolvedSha === runtime.resolvedSha,
|
|
194
|
+
);
|
|
195
|
+
const ref =
|
|
196
|
+
channel?.ref || (runtime.version.includes("-") ? "v4-alpha" : "v4");
|
|
197
|
+
return authority?.admission?.channels?.[ref] ? { ...runtime, ref } : runtime;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function paperRuntimeLockPath(cwd, runtime, authority) {
|
|
201
|
+
return path.resolve(
|
|
202
|
+
cwd,
|
|
203
|
+
authority?.admission?.channels?.[runtime.ref]?.lockPath ||
|
|
204
|
+
".buildchain/contract-lock.json",
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function channelWorld(root, ref, current) {
|
|
209
|
+
if (current) return current;
|
|
210
|
+
const sha = gitValue(root, [
|
|
211
|
+
"rev-parse",
|
|
212
|
+
"--verify",
|
|
213
|
+
`refs/tags/${ref}^{commit}`,
|
|
214
|
+
]);
|
|
215
|
+
const text =
|
|
216
|
+
sha &&
|
|
217
|
+
gitValue(root, ["show", `${sha}:dist/site/buildchain-contract.json`]);
|
|
218
|
+
if (!/^[0-9a-f]{40}$/.test(sha) || !text) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`Paper migration needs the fetched ${ref} tag or an explicit ${ref === "v4" ? "stable" : "alpha"} Buildchain root`,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
const published = JSON.parse(text);
|
|
224
|
+
const world = Array.isArray(published.compatibilityFacts)
|
|
225
|
+
? finalizeBuildchainContractWorld(published)
|
|
226
|
+
: {
|
|
227
|
+
...published,
|
|
228
|
+
contractDigest: `sha256:${sha256Json({ ...published, contractDigest: undefined, compatibilityDigest: undefined })}`,
|
|
229
|
+
compatibilityDigest: `sha256:${sha256Json({ schemaVersion: published.schemaVersion, contract: published.contract, majorLine: published.majorLine, surfaces: published.surfaces.map(({ id, kind, breakingDigest }) => ({ id, kind, breakingDigest })) })}`,
|
|
230
|
+
};
|
|
231
|
+
if (
|
|
232
|
+
published.contractDigest !== world.contractDigest ||
|
|
233
|
+
published.compatibilityDigest !== world.compatibilityDigest
|
|
234
|
+
) {
|
|
235
|
+
throw new Error(`Paper ${ref} contract digest mismatch`);
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
sha,
|
|
239
|
+
world,
|
|
240
|
+
acceptedAt: gitValue(root, ["show", "-s", "--format=%cI", sha]),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function explicitWorld(root, ref) {
|
|
245
|
+
const sha = gitValue(root, ["rev-parse", "HEAD"]);
|
|
246
|
+
if (!/^[0-9a-f]{40}$/.test(sha))
|
|
247
|
+
throw new Error("Paper channel root must have exact Git provenance");
|
|
248
|
+
if (gitValue(root, ["status", "--porcelain", "--untracked-files=no"]))
|
|
249
|
+
throw new Error("Paper channel root must have committed runtime bytes");
|
|
250
|
+
const pkg = readJson(path.join(root, "package.json")).value;
|
|
251
|
+
if (
|
|
252
|
+
pkg?.name !== "@kungfu-tech/buildchain" ||
|
|
253
|
+
!/^4\./.test(pkg?.version || "") ||
|
|
254
|
+
pkg.version.includes("-") !== (ref === "v4-alpha")
|
|
255
|
+
)
|
|
256
|
+
throw new Error(`Paper channel root does not belong to ${ref}`);
|
|
257
|
+
const contractPath = path.join(root, "dist/site/buildchain-contract.json");
|
|
258
|
+
return {
|
|
259
|
+
sha,
|
|
260
|
+
world: fs.existsSync(contractPath)
|
|
261
|
+
? readBuildchainContractWorld(contractPath)
|
|
262
|
+
: createBuildchainContractWorld({ root }),
|
|
263
|
+
acceptedAt: gitValue(root, ["show", "-s", "--format=%cI", sha]),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function paperV4Channels({
|
|
268
|
+
cwd,
|
|
269
|
+
buildchainRoot,
|
|
270
|
+
buildchainVersion,
|
|
271
|
+
buildchainSha,
|
|
272
|
+
contractWorld,
|
|
273
|
+
acceptedAt,
|
|
274
|
+
stableBuildchainRoot = "",
|
|
275
|
+
alphaBuildchainRoot = "",
|
|
276
|
+
}) {
|
|
277
|
+
const selectedRef = buildchainVersion.includes("-") ? "v4-alpha" : "v4";
|
|
278
|
+
const current = {
|
|
279
|
+
sha: buildchainSha,
|
|
280
|
+
world: contractWorld,
|
|
281
|
+
acceptedAt,
|
|
282
|
+
};
|
|
283
|
+
const channels = {};
|
|
284
|
+
for (const [ref, lockPath, explicitRoot] of [
|
|
285
|
+
["v4", ".buildchain/contract-lock.json", stableBuildchainRoot],
|
|
286
|
+
["v4-alpha", PAPER_ALPHA_LOCK, alphaBuildchainRoot],
|
|
287
|
+
]) {
|
|
288
|
+
const resolved = explicitRoot
|
|
289
|
+
? explicitWorld(explicitRoot, ref)
|
|
290
|
+
: channelWorld(buildchainRoot, ref, ref === selectedRef ? current : null);
|
|
291
|
+
if (resolved.world.majorLine !== "v4")
|
|
292
|
+
throw new Error(`Paper ${ref} contract must belong to v4`);
|
|
293
|
+
const existing = readJson(path.join(cwd, lockPath)).value;
|
|
294
|
+
const lock = createBuildchainContractLock({
|
|
295
|
+
buildchainRef: ref,
|
|
296
|
+
resolvedSha: resolved.sha,
|
|
297
|
+
contractWorld: resolved.world,
|
|
298
|
+
acceptedAt:
|
|
299
|
+
existing?.buildchain?.resolvedSha === resolved.sha
|
|
300
|
+
? existing.buildchain.acceptedAt
|
|
301
|
+
: new Date(resolved.acceptedAt).toISOString(),
|
|
302
|
+
});
|
|
303
|
+
const content = `${JSON.stringify(lock, null, 2)}\n`;
|
|
304
|
+
channels[ref] = {
|
|
305
|
+
ref,
|
|
306
|
+
resolvedSha: resolved.sha,
|
|
307
|
+
lockPath,
|
|
308
|
+
lockDigest: sha256Text(content),
|
|
309
|
+
content,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
return { selectedRef, channels };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export function bindPaperV4Authority(authority, channelPlan, files) {
|
|
316
|
+
const { selectedRef, channels } = channelPlan;
|
|
317
|
+
const entryPath = authority.agentEntry.policyPath;
|
|
318
|
+
const entry = JSON.parse(files.get(entryPath));
|
|
319
|
+
entry.runtime.channels = Object.fromEntries(
|
|
320
|
+
Object.entries(channels).map(([ref, channel]) => [
|
|
321
|
+
ref,
|
|
322
|
+
channel.resolvedSha,
|
|
323
|
+
]),
|
|
324
|
+
);
|
|
325
|
+
const { entryDigest: _entryDigest, ...entryPayload } = entry;
|
|
326
|
+
entry.entryDigest = sha256Text(stableJson(entryPayload));
|
|
327
|
+
files.set(entryPath, `${JSON.stringify(entry, null, 2)}\n`);
|
|
328
|
+
authority.agentEntry.policyDigest = sha256Text(files.get(entryPath));
|
|
329
|
+
authority.runtime.ref = selectedRef;
|
|
330
|
+
authority.admission.acceptedRef = selectedRef;
|
|
331
|
+
authority.admission.contractLockPath = channels[selectedRef].lockPath;
|
|
332
|
+
authority.admission.contractLockDigest = channels[selectedRef].lockDigest;
|
|
333
|
+
authority.admission.channels = Object.fromEntries(
|
|
334
|
+
Object.entries(channels).map(([ref, { content: _content, ...channel }]) => [
|
|
335
|
+
ref,
|
|
336
|
+
channel,
|
|
337
|
+
]),
|
|
338
|
+
);
|
|
339
|
+
for (const [role, workflow] of Object.entries(authority.workflows)) {
|
|
340
|
+
workflow.reusableRef = role === "release" ? "v4" : "v4-alpha";
|
|
341
|
+
workflow.reusableRefs =
|
|
342
|
+
role === "release" ? ["v4", "v4-alpha"] : ["v4-alpha"];
|
|
343
|
+
workflow.sourceDigest = sha256Text(files.get(workflow.path));
|
|
344
|
+
}
|
|
345
|
+
const { authorityDigest: _authorityDigest, ...payload } = authority;
|
|
346
|
+
authority.authorityDigest = sha256Text(stableJson(payload));
|
|
347
|
+
return authority;
|
|
348
|
+
}
|
|
@@ -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, resolvePaperNpmRuntimeSha } 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,
|
|
@@ -255,23 +257,7 @@ export function resolvePaperRuntimeGitSha(
|
|
|
255
257
|
const value = resolvePaperBuildchainSha(buildchainRoot);
|
|
256
258
|
if (GIT_SHA_PATTERN.test(value)) return value;
|
|
257
259
|
const identity = buildchainPackageIdentity(buildchainRoot, buildchainVersion);
|
|
258
|
-
|
|
259
|
-
const observed = commandResult(
|
|
260
|
-
"npm",
|
|
261
|
-
[
|
|
262
|
-
"view",
|
|
263
|
-
`${identity.name}@${identity.version}`,
|
|
264
|
-
"gitHead",
|
|
265
|
-
"--json",
|
|
266
|
-
`--registry=${NPM_REGISTRY}`,
|
|
267
|
-
],
|
|
268
|
-
{ cwd: buildchainRoot },
|
|
269
|
-
);
|
|
270
|
-
if (!observed.ok) return "";
|
|
271
|
-
const parsed = safeParseJson(observed.stdout);
|
|
272
|
-
const gitHead =
|
|
273
|
-
typeof parsed === "string" ? parsed : String(parsed?.gitHead || "");
|
|
274
|
-
return GIT_SHA_PATTERN.test(gitHead) ? gitHead : "";
|
|
260
|
+
return resolvePaperNpmRuntimeSha(buildchainRoot, identity);
|
|
275
261
|
}
|
|
276
262
|
|
|
277
263
|
function runtimeAcceptedAt(buildchainRoot, sha, buildchainVersion = "") {
|
|
@@ -424,7 +410,7 @@ jobs:
|
|
|
424
410
|
`;
|
|
425
411
|
}
|
|
426
412
|
|
|
427
|
-
function scaffoldVerifyWorkflow(buildchainSha) {
|
|
413
|
+
function scaffoldVerifyWorkflow(buildchainSha, buildchainVersion = "") {
|
|
428
414
|
return `${nextDevelopmentWorkflowHeader()}name: Verify
|
|
429
415
|
|
|
430
416
|
on:
|
|
@@ -437,7 +423,7 @@ on:
|
|
|
437
423
|
workflow_dispatch:
|
|
438
424
|
|
|
439
425
|
permissions:
|
|
440
|
-
contents: read
|
|
426
|
+
${buildchainVersion.startsWith("4.") ? " actions: read\n contents: read\n pull-requests: read" : " contents: read"}
|
|
441
427
|
|
|
442
428
|
jobs:
|
|
443
429
|
check:
|
|
@@ -635,7 +621,7 @@ function scaffoldFiles({
|
|
|
635
621
|
artifactPaths: "_build/main.pdf",
|
|
636
622
|
releasePassportProductName: title,
|
|
637
623
|
});
|
|
638
|
-
const verifyWorkflow = scaffoldVerifyWorkflow(buildchainSha);
|
|
624
|
+
const verifyWorkflow = scaffoldVerifyWorkflow(buildchainSha, buildchainVersion);
|
|
639
625
|
const agentEntry = paperAgentEntryFiles({
|
|
640
626
|
cwd,
|
|
641
627
|
buildchainVersion,
|
|
@@ -700,7 +686,7 @@ function scaffoldFiles({
|
|
|
700
686
|
["LICENSE", licenseText],
|
|
701
687
|
[
|
|
702
688
|
".gitignore",
|
|
703
|
-
"
|
|
689
|
+
"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
690
|
],
|
|
705
691
|
]);
|
|
706
692
|
}
|
|
@@ -710,6 +696,8 @@ function migrationFiles({
|
|
|
710
696
|
buildchainRoot,
|
|
711
697
|
buildchainVersion,
|
|
712
698
|
buildchainSha,
|
|
699
|
+
stableBuildchainRoot,
|
|
700
|
+
alphaBuildchainRoot,
|
|
713
701
|
}) {
|
|
714
702
|
const configResult = paperConfig(cwd);
|
|
715
703
|
if (configResult.error) {
|
|
@@ -768,7 +756,7 @@ function migrationFiles({
|
|
|
768
756
|
artifactPaths: config.publication.artifactPaths.join(","),
|
|
769
757
|
releasePassportProductName: config.publication.title,
|
|
770
758
|
});
|
|
771
|
-
const verifyWorkflow = scaffoldVerifyWorkflow(runtimeSha);
|
|
759
|
+
const verifyWorkflow = scaffoldVerifyWorkflow(runtimeSha, runtimeIdentity.version);
|
|
772
760
|
const agentEntry = paperAgentEntryFiles({
|
|
773
761
|
cwd,
|
|
774
762
|
buildchainVersion: runtimeIdentity.version,
|
|
@@ -817,7 +805,26 @@ function migrationFiles({
|
|
|
817
805
|
[PAPER_PATHS.provisioningAuthority, jsonText(provisioningAuthority)],
|
|
818
806
|
...agentEntry,
|
|
819
807
|
["package.json", jsonText(packageJson)],
|
|
808
|
+
[".gitignore", paperDependencyIgnore(fs.existsSync(path.join(cwd, ".gitignore")) ? fs.readFileSync(path.join(cwd, ".gitignore"), "utf8") : "")],
|
|
820
809
|
]);
|
|
810
|
+
if (runtimeIdentity.version.startsWith("4.")) {
|
|
811
|
+
const channelPlan = paperV4Channels({
|
|
812
|
+
cwd, buildchainRoot, buildchainVersion: runtimeIdentity.version,
|
|
813
|
+
buildchainSha: runtimeSha, contractWorld: runtimeContractWorld(buildchainRoot),
|
|
814
|
+
acceptedAt: contractLock.buildchain.acceptedAt,
|
|
815
|
+
stableBuildchainRoot, alphaBuildchainRoot,
|
|
816
|
+
});
|
|
817
|
+
for (const channel of Object.values(channelPlan.channels)) files.set(channel.lockPath, channel.content);
|
|
818
|
+
for (const workflowPath of [PAPER_PATHS.buildWorkflow, PAPER_PATHS.verifyWorkflow]) {
|
|
819
|
+
files.set(workflowPath, files.get(workflowPath).replaceAll(runtimeSha, "v4-alpha").replaceAll(".buildchain/contract-lock.json", ".buildchain/alpha-contract-lock.json"));
|
|
820
|
+
}
|
|
821
|
+
const releaseStart = releaseWorkflow.indexOf(" paper-release:\n");
|
|
822
|
+
const releaseJob = releaseWorkflow.slice(releaseStart);
|
|
823
|
+
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");
|
|
824
|
+
const stableJob = releaseJob.replace(" paper-release:\n", " paper-release:\n if: ${{ startsWith(github.ref_name, 'release/') }}\n").replaceAll(runtimeSha, "v4");
|
|
825
|
+
files.set(PAPER_PATHS.releaseWorkflow, `${releaseWorkflow.slice(0, releaseStart)}${alphaJob}\n${stableJob}`);
|
|
826
|
+
files.set(PAPER_PATHS.provisioningAuthority, jsonText(bindPaperV4Authority(provisioningAuthority, channelPlan, files)));
|
|
827
|
+
}
|
|
821
828
|
return files;
|
|
822
829
|
}
|
|
823
830
|
|
|
@@ -1326,6 +1333,7 @@ function validatePaperProvisioningAuthority(cwd) {
|
|
|
1326
1333
|
}
|
|
1327
1334
|
const value = source.value;
|
|
1328
1335
|
const errors = [];
|
|
1336
|
+
const floating = /^v4(?:-alpha)?$/.test(value.runtime?.ref || "");
|
|
1329
1337
|
if (value.contract !== PAPER_PROVISIONING_CONTRACT) {
|
|
1330
1338
|
errors.push("paper provisioning authority contract mismatch");
|
|
1331
1339
|
}
|
|
@@ -1338,8 +1346,8 @@ function validatePaperProvisioningAuthority(cwd) {
|
|
|
1338
1346
|
}
|
|
1339
1347
|
if (
|
|
1340
1348
|
!GIT_SHA_PATTERN.test(String(value.runtime?.resolvedSha || "")) ||
|
|
1341
|
-
value.runtime?.ref !== value.runtime?.resolvedSha ||
|
|
1342
|
-
value.admission?.acceptedRef !== value.runtime?.
|
|
1349
|
+
(!floating && value.runtime?.ref !== value.runtime?.resolvedSha) ||
|
|
1350
|
+
value.admission?.acceptedRef !== value.runtime?.ref ||
|
|
1343
1351
|
value.admission?.acceptedSha !== value.runtime?.resolvedSha
|
|
1344
1352
|
) {
|
|
1345
1353
|
errors.push("paper runtime and admission are not bound to one exact SHA");
|
|
@@ -1397,49 +1405,7 @@ function validatePaperProvisioningAuthority(cwd) {
|
|
|
1397
1405
|
errors.push(`paper agent-entry source digest mismatch: ${entryPath}`);
|
|
1398
1406
|
}
|
|
1399
1407
|
}
|
|
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
|
-
}
|
|
1408
|
+
errors.push(...paperProvisioningWorkflowErrors(cwd, value));
|
|
1443
1409
|
return {
|
|
1444
1410
|
exists: true,
|
|
1445
1411
|
valid: errors.length === 0,
|
|
@@ -2077,12 +2043,11 @@ export function collectPaperPreflight({
|
|
|
2077
2043
|
} catch (error) {
|
|
2078
2044
|
validationError = error.message;
|
|
2079
2045
|
}
|
|
2080
|
-
const runtime = runtimeFacts({
|
|
2046
|
+
const runtime = selectPaperRuntime(runtimeFacts({
|
|
2081
2047
|
buildchainRoot, buildchainVersion,
|
|
2082
2048
|
buildchainRef: provisioning.value?.runtime?.ref || buildchainRef, buildchainSha,
|
|
2083
|
-
});
|
|
2084
|
-
const
|
|
2085
|
-
const lockPath = path.resolve(resolvedCwd, PAPER_PATHS.contractLock);
|
|
2049
|
+
}), provisioning.value);
|
|
2050
|
+
const lockPath = paperRuntimeLockPath(resolvedCwd, runtime, provisioning.value);
|
|
2086
2051
|
let lockEvaluation = {
|
|
2087
2052
|
status: "missing-lock",
|
|
2088
2053
|
compatible: false,
|
|
@@ -2109,6 +2074,7 @@ export function collectPaperPreflight({
|
|
|
2109
2074
|
reasons: [error.message],
|
|
2110
2075
|
};
|
|
2111
2076
|
}
|
|
2077
|
+
const agentEntry = collectPaperAgentEntry({ cwd: resolvedCwd, buildchainSha: runtime.resolvedSha, mode: agentEntryMode, runtimeAdmission: { compatible: lockEvaluation.compatible, sha: runtime.resolvedSha, ref: runtime.ref } });
|
|
2112
2078
|
const source = {
|
|
2113
2079
|
repositoryRoot: gitValue(resolvedCwd, ["rev-parse", "--show-toplevel"]),
|
|
2114
2080
|
head: gitValue(resolvedCwd, ["rev-parse", "HEAD"]),
|
|
@@ -201,7 +201,7 @@ function explainReleaseLineDryRun({
|
|
|
201
201
|
);
|
|
202
202
|
plan.branchUpdates.push(
|
|
203
203
|
{ ref: targetRef, action: "would move to alpha version-state commit" },
|
|
204
|
-
{ ref: `dev/v${rule.major}/v${rule.major}.${rule.minor}`, action: "would align dev with the published alpha state" },
|
|
204
|
+
{ ref: `dev/v${rule.major}/v${rule.major}.${rule.minor}`, action: rule.major === 4 ? "would prepare the next alpha version on dev through a separate protected PR after publication" : "would align dev with the published alpha state" },
|
|
205
205
|
);
|
|
206
206
|
plan.versionState.targetVersions = [exactAlpha.replace(/^next v?/, "").replace(/^v/, "")];
|
|
207
207
|
plan.notes.push("Alpha promotion opens or advances the test channel; it does not move production refs.");
|
|
@@ -405,7 +405,7 @@ export function prepareReleasePassportKfdSections({ kfd1, kfd2Claims, kfd3, kfdA
|
|
|
405
405
|
};
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
-
export function collectKfdAdopterReleaseEvidence({ manifest, gateResults = [], comparisonMatrix, sourceSha = "", checkedAt } = {}) {
|
|
408
|
+
export function collectKfdAdopterReleaseEvidence({ manifest, gateResults = [], comparisonMatrix, expectedAdopterId = "kungfu-systems/buildchain", expectedSourceRepository = "", sourceSha = "", checkedAt } = {}) {
|
|
409
409
|
if (!manifest) {
|
|
410
410
|
if (comparisonMatrix || gateResults.length > 0) {
|
|
411
411
|
throw new Error("KFD support and product-gate inputs require --kfd-adopter-manifest-json; --kfd-support-matrix-json is comparison-only");
|
|
@@ -414,7 +414,7 @@ export function collectKfdAdopterReleaseEvidence({ manifest, gateResults = [], c
|
|
|
414
414
|
}
|
|
415
415
|
const manifestGate = createKfdAdopterManifestGate({
|
|
416
416
|
manifest, packageArtifactRoot: installedKfdPackageArtifactRoot(), gateResults,
|
|
417
|
-
expectedSourceSha: sourceSha, checkedAt,
|
|
417
|
+
expectedAdopterId, expectedSourceRepository, expectedSourceSha: sourceSha, checkedAt,
|
|
418
418
|
});
|
|
419
419
|
const legacyProjection = createKfdLegacySupportMatrixProjection({ manifest, manifestGate });
|
|
420
420
|
if (comparisonMatrix) {
|
|
@@ -425,18 +425,18 @@ export function collectKfdAdopterReleaseEvidence({ manifest, gateResults = [], c
|
|
|
425
425
|
}
|
|
426
426
|
return {
|
|
427
427
|
manifest, manifestGate, legacyProjection,
|
|
428
|
-
binding: createKfdAdopterReleaseBinding({ manifest, manifestGate, legacyProjection, expectedSourceSha: sourceSha }),
|
|
428
|
+
binding: createKfdAdopterReleaseBinding({ manifest, manifestGate, legacyProjection, expectedAdopterId, expectedSourceRepository, expectedSourceSha: sourceSha }),
|
|
429
429
|
};
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
-
export function validateKfdAdopterReleaseEvidence({ binding, artifactBinding, manifest, manifestGate, legacyProjection, passportLegacyProjection, expectedSourceSha = "" } = {}) {
|
|
432
|
+
export function validateKfdAdopterReleaseEvidence({ binding, artifactBinding, manifest, manifestGate, legacyProjection, passportLegacyProjection, expectedAdopterId = "kungfu-systems/buildchain", expectedSourceRepository = "", expectedSourceSha = "" } = {}) {
|
|
433
433
|
if (!binding) {
|
|
434
434
|
return passportLegacyProjection
|
|
435
435
|
? [{ code: "kfdSupport.authority", message: "legacy KFD support projection requires the standard adopter manifest binding" }]
|
|
436
436
|
: [];
|
|
437
437
|
}
|
|
438
438
|
const issues = validateKfdAdopterReleaseBinding(binding, {
|
|
439
|
-
manifest, manifestGate, legacyProjection, expectedSourceSha,
|
|
439
|
+
manifest, manifestGate, legacyProjection, expectedAdopterId, expectedSourceRepository, expectedSourceSha,
|
|
440
440
|
}).issues.map((entry) => ({ code: `kfdAdopter.${entry.path || entry.code}`, message: entry.message, details: entry }));
|
|
441
441
|
if (!artifactBinding || passportStableJson(artifactBinding) !== passportStableJson(binding)) {
|
|
442
442
|
issues.push({ code: "kfdAdopter.artifactEvidence", message: "release passport and artifact evidence must bind the same exact KFD adopter closure" });
|
|
@@ -1431,7 +1431,7 @@ export function createReleasePassport(options = {}) {
|
|
|
1431
1431
|
};
|
|
1432
1432
|
}
|
|
1433
1433
|
|
|
1434
|
-
function collectKfdAdopterReleaseInputs({ cwd, manifestJson, supportMatrixJson, productGateJsons, sourceSha, checkedAt }) {
|
|
1434
|
+
function collectKfdAdopterReleaseInputs({ cwd, manifestJson, supportMatrixJson, productGateJsons, repository, sourceSha, checkedAt }) {
|
|
1435
1435
|
const manifestMeta = parseJsonInputWithMeta(manifestJson, undefined, { cwd, label: "kfdAdopterManifestJson" });
|
|
1436
1436
|
const supportMatrixMeta = parseJsonInputWithMeta(supportMatrixJson, undefined, { cwd, label: "kfdSupportMatrixJson" });
|
|
1437
1437
|
const productGateMetas = (productGateJsons || [])
|
|
@@ -1442,7 +1442,7 @@ function collectKfdAdopterReleaseInputs({ cwd, manifestJson, supportMatrixJson,
|
|
|
1442
1442
|
manifest: manifestMeta.value,
|
|
1443
1443
|
gateResults: productGateMetas.map((meta) => meta.value),
|
|
1444
1444
|
comparisonMatrix: supportMatrixMeta.value,
|
|
1445
|
-
sourceSha,
|
|
1445
|
+
expectedAdopterId: repository || "kungfu-systems/buildchain", expectedSourceRepository: repository, sourceSha,
|
|
1446
1446
|
checkedAt,
|
|
1447
1447
|
});
|
|
1448
1448
|
}
|
|
@@ -1620,7 +1620,7 @@ export function collectGitHubReleasePassport({
|
|
|
1620
1620
|
const productMechanism = defaultProductMechanism({ repository, productName });
|
|
1621
1621
|
const kfdAdopterEvidence = collectKfdAdopterReleaseInputs({
|
|
1622
1622
|
cwd, manifestJson: kfdAdopterManifestJson, supportMatrixJson: kfdSupportMatrixJson,
|
|
1623
|
-
productGateJsons: kfdProductGateJsons, sourceSha, checkedAt: resolvedCheckedAt,
|
|
1623
|
+
productGateJsons: kfdProductGateJsons, repository, sourceSha, checkedAt: resolvedCheckedAt,
|
|
1624
1624
|
});
|
|
1625
1625
|
const {
|
|
1626
1626
|
manifest: kfdAdopterManifest,
|
|
@@ -2060,7 +2060,8 @@ function validateReleaseEvidenceContracts({
|
|
|
2060
2060
|
manifestGate: kfdAdopterManifestGate,
|
|
2061
2061
|
legacyProjection: kfdSupportEvidence,
|
|
2062
2062
|
passportLegacyProjection: passport?.kfdSupport,
|
|
2063
|
-
|
|
2063
|
+
expectedAdopterId: optionalString(passport?.product?.repository) || "kungfu-systems/buildchain",
|
|
2064
|
+
expectedSourceRepository: optionalString(passport?.product?.repository), expectedSourceSha: optionalString(passport?.release?.sourceSha),
|
|
2064
2065
|
})) {
|
|
2065
2066
|
issues.push(issue("error", entry.code, entry.message, entry.details));
|
|
2066
2067
|
}
|