@kungfu-tech/buildchain 4.0.1-alpha.5 → 4.0.1-alpha.8
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/actions/release-tail/README.md +8 -0
- package/architecture/ci-lane-change-budget.json +187 -0
- package/architecture/internal-capabilities.json +65 -5
- package/architecture/maintainability-policy.json +27 -21
- package/architecture/release-tail-contract-inventory.json +5 -2
- package/architecture/v4-adopter-delivery-parity.json +32 -5
- package/architecture/v4-floating-consumer-policy.json +6 -0
- package/architecture/v4-publication-rehearsal-parity.json +84 -0
- package/bin/buildchain.mjs +7 -1
- package/bin/internal/adopter-delivery-cli.mjs +70 -0
- package/bin/internal/command-registry.mjs +1 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
- package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
- package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +4 -2
- package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/agent-index.json +3 -0
- package/dist/site/artifact-schemas.json +6 -0
- package/dist/site/buildchain-contract.json +14 -14
- package/dist/site/buildchain-site.json +231 -24
- package/dist/site/capability-registry.json +11 -10
- package/dist/site/cli-registry.json +53 -1
- package/dist/site/kfd-claims.json +279 -14
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +20 -5
- package/dist/site/node-api-registry.json +11569 -9299
- package/dist/site/page-registry.json +214 -15
- package/dist/site/public-surface-audit.json +206 -11
- package/dist/site/publication-authority-registry.json +39 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +15 -0
- package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
- package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
- package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/site-manifest.json +17 -9
- package/dist/site/workflow-registry.json +110 -8
- package/docs/MAP.md +3 -1
- package/docs/cli-reference.md +68 -0
- package/docs/cli.md +8 -0
- package/docs/dev-delivery-warrant.md +22 -1
- package/docs/node-api-reference.md +332 -172
- package/docs/release-tail-provider-plane.md +30 -0
- package/docs/v4-adopter-delivery.md +133 -0
- package/docs/v4-publication-rehearsal.md +130 -0
- package/package.json +18 -3
- package/packages/core/adopter-delivery-passport.js +259 -0
- package/packages/core/adopter-delivery-vectors.js +158 -0
- package/packages/core/adopter-delivery-vectors.json +77 -0
- package/packages/core/buildchain-agent-manuals.js +1 -0
- package/packages/core/buildchain-config.js +84 -0
- package/packages/core/buildchain-delivery-bootstrap.js +240 -0
- package/packages/core/buildchain-delivery-infrastructure.js +164 -0
- package/packages/core/buildchain-delivery-self-dogfood.js +395 -0
- package/packages/core/buildchain-publication-authority.js +1 -0
- package/packages/core/ci-lane-change-budget.js +247 -0
- package/packages/core/dev-delivery-native-proof.js +43 -27
- package/packages/core/dev-delivery-warrant-state.js +31 -8
- package/packages/core/dev-delivery-warrant.js +8 -3
- package/packages/core/github-governance-authority.js +1 -0
- package/packages/core/index.js +5 -0
- package/packages/core/legacy-kfd-adopter-driver.js +167 -0
- package/packages/core/published-delivery-authority.js +266 -0
- package/packages/core/release-candidate-recovery.js +66 -11
- package/packages/core/release-candidate.js +2 -2
- package/packages/core/release-passport.js +36 -9
- package/packages/core/v4-adopter-delivery-parity.js +3 -3
- package/packages/core/v4-adopter-delivery.js +359 -0
- package/packages/core/v4-delivery-warrant-read-candidate.js +10 -3
- package/packages/core/v4-floating-consumer-evidence.js +18 -4
- package/packages/core/v4-floating-consumer-policy.js +138 -35
- package/packages/core/v4-floating-consumer-release-passport.js +30 -3
- package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
- package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
- package/packages/core/v4-publication-rehearsal.js +481 -0
- package/packages/core/workflow-yaml-contract.js +24 -2
- package/scripts/buildchain-cli-help.mjs +10 -0
- package/scripts/check-ci-lane-change-budget.mjs +100 -0
- package/scripts/check-inventory.mjs +3 -3
- package/scripts/check-v4-floating-consumer-policy-contract.mjs +23 -4
- package/scripts/dev-delivery-source-proof-replay.mjs +153 -0
- package/scripts/dev-delivery-source-proof-reuse.mjs +13 -97
- package/scripts/dev-delivery-warrant-store.mjs +10 -6
- package/scripts/dev-delivery-warrant.mjs +5 -1
- package/scripts/dev-pr-delivery-warrant.mjs +1 -0
- package/scripts/dev-qualification-patrol.mjs +9 -9
- package/scripts/generate-channel-promotion-workflow.mjs +2 -2
- package/scripts/generate-release-candidate-passport.mjs +152 -0
- package/scripts/generate-site-bundle.mjs +30 -0
- package/scripts/promotion-channel-router.mjs +4 -4
- package/scripts/release-candidate-anchor-provenance.mjs +99 -0
- package/scripts/release-tail.mjs +51 -3
- package/scripts/resume-from-candidate-run.mjs +556 -29
- package/scripts/site-capability-metadata.mjs +17 -0
- package/scripts/v4-consumer-policy.mjs +4 -0
- package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import {
|
|
2
|
+
adopterDeliveryGateDigest,
|
|
3
|
+
createAdopterDeliveryGate,
|
|
4
|
+
createGitCommitArtifactProfile,
|
|
5
|
+
createPackageArtifactProfile,
|
|
6
|
+
defineAdopterProtocolDriver,
|
|
7
|
+
ADOPTER_PROTOCOL_DRIVER_INTERFACE,
|
|
8
|
+
} from "./adopter-delivery-gate.js";
|
|
9
|
+
import { qualifyBuildchainDeliveryInfrastructureBootstrap } from "./buildchain-delivery-bootstrap.js";
|
|
10
|
+
import { createKfdAdopterCategoryProtocolDriver } from "./kfd-adopter-category-driver.js";
|
|
11
|
+
import { createLegacyKfdAdopterProtocolDriver } from "./legacy-kfd-adopter-driver.js";
|
|
12
|
+
import { loadPublishedBuildchainDeliveryAuthority } from "./published-delivery-authority.js";
|
|
13
|
+
|
|
14
|
+
export const V4_ADOPTER_DELIVERY_CONTRACT =
|
|
15
|
+
"kungfu-buildchain-v4-adopter-delivery/v1";
|
|
16
|
+
export const V4_ADOPTER_DELIVERY_READBACK_CONTRACT =
|
|
17
|
+
"kungfu-buildchain-v4-adopter-delivery-readback/v1";
|
|
18
|
+
export const V4_ADOPTER_DELIVERY_BOOTSTRAP_LINEAGE_CONTRACT =
|
|
19
|
+
"kungfu-buildchain-v4-adopter-delivery-bootstrap-lineage/v1";
|
|
20
|
+
export const V4_ADOPTER_DELIVERY_SOURCE = Object.freeze({
|
|
21
|
+
branch: "dev/v3/v3.0",
|
|
22
|
+
commit: "6b96bdad8d9f8ccf9275f27d9370a226a9c78465",
|
|
23
|
+
vectorSuiteRoot:
|
|
24
|
+
"sha256:c978707556406ffda4ef4192032332c01c2da1cebb6ad8c4f0edfc65d5cef0c7",
|
|
25
|
+
kfdPackage: "@kungfu-tech/kfd@1.0.0-alpha.65",
|
|
26
|
+
targetBranch: "dev/v4/v4.0",
|
|
27
|
+
initialTargetBase: "e5611377efc03178f8687d99968cfdfa3ce2825b",
|
|
28
|
+
targetBase: "e0342713c7447960c13bd73377282b2e93f4853d",
|
|
29
|
+
});
|
|
30
|
+
export const V4_ADOPTER_DELIVERY_ARCHIVE_AUTHORITY = Object.freeze({
|
|
31
|
+
buildchain: Object.freeze({
|
|
32
|
+
name: "@kungfu-tech/buildchain",
|
|
33
|
+
version: "3.0.9-alpha.16",
|
|
34
|
+
archiveRoot:
|
|
35
|
+
"sha256:3f425e2c77d11f0bee8eb9aa2448566bca7a9d14672a7e528ced3e505b3f14a3",
|
|
36
|
+
artifactRoot:
|
|
37
|
+
"sha256:3f425e2c77d11f0bee8eb9aa2448566bca7a9d14672a7e528ced3e505b3f14a3",
|
|
38
|
+
}),
|
|
39
|
+
kfd: Object.freeze({
|
|
40
|
+
name: "@kungfu-tech/kfd",
|
|
41
|
+
version: "1.0.0-alpha.65",
|
|
42
|
+
archiveRoot:
|
|
43
|
+
"sha256:c4dbd3f954910236d7f0823ea6887f4151e43b871df526ccdd599123421bced2",
|
|
44
|
+
artifactRoot:
|
|
45
|
+
"sha256:c0781bcaf191a58561ae32ee2fbedabbb48ed50b5725c356fbd83704089637f8",
|
|
46
|
+
}),
|
|
47
|
+
authorityRoot:
|
|
48
|
+
"sha256:9ba0cc6042b189ce749d01003617b57bcd03ea6ecf40e96d19e8ecbbfa347134",
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const ROOT = /^sha256:[0-9a-f]{64}$/u;
|
|
52
|
+
const SHA = /^[0-9a-f]{40}$/u;
|
|
53
|
+
const BUILTIN_DRIVER_SELECTORS = Object.freeze({
|
|
54
|
+
"json-assertion": "buildchain.adopter/json-assertion@1.0.0",
|
|
55
|
+
"kfd-category": "kfd.adopter-category/instance-manifest@1.0.0",
|
|
56
|
+
"legacy-kfd": "buildchain.kfd-adopter/legacy-support-matrix@1.0.0",
|
|
57
|
+
});
|
|
58
|
+
const BUILTIN_PROFILE_SELECTORS = Object.freeze({
|
|
59
|
+
"git-commit": "buildchain.artifact/git-commit@1.0.0",
|
|
60
|
+
package: "buildchain.artifact/package@1.0.0",
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
function exactKeys(value, keys, label) {
|
|
64
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
65
|
+
throw new TypeError(`${label} must be an object`);
|
|
66
|
+
}
|
|
67
|
+
const actual = Object.keys(value).sort();
|
|
68
|
+
const expected = [...keys].sort();
|
|
69
|
+
if (
|
|
70
|
+
actual.length !== expected.length ||
|
|
71
|
+
actual.some((entry, index) => entry !== expected[index])
|
|
72
|
+
) {
|
|
73
|
+
throw new TypeError(`${label} has an unsupported field set`);
|
|
74
|
+
}
|
|
75
|
+
return structuredClone(value);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function definitionKey(value) {
|
|
79
|
+
return `${value.id}@${value.version}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function resolveSelector(selector, table, definitions, label) {
|
|
83
|
+
const expected = table[selector];
|
|
84
|
+
if (!expected) throw new RangeError(`unknown ${label} selector: ${selector}`);
|
|
85
|
+
const selected = definitions.find(
|
|
86
|
+
(entry) => definitionKey(entry) === expected,
|
|
87
|
+
);
|
|
88
|
+
if (!selected)
|
|
89
|
+
throw new Error(
|
|
90
|
+
`${label} selector is not backed by an exact implementation`,
|
|
91
|
+
);
|
|
92
|
+
return selected;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function createJsonAssertionAdopterProtocolDriver() {
|
|
96
|
+
return defineAdopterProtocolDriver({
|
|
97
|
+
interface: ADOPTER_PROTOCOL_DRIVER_INTERFACE,
|
|
98
|
+
id: "buildchain.adopter/json-assertion",
|
|
99
|
+
version: "1.0.0",
|
|
100
|
+
verify({ request }) {
|
|
101
|
+
const declaration = request.declaration;
|
|
102
|
+
const exact =
|
|
103
|
+
declaration &&
|
|
104
|
+
typeof declaration === "object" &&
|
|
105
|
+
!Array.isArray(declaration) &&
|
|
106
|
+
Object.keys(declaration).length === 1 &&
|
|
107
|
+
typeof declaration.valid === "boolean";
|
|
108
|
+
const valid = exact && declaration.valid === true;
|
|
109
|
+
const issues = valid
|
|
110
|
+
? []
|
|
111
|
+
: [
|
|
112
|
+
{
|
|
113
|
+
code: "delivery-json-assertion-rejected",
|
|
114
|
+
path: "/declaration",
|
|
115
|
+
message:
|
|
116
|
+
"JSON assertion declarations must contain only valid: true.",
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
const report = {
|
|
120
|
+
schemaVersion: 1,
|
|
121
|
+
contract: "kungfu-buildchain-json-assertion-driver-report/v1",
|
|
122
|
+
valid,
|
|
123
|
+
declaration: structuredClone(declaration),
|
|
124
|
+
qualifying: false,
|
|
125
|
+
selfCertified: false,
|
|
126
|
+
releaseAuthorized: false,
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
valid,
|
|
130
|
+
report,
|
|
131
|
+
reportRoot: adopterDeliveryGateDigest(report),
|
|
132
|
+
issues,
|
|
133
|
+
};
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function createV4AdopterDeliveryRuntime({
|
|
139
|
+
drivers = [],
|
|
140
|
+
artifactProfiles = [],
|
|
141
|
+
} = {}) {
|
|
142
|
+
const allDrivers = [
|
|
143
|
+
createJsonAssertionAdopterProtocolDriver(),
|
|
144
|
+
createKfdAdopterCategoryProtocolDriver(),
|
|
145
|
+
createLegacyKfdAdopterProtocolDriver(),
|
|
146
|
+
...drivers,
|
|
147
|
+
];
|
|
148
|
+
const allProfiles = [
|
|
149
|
+
createGitCommitArtifactProfile(),
|
|
150
|
+
createPackageArtifactProfile(),
|
|
151
|
+
...artifactProfiles,
|
|
152
|
+
];
|
|
153
|
+
return Object.freeze({
|
|
154
|
+
drivers: Object.freeze(allDrivers),
|
|
155
|
+
artifactProfiles: Object.freeze(allProfiles),
|
|
156
|
+
evaluate({
|
|
157
|
+
request,
|
|
158
|
+
context = {},
|
|
159
|
+
driverSelector,
|
|
160
|
+
artifactProfileSelector,
|
|
161
|
+
} = {}) {
|
|
162
|
+
const driver = resolveSelector(
|
|
163
|
+
driverSelector,
|
|
164
|
+
BUILTIN_DRIVER_SELECTORS,
|
|
165
|
+
allDrivers,
|
|
166
|
+
"driver",
|
|
167
|
+
);
|
|
168
|
+
const profile = resolveSelector(
|
|
169
|
+
artifactProfileSelector,
|
|
170
|
+
BUILTIN_PROFILE_SELECTORS,
|
|
171
|
+
allProfiles,
|
|
172
|
+
"artifact profile",
|
|
173
|
+
);
|
|
174
|
+
if (definitionKey(driver) !== definitionKey(request?.protocol ?? {})) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
"driver selector does not match the exact request protocol",
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
if (
|
|
180
|
+
definitionKey(profile) !== definitionKey(request?.artifactProfile ?? {})
|
|
181
|
+
) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
"artifact profile selector does not match the exact request profile",
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
return createAdopterDeliveryGate({
|
|
187
|
+
drivers: [driver],
|
|
188
|
+
artifactProfiles: [profile],
|
|
189
|
+
}).evaluate(structuredClone(request), structuredClone(context));
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function runV4AdopterDeliveryGate(input, options = {}) {
|
|
195
|
+
const normalized = exactKeys(
|
|
196
|
+
input,
|
|
197
|
+
[
|
|
198
|
+
"schemaVersion",
|
|
199
|
+
"contract",
|
|
200
|
+
"driverSelector",
|
|
201
|
+
"artifactProfileSelector",
|
|
202
|
+
"request",
|
|
203
|
+
"context",
|
|
204
|
+
],
|
|
205
|
+
"v4 adopter delivery input",
|
|
206
|
+
);
|
|
207
|
+
if (
|
|
208
|
+
normalized.schemaVersion !== 1 ||
|
|
209
|
+
normalized.contract !== V4_ADOPTER_DELIVERY_CONTRACT
|
|
210
|
+
) {
|
|
211
|
+
throw new Error("v4 adopter delivery contract or version is unsupported");
|
|
212
|
+
}
|
|
213
|
+
const gateResult =
|
|
214
|
+
createV4AdopterDeliveryRuntime(options).evaluate(normalized);
|
|
215
|
+
const result = {
|
|
216
|
+
schemaVersion: 1,
|
|
217
|
+
contract: V4_ADOPTER_DELIVERY_READBACK_CONTRACT,
|
|
218
|
+
sourceAuthority: structuredClone(V4_ADOPTER_DELIVERY_SOURCE),
|
|
219
|
+
selection: {
|
|
220
|
+
driver: normalized.driverSelector,
|
|
221
|
+
artifactProfile: normalized.artifactProfileSelector,
|
|
222
|
+
},
|
|
223
|
+
gateResult,
|
|
224
|
+
qualifying: false,
|
|
225
|
+
selfCertified: false,
|
|
226
|
+
releaseAuthorized: false,
|
|
227
|
+
finalAuthority: "selected-public-driver-plus-exact-consumer-readback",
|
|
228
|
+
};
|
|
229
|
+
result.deliveryRoot = adopterDeliveryGateDigest(result);
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function verifyV4AdopterDeliveryReadback(
|
|
234
|
+
{ input, readback } = {},
|
|
235
|
+
options = {},
|
|
236
|
+
) {
|
|
237
|
+
if (!readback || !ROOT.test(readback.deliveryRoot ?? "")) {
|
|
238
|
+
throw new Error("exact adopter delivery readback is required");
|
|
239
|
+
}
|
|
240
|
+
const observed = runV4AdopterDeliveryGate(input, options);
|
|
241
|
+
if (
|
|
242
|
+
observed.deliveryRoot !== readback.deliveryRoot ||
|
|
243
|
+
adopterDeliveryGateDigest(observed) !== adopterDeliveryGateDigest(readback)
|
|
244
|
+
) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
"adopter delivery readback does not match exact recomputation",
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
return observed;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function qualifyV4AdopterDeliveryBootstrap({ input, lineage } = {}) {
|
|
253
|
+
const normalizedLineage = exactKeys(
|
|
254
|
+
lineage,
|
|
255
|
+
[
|
|
256
|
+
"schemaVersion",
|
|
257
|
+
"contract",
|
|
258
|
+
"sourceAuthorityCommit",
|
|
259
|
+
"targetBaseCommit",
|
|
260
|
+
"authorityArchiveRoot",
|
|
261
|
+
],
|
|
262
|
+
"bootstrap lineage",
|
|
263
|
+
);
|
|
264
|
+
if (
|
|
265
|
+
normalizedLineage.schemaVersion !== 1 ||
|
|
266
|
+
normalizedLineage.contract !==
|
|
267
|
+
V4_ADOPTER_DELIVERY_BOOTSTRAP_LINEAGE_CONTRACT ||
|
|
268
|
+
normalizedLineage.sourceAuthorityCommit !==
|
|
269
|
+
V4_ADOPTER_DELIVERY_SOURCE.commit ||
|
|
270
|
+
normalizedLineage.targetBaseCommit !==
|
|
271
|
+
V4_ADOPTER_DELIVERY_SOURCE.targetBase ||
|
|
272
|
+
!SHA.test(normalizedLineage.sourceAuthorityCommit) ||
|
|
273
|
+
!SHA.test(normalizedLineage.targetBaseCommit) ||
|
|
274
|
+
!ROOT.test(normalizedLineage.authorityArchiveRoot ?? "") ||
|
|
275
|
+
normalizedLineage.authorityArchiveRoot !== input?.authority?.packageRoot
|
|
276
|
+
) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
"bootstrap lineage does not bind the exact v3 authority and v4 base",
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
const bootstrap = qualifyBuildchainDeliveryInfrastructureBootstrap(input);
|
|
282
|
+
const result = {
|
|
283
|
+
schemaVersion: 1,
|
|
284
|
+
contract: "kungfu-buildchain-v4-adopter-delivery-bootstrap/v1",
|
|
285
|
+
sourceAuthority: structuredClone(V4_ADOPTER_DELIVERY_SOURCE),
|
|
286
|
+
lineage: normalizedLineage,
|
|
287
|
+
bootstrap,
|
|
288
|
+
qualifying: false,
|
|
289
|
+
selfCertified: false,
|
|
290
|
+
releaseAuthorized: false,
|
|
291
|
+
};
|
|
292
|
+
result.resultRoot = adopterDeliveryGateDigest(result);
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export async function loadV4PublishedAdopterDeliveryAuthority({
|
|
297
|
+
packages,
|
|
298
|
+
expectedAuthorityRoot,
|
|
299
|
+
} = {}) {
|
|
300
|
+
const request = assertV4PublishedAdopterDeliveryRequest({
|
|
301
|
+
packages,
|
|
302
|
+
expectedAuthorityRoot,
|
|
303
|
+
});
|
|
304
|
+
const authority = await loadPublishedBuildchainDeliveryAuthority(
|
|
305
|
+
request.packages,
|
|
306
|
+
);
|
|
307
|
+
if (authority.authorityRoot !== expectedAuthorityRoot) {
|
|
308
|
+
await authority.dispose();
|
|
309
|
+
throw new Error("published archive authority readback root does not match");
|
|
310
|
+
}
|
|
311
|
+
return authority;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function assertV4PublishedAdopterDeliveryRequest({
|
|
315
|
+
packages,
|
|
316
|
+
expectedAuthorityRoot,
|
|
317
|
+
} = {}) {
|
|
318
|
+
if (
|
|
319
|
+
!ROOT.test(expectedAuthorityRoot ?? "") ||
|
|
320
|
+
expectedAuthorityRoot !==
|
|
321
|
+
V4_ADOPTER_DELIVERY_ARCHIVE_AUTHORITY.authorityRoot
|
|
322
|
+
) {
|
|
323
|
+
throw new Error(
|
|
324
|
+
"exact published archive authority readback root is required",
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
const normalizedPackages = exactKeys(
|
|
328
|
+
packages,
|
|
329
|
+
["buildchain", "kfd"],
|
|
330
|
+
"published archive packages",
|
|
331
|
+
);
|
|
332
|
+
for (const key of ["buildchain", "kfd"]) {
|
|
333
|
+
const declaration = exactKeys(
|
|
334
|
+
normalizedPackages[key],
|
|
335
|
+
["name", "version", "archivePath", "archiveRoot", "artifactRoot"],
|
|
336
|
+
`${key} archive identity`,
|
|
337
|
+
);
|
|
338
|
+
const expected = V4_ADOPTER_DELIVERY_ARCHIVE_AUTHORITY[key];
|
|
339
|
+
if (
|
|
340
|
+
typeof declaration.archivePath !== "string" ||
|
|
341
|
+
declaration.archivePath.length === 0 ||
|
|
342
|
+
declaration.name !== expected.name ||
|
|
343
|
+
declaration.version !== expected.version ||
|
|
344
|
+
declaration.archiveRoot !== expected.archiveRoot ||
|
|
345
|
+
declaration.artifactRoot !== expected.artifactRoot
|
|
346
|
+
) {
|
|
347
|
+
throw new Error(`${key} archive identity is not the exact v3 authority`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
packages: normalizedPackages,
|
|
352
|
+
expectedAuthorityRoot,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export const V4_ADOPTER_DELIVERY_SELECTORS = Object.freeze({
|
|
357
|
+
drivers: BUILTIN_DRIVER_SELECTORS,
|
|
358
|
+
artifactProfiles: BUILTIN_PROFILE_SELECTORS,
|
|
359
|
+
});
|
|
@@ -100,7 +100,9 @@ export function validateV4DeliveryWarrantReadQualification(
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
export function projectV3QueueToV4ReadState(queueInput) {
|
|
103
|
-
const queue = normalizeDevDeliveryQueue(queueInput
|
|
103
|
+
const queue = normalizeDevDeliveryQueue(queueInput, {
|
|
104
|
+
allowLegacyV3Readback: true,
|
|
105
|
+
});
|
|
104
106
|
return {
|
|
105
107
|
schema: STATE_CONTRACT,
|
|
106
108
|
generation: queue.generation,
|
|
@@ -219,7 +221,9 @@ export async function runV4DeliveryWarrantReadCandidate(
|
|
|
219
221
|
});
|
|
220
222
|
if (typeof retain !== "function")
|
|
221
223
|
fail("retention-required", "v4 read evidence retention is required");
|
|
222
|
-
const queue = normalizeDevDeliveryQueue(queueInput
|
|
224
|
+
const queue = normalizeDevDeliveryQueue(queueInput, {
|
|
225
|
+
allowLegacyV3Readback: true,
|
|
226
|
+
});
|
|
223
227
|
const state = projectV3QueueToV4ReadState(queue);
|
|
224
228
|
const request = createV4DeliveryWarrantReadRequest(state, {
|
|
225
229
|
requestId,
|
|
@@ -235,7 +239,10 @@ export async function runV4DeliveryWarrantReadCandidate(
|
|
|
235
239
|
);
|
|
236
240
|
}
|
|
237
241
|
const projection = validateProjection(response, request, state);
|
|
238
|
-
const observation = observeDevDeliveryQueue(queue, {
|
|
242
|
+
const observation = observeDevDeliveryQueue(queue, {
|
|
243
|
+
now: observedAt,
|
|
244
|
+
allowLegacyV3Readback: true,
|
|
245
|
+
});
|
|
239
246
|
const evidence = {
|
|
240
247
|
schema: V4_DELIVERY_WARRANT_READ_EVIDENCE_CONTRACT,
|
|
241
248
|
authority: "typescript-v3",
|
|
@@ -8,6 +8,12 @@ export const V4_FLOATING_CONSUMER_CERTIFICATION =
|
|
|
8
8
|
"kungfu-buildchain-v4-floating-consumer-certification/v1";
|
|
9
9
|
|
|
10
10
|
const SHA256_ROOT = /^sha256:[0-9a-f]{64}$/u;
|
|
11
|
+
const ALPHA_RECOVERY_BOOTSTRAP = Object.freeze({
|
|
12
|
+
repository: "kungfu-systems/buildchain",
|
|
13
|
+
sourcePath: ".github/workflows/buildchain-ref-promotion-recovery.yml",
|
|
14
|
+
workflow: ".github/workflows/.release-candidate-promote.yml",
|
|
15
|
+
selector: "alpha/v4/v4.0",
|
|
16
|
+
});
|
|
11
17
|
|
|
12
18
|
function stableJson(value) {
|
|
13
19
|
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
@@ -109,15 +115,23 @@ function verifyAuthorityFields(value, expected, check, label) {
|
|
|
109
115
|
}
|
|
110
116
|
|
|
111
117
|
function verifyFloatingSelector(value, check, label) {
|
|
118
|
+
const bootstrap =
|
|
119
|
+
value?.caller?.repository === ALPHA_RECOVERY_BOOTSTRAP.repository &&
|
|
120
|
+
value?.invocation?.sourcePath === ALPHA_RECOVERY_BOOTSTRAP.sourcePath &&
|
|
121
|
+
value?.invocation?.workflow === ALPHA_RECOVERY_BOOTSTRAP.workflow &&
|
|
122
|
+
value?.invocation?.visibleSelector === ALPHA_RECOVERY_BOOTSTRAP.selector &&
|
|
123
|
+
value?.invocation?.selectorClass === "protected-bootstrap" &&
|
|
124
|
+
value?.invocation?.channel === "alpha";
|
|
112
125
|
check(
|
|
113
|
-
value?.invocation?.selectorClass === "floating",
|
|
126
|
+
value?.invocation?.selectorClass === "floating" || bootstrap,
|
|
114
127
|
"selector-class-invalid",
|
|
115
|
-
`${label} must bind a floating selector`,
|
|
128
|
+
`${label} must bind a floating selector or the bounded protected alpha recovery bootstrap`,
|
|
116
129
|
);
|
|
117
130
|
check(
|
|
118
|
-
["v4", "v4-alpha"].includes(value?.invocation?.visibleSelector)
|
|
131
|
+
["v4", "v4-alpha"].includes(value?.invocation?.visibleSelector) ||
|
|
132
|
+
bootstrap,
|
|
119
133
|
"selector-invalid",
|
|
120
|
-
`${label} selector must be v4
|
|
134
|
+
`${label} selector must be v4, v4-alpha, or the bounded protected alpha recovery bootstrap`,
|
|
121
135
|
);
|
|
122
136
|
}
|
|
123
137
|
|
|
@@ -26,6 +26,11 @@ const EXACT_SHA = /^[0-9a-f]{40}$/u;
|
|
|
26
26
|
const SHA256_ROOT = /^sha256:[0-9a-f]{64}$/u;
|
|
27
27
|
const BUILDCHAIN_REPOSITORY = "kungfu-systems/buildchain";
|
|
28
28
|
const CHANNELS = Object.freeze({ v4: "stable", "v4-alpha": "alpha" });
|
|
29
|
+
const ALPHA_RECOVERY_BOOTSTRAP = Object.freeze({
|
|
30
|
+
selector: "alpha/v4/v4.0",
|
|
31
|
+
sourcePath: ".github/workflows/buildchain-ref-promotion-recovery.yml",
|
|
32
|
+
workflow: ".github/workflows/.release-candidate-promote.yml",
|
|
33
|
+
});
|
|
29
34
|
const DEFAULT_STABLE_LOCK_PATH = ".buildchain/contract-lock.json";
|
|
30
35
|
const DEFAULT_ALPHA_LOCK_PATH = ".buildchain/alpha-contract-lock.json";
|
|
31
36
|
const TRANSIENT_ACTION =
|
|
@@ -277,7 +282,7 @@ function readContractLock(root, relative, expectedRef, label, failures) {
|
|
|
277
282
|
}
|
|
278
283
|
}
|
|
279
284
|
|
|
280
|
-
function classifyBuildchainUses(records, failures) {
|
|
285
|
+
function classifyBuildchainUses(records, failures, { repository } = {}) {
|
|
281
286
|
const uses = [];
|
|
282
287
|
for (const record of records) {
|
|
283
288
|
const parsed = coordinate(record.uses);
|
|
@@ -286,15 +291,27 @@ function classifyBuildchainUses(records, failures) {
|
|
|
286
291
|
parsed.selector === "v4" ||
|
|
287
292
|
parsed.selector === "v4-alpha" ||
|
|
288
293
|
/^v4(?:[./-]|$)/u.test(parsed.selector) ||
|
|
294
|
+
parsed.selector === ALPHA_RECOVERY_BOOTSTRAP.selector ||
|
|
289
295
|
EXACT_SHA.test(parsed.selector.toLowerCase()) ||
|
|
290
296
|
parsed.selector.includes("${{");
|
|
291
297
|
if (!v4Candidate) continue;
|
|
292
|
-
const
|
|
298
|
+
const protectedBootstrap =
|
|
299
|
+
repository === BUILDCHAIN_REPOSITORY &&
|
|
300
|
+
normalizeWorkflowPath(parsed.path) ===
|
|
301
|
+
ALPHA_RECOVERY_BOOTSTRAP.workflow &&
|
|
302
|
+
record.sourcePath === ALPHA_RECOVERY_BOOTSTRAP.sourcePath &&
|
|
303
|
+
parsed.selector === ALPHA_RECOVERY_BOOTSTRAP.selector;
|
|
304
|
+
const channel =
|
|
305
|
+
CHANNELS[parsed.selector] || (protectedBootstrap ? "alpha" : "");
|
|
293
306
|
uses.push({
|
|
294
307
|
...record,
|
|
295
308
|
...parsed,
|
|
296
309
|
channel,
|
|
297
|
-
selectorClass:
|
|
310
|
+
selectorClass: protectedBootstrap
|
|
311
|
+
? "protected-bootstrap"
|
|
312
|
+
: channel
|
|
313
|
+
? "floating"
|
|
314
|
+
: "rejected",
|
|
298
315
|
});
|
|
299
316
|
if (!channel) failures.push(selectorFailure(record, parsed.selector));
|
|
300
317
|
}
|
|
@@ -320,8 +337,97 @@ function selectInvocation(uses, workflow, failures, source = "", channel = "") {
|
|
|
320
337
|
return { expectedWorkflow, selected: invocations[0] };
|
|
321
338
|
}
|
|
322
339
|
|
|
340
|
+
function scanInvocationSource({
|
|
341
|
+
sourceRoot,
|
|
342
|
+
invocationRoot,
|
|
343
|
+
sourceScan,
|
|
344
|
+
sourceUses,
|
|
345
|
+
failures,
|
|
346
|
+
repository,
|
|
347
|
+
}) {
|
|
348
|
+
if (invocationRoot === sourceRoot)
|
|
349
|
+
return { invocationScan: sourceScan, invocationUses: sourceUses };
|
|
350
|
+
try {
|
|
351
|
+
const invocationScan = enumerateUses(invocationRoot);
|
|
352
|
+
return {
|
|
353
|
+
invocationScan,
|
|
354
|
+
invocationUses: classifyBuildchainUses(invocationScan.records, failures, {
|
|
355
|
+
repository,
|
|
356
|
+
}),
|
|
357
|
+
};
|
|
358
|
+
} catch (error) {
|
|
359
|
+
failures.push({
|
|
360
|
+
code: "invocation-source-scan-failed",
|
|
361
|
+
message: error.message,
|
|
362
|
+
});
|
|
363
|
+
return {
|
|
364
|
+
invocationScan: { files: [], records: [] },
|
|
365
|
+
invocationUses: [],
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function sourceScanRoot({
|
|
371
|
+
sourceRoot,
|
|
372
|
+
invocationRoot,
|
|
373
|
+
sourceScan,
|
|
374
|
+
invocationScan,
|
|
375
|
+
invocationUses,
|
|
376
|
+
}) {
|
|
377
|
+
const material =
|
|
378
|
+
invocationRoot === sourceRoot
|
|
379
|
+
? sourceScan
|
|
380
|
+
: {
|
|
381
|
+
source: sourceScan,
|
|
382
|
+
invocation: {
|
|
383
|
+
files: invocationScan.files,
|
|
384
|
+
invocations: invocationUses,
|
|
385
|
+
},
|
|
386
|
+
};
|
|
387
|
+
return sha256(stableJson(material));
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function verifySelectedLock({
|
|
391
|
+
selected,
|
|
392
|
+
stable,
|
|
393
|
+
alpha,
|
|
394
|
+
workflowSha,
|
|
395
|
+
failures,
|
|
396
|
+
}) {
|
|
397
|
+
const selectedLock = selected?.channel === "alpha" ? alpha : stable;
|
|
398
|
+
if (!selected?.channel || !selectedLock) return;
|
|
399
|
+
const authorityRef =
|
|
400
|
+
selected.selectorClass === "protected-bootstrap"
|
|
401
|
+
? "v4-alpha"
|
|
402
|
+
: selected.selector;
|
|
403
|
+
try {
|
|
404
|
+
const compatibility = evaluateBuildchainContractLock({
|
|
405
|
+
lock: selectedLock,
|
|
406
|
+
current: createBuildchainContractWorld({ root: defaultRuntimeRoot() }),
|
|
407
|
+
runtimeRef: authorityRef,
|
|
408
|
+
runtimeSha: workflowSha,
|
|
409
|
+
runtimeClass: selected.channel,
|
|
410
|
+
workflowShellRef: authorityRef,
|
|
411
|
+
expectedChannel: selected.channel,
|
|
412
|
+
expectedMajor: "v4",
|
|
413
|
+
});
|
|
414
|
+
if (!compatibility.ok) {
|
|
415
|
+
failures.push({
|
|
416
|
+
code: "selected-lock-runtime-incompatible",
|
|
417
|
+
message: `${selected.channel} contract lock rejects resolved workflow shell ${workflowSha}: ${compatibility.reasons?.join("; ") || compatibility.status}`,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
} catch (error) {
|
|
421
|
+
failures.push({
|
|
422
|
+
code: "selected-lock-contract-world-invalid",
|
|
423
|
+
message: `cannot evaluate ${selected.channel} contract lock against the resolved workflow shell: ${error.message}`,
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
323
428
|
export function scanV4FloatingConsumerPolicy({
|
|
324
429
|
root = process.cwd(),
|
|
430
|
+
invocationRoot = root,
|
|
325
431
|
repository = "",
|
|
326
432
|
sourceSha = "",
|
|
327
433
|
invokedWorkflow = "",
|
|
@@ -335,6 +441,7 @@ export function scanV4FloatingConsumerPolicy({
|
|
|
335
441
|
scannerRoot = "",
|
|
336
442
|
} = {}) {
|
|
337
443
|
const resolvedRoot = path.resolve(root);
|
|
444
|
+
const resolvedInvocationRoot = path.resolve(invocationRoot);
|
|
338
445
|
if (!policy || policy.contract !== V4_FLOATING_CONSUMER_POLICY) {
|
|
339
446
|
throw new Error(`policy must use ${V4_FLOATING_CONSUMER_POLICY}`);
|
|
340
447
|
}
|
|
@@ -374,43 +481,34 @@ export function scanV4FloatingConsumerPolicy({
|
|
|
374
481
|
} catch (error) {
|
|
375
482
|
failures.push({ code: "source-scan-failed", message: error.message });
|
|
376
483
|
}
|
|
377
|
-
const buildchainUses = classifyBuildchainUses(scanned.records, failures
|
|
484
|
+
const buildchainUses = classifyBuildchainUses(scanned.records, failures, {
|
|
485
|
+
repository,
|
|
486
|
+
});
|
|
487
|
+
const { invocationScan, invocationUses } = scanInvocationSource({
|
|
488
|
+
sourceRoot: resolvedRoot,
|
|
489
|
+
invocationRoot: resolvedInvocationRoot,
|
|
490
|
+
sourceScan: scanned,
|
|
491
|
+
sourceUses: buildchainUses,
|
|
492
|
+
failures,
|
|
493
|
+
repository,
|
|
494
|
+
});
|
|
378
495
|
const sourcePath = String(invocationSourcePath || "")
|
|
379
496
|
.split("@")[0]
|
|
380
497
|
.replace(`${repository}/`, "");
|
|
381
498
|
const { expectedWorkflow, selected } = selectInvocation(
|
|
382
|
-
|
|
499
|
+
invocationUses,
|
|
383
500
|
invokedWorkflow,
|
|
384
501
|
failures,
|
|
385
502
|
sourcePath,
|
|
386
503
|
expectedInvocationChannel,
|
|
387
504
|
);
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
runtimeSha: normalizedWorkflowSha,
|
|
396
|
-
runtimeClass: selected.channel,
|
|
397
|
-
workflowShellRef: selected.selector,
|
|
398
|
-
expectedChannel: selected.channel,
|
|
399
|
-
expectedMajor: "v4",
|
|
400
|
-
});
|
|
401
|
-
if (!compatibility.ok) {
|
|
402
|
-
failures.push({
|
|
403
|
-
code: "selected-lock-runtime-incompatible",
|
|
404
|
-
message: `${selected.channel} contract lock rejects resolved workflow shell ${normalizedWorkflowSha}: ${compatibility.reasons?.join("; ") || compatibility.status}`,
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
} catch (error) {
|
|
408
|
-
failures.push({
|
|
409
|
-
code: "selected-lock-contract-world-invalid",
|
|
410
|
-
message: `cannot evaluate ${selected.channel} contract lock against the resolved workflow shell: ${error.message}`,
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
}
|
|
505
|
+
verifySelectedLock({
|
|
506
|
+
selected,
|
|
507
|
+
stable,
|
|
508
|
+
alpha,
|
|
509
|
+
workflowSha: normalizedWorkflowSha,
|
|
510
|
+
failures,
|
|
511
|
+
});
|
|
414
512
|
|
|
415
513
|
const policyRoot = sha256(stableJson(policy));
|
|
416
514
|
const resolvedScannerRoot = scannerRoot || policyRoot;
|
|
@@ -424,9 +522,14 @@ export function scanV4FloatingConsumerPolicy({
|
|
|
424
522
|
stable: stable ? sha256(stableJson(stable)) : "",
|
|
425
523
|
alpha: alpha ? sha256(stableJson(alpha)) : "",
|
|
426
524
|
};
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
525
|
+
const sourceScan = { files: scanned.files, invocations: buildchainUses };
|
|
526
|
+
const scanRoot = sourceScanRoot({
|
|
527
|
+
sourceRoot: resolvedRoot,
|
|
528
|
+
invocationRoot: resolvedInvocationRoot,
|
|
529
|
+
sourceScan,
|
|
530
|
+
invocationScan,
|
|
531
|
+
invocationUses,
|
|
532
|
+
});
|
|
430
533
|
const receipt = {
|
|
431
534
|
schema: V4_FLOATING_CONSUMER_RECEIPT,
|
|
432
535
|
status: failures.length ? "failed" : "passed",
|
|
@@ -459,7 +562,7 @@ export function scanV4FloatingConsumerPolicy({
|
|
|
459
562
|
ok: failures.length === 0,
|
|
460
563
|
failures,
|
|
461
564
|
scannedFiles: scanned.files,
|
|
462
|
-
invocations:
|
|
565
|
+
invocations: invocationUses,
|
|
463
566
|
receipt,
|
|
464
567
|
receiptRoot,
|
|
465
568
|
};
|