@kungfu-tech/buildchain 4.0.1-alpha.3 → 4.0.1-alpha.31
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/decisions/0002-next-development-transition.md +101 -0
- package/architecture/internal-capabilities.json +117 -8
- package/architecture/maintainability-policy.json +43 -25
- package/architecture/release-tail-contract-inventory.json +5 -2
- package/architecture/v3-core-mechanism-inventory.json +89 -0
- package/architecture/v4-adopter-delivery-parity.json +32 -5
- package/architecture/v4-capability-state-machine-manifest.json +73 -0
- package/architecture/v4-compatibility-facts-parity.json +77 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.json +15 -6
- package/architecture/v4-floating-consumer-policy.json +6 -0
- package/architecture/v4-next-development-parity.json +192 -0
- package/architecture/v4-publication-rehearsal-parity.json +84 -0
- package/architecture/v4-release-train-parity.json +1 -0
- package/architecture/v4-tail-reseal-parity.json +150 -0
- package/bin/buildchain.mjs +23 -2
- package/bin/internal/adopter-delivery-cli.mjs +70 -0
- package/bin/internal/command-registry.mjs +3 -0
- package/bin/internal/compatibility-facts-cli.mjs +101 -0
- package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +47 -0
- package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +47 -0
- package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +40 -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-compatibility-facts-v1/shared.json +1129 -0
- package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +7 -1
- 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 +229 -0
- package/contracts/next-development-request-v1.schema.json +66 -0
- package/contracts/next-development-transition-v1.schema.json +292 -0
- package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
- package/contracts/v4-compatibility-facts-v1.schema.json +230 -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/contracts/v4-tail-reseal-v1.schema.json +276 -0
- package/dist/site/agent-index.json +4 -0
- package/dist/site/artifact-schemas.json +8 -0
- package/dist/site/buildchain-contract.json +1393 -28
- package/dist/site/buildchain-site.json +477 -32
- package/dist/site/capability-registry.json +17 -14
- package/dist/site/cli-registry.json +159 -1
- package/dist/site/kfd-claims.json +455 -15
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +53 -7
- package/dist/site/node-api-registry.json +13921 -9565
- package/dist/site/page-registry.json +442 -21
- package/dist/site/public-surface-audit.json +440 -11
- package/dist/site/publication-authority-registry.json +44 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +22 -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-compatibility-facts-v1.schema.json +230 -0
- package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/site-manifest.json +35 -11
- package/dist/site/workflow-registry.json +142 -8
- package/docs/MAP.md +7 -2
- package/docs/cli-reference.md +193 -0
- package/docs/cli.md +8 -0
- package/docs/dev-alpha-candidate-patrol.md +8 -0
- package/docs/dev-delivery-warrant.md +30 -5
- package/docs/next-development-transition.md +118 -0
- package/docs/node-api-reference.md +578 -285
- package/docs/release-candidate.md +7 -0
- package/docs/release-tail-provider-plane.md +30 -0
- package/docs/v4-adopter-delivery.md +133 -0
- package/docs/v4-compatibility-facts.md +91 -0
- package/docs/v4-publication-rehearsal.md +130 -0
- package/docs/v4-tail-reseal.md +73 -0
- package/docs/versioning.md +4 -3
- package/package.json +30 -6
- 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 +3 -0
- package/packages/core/buildchain-compatibility-authority.js +336 -0
- package/packages/core/buildchain-compatibility-fact.js +394 -0
- package/packages/core/buildchain-compatibility-facts.json +217 -0
- package/packages/core/buildchain-compatibility-proof.js +566 -0
- package/packages/core/buildchain-config.js +84 -0
- package/packages/core/buildchain-contract.js +93 -141
- 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/channel-candidate.js +8 -0
- package/packages/core/channel-promotion-baseline.js +55 -0
- package/packages/core/ci-lane-change-budget.js +247 -0
- package/packages/core/dev-alpha-candidate-selection.js +10 -2
- package/packages/core/dev-delivery-execution-transfer.js +21 -1
- package/packages/core/dev-delivery-native-proof.js +43 -27
- package/packages/core/dev-delivery-process-boundary.js +19 -7
- package/packages/core/dev-delivery-provider-heartbeat.js +18 -6
- package/packages/core/dev-delivery-warrant-state.js +32 -10
- package/packages/core/dev-delivery-warrant.js +9 -3
- package/packages/core/github-governance-authority.js +1 -0
- package/packages/core/index.js +5 -0
- package/packages/core/kungfu-temporal-fact.js +557 -0
- package/packages/core/legacy-kfd-adopter-driver.js +167 -0
- package/packages/core/next-development-candidate-reservation.js +186 -0
- package/packages/core/next-development-controller.js +728 -0
- package/packages/core/next-development-projection.js +287 -0
- package/packages/core/next-development-transition.js +738 -0
- package/packages/core/paper-agent-entry.js +11 -5
- package/packages/core/paper.js +16 -5
- package/packages/core/published-delivery-authority.js +266 -0
- package/packages/core/release-candidate-recovery.js +70 -15
- 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-canonical-contracts.js +5 -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 +143 -20
- package/packages/core/v4-floating-consumer-release-passport.js +35 -8
- 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/v4-tail-reseal-contract.js +26 -0
- package/packages/core/v4-tail-reseal-github.js +178 -0
- package/packages/core/v4-tail-reseal-receipt.js +225 -0
- package/packages/core/v4-tail-reseal.js +594 -0
- package/packages/core/workflow-yaml-contract.js +24 -2
- package/scripts/artifact-signing-controller-core.mjs +1 -1
- package/scripts/buildchain-cli-help.mjs +30 -0
- package/scripts/check-ci-lane-change-budget.mjs +100 -0
- package/scripts/check-inventory.mjs +14 -3
- package/scripts/check-v4-floating-consumer-policy-contract.mjs +23 -4
- package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
- 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-auto-merge.mjs +1 -1
- package/scripts/dev-pr-delivery-warrant.mjs +1 -0
- package/scripts/dev-pr-prequeue-guard.mjs +0 -18
- package/scripts/dev-qualification-patrol.mjs +9 -9
- package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
- package/scripts/generate-channel-promotion-workflow.mjs +2 -2
- package/scripts/generate-next-development-guidance.mjs +49 -0
- package/scripts/generate-release-candidate-passport.mjs +152 -0
- package/scripts/generate-site-bundle.mjs +46 -3
- package/scripts/init-repo.mjs +22 -4
- package/scripts/next-development-self-dogfood-harness.mjs +460 -0
- package/scripts/next-development-self-dogfood.mjs +597 -0
- package/scripts/next-development-transition.mjs +47 -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/release-train-self-dogfood.mjs +94 -92
- package/scripts/resume-from-candidate-run.mjs +560 -30
- package/scripts/site-capability-metadata.mjs +29 -0
- package/scripts/stable-candidate-qualification.mjs +9 -10
- package/scripts/v4-consumer-policy.mjs +5 -5
- package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
- package/scripts/v4-tail-reseal-macos-rehearsal.mjs +134 -0
- package/scripts/v4-tail-reseal.mjs +416 -0
- package/templates/native-dev-delivery.yml +2 -0
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
import {
|
|
2
|
+
devDeliveryClone as clone,
|
|
3
|
+
devDeliveryContentRoot as contentRoot,
|
|
4
|
+
devDeliveryExactRoot as exactRoot,
|
|
5
|
+
devDeliveryText as text,
|
|
6
|
+
} from "./dev-delivery-common.js";
|
|
7
|
+
import { evaluateBuildchainChannelBinding } from "./buildchain-channel-identity.js";
|
|
8
|
+
import {
|
|
9
|
+
assertBuildchainCompatibilityProjection,
|
|
10
|
+
createBuildchainCompatibilityProofRegistry,
|
|
11
|
+
resolveBuildchainCompatibilityProof,
|
|
12
|
+
} from "./buildchain-compatibility-authority.js";
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
BUILDCHAIN_COMPATIBILITY_FACT_REGISTRY_SCHEMA,
|
|
16
|
+
BUILDCHAIN_COMPATIBILITY_RELEASE_EVIDENCE_SCHEMA,
|
|
17
|
+
BUILDCHAIN_COMPATIBILITY_FACT_SCHEMA,
|
|
18
|
+
BUILDCHAIN_COMPATIBILITY_PROOF_REGISTRY_SCHEMA,
|
|
19
|
+
BUILDCHAIN_COMPATIBILITY_PROOF_SCHEMA,
|
|
20
|
+
createBuildchainCompatibilityFact,
|
|
21
|
+
createBuildchainCompatibilityFactRegistry,
|
|
22
|
+
createBuildchainCompatibilityProof,
|
|
23
|
+
createHistoricalBuildchainCompatibilityFacts,
|
|
24
|
+
createHistoricalBuildchainCompatibilityProofs,
|
|
25
|
+
verifyBuildchainCompatibilityFact,
|
|
26
|
+
verifyBuildchainCompatibilityFactRegistry,
|
|
27
|
+
verifyBuildchainCompatibilityProof,
|
|
28
|
+
} from "./buildchain-compatibility-fact.js";
|
|
29
|
+
export {
|
|
30
|
+
assertBuildchainCompatibilityProjection,
|
|
31
|
+
createBuildchainCompatibilityPathQuery,
|
|
32
|
+
createBuildchainCompatibilityProofRegistry,
|
|
33
|
+
createBuildchainCompatibilityReleaseEvidence,
|
|
34
|
+
resolveBuildchainCompatibilityProof,
|
|
35
|
+
verifyBuildchainCompatibilityPath,
|
|
36
|
+
} from "./buildchain-compatibility-authority.js";
|
|
37
|
+
|
|
38
|
+
export const BUILDCHAIN_LEGACY_COMPATIBILITY_VERIFICATION_RECEIPT_SCHEMA =
|
|
39
|
+
"kungfu.buildchain.compatibility-verification-receipt/v1";
|
|
40
|
+
export const BUILDCHAIN_COMPATIBILITY_VERIFICATION_RECEIPT_SCHEMA =
|
|
41
|
+
"kungfu.buildchain.compatibility-verification-receipt/v2";
|
|
42
|
+
|
|
43
|
+
const BUILDCHAIN_CONTRACT = "kungfu-buildchain-runtime-contract-world";
|
|
44
|
+
const COMPATIBILITY_OPERATION = "accept-contract-lock";
|
|
45
|
+
const COMPATIBILITY_DIRECTION = "source-to-target";
|
|
46
|
+
|
|
47
|
+
function exactText(value, label) {
|
|
48
|
+
const normalized = text(value);
|
|
49
|
+
if (!normalized) throw new Error(`${label} must not be empty`);
|
|
50
|
+
return normalized;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function createBuildchainCompatibilityVerificationReceipt(input = {}) {
|
|
54
|
+
const decisions = clone(input.decisions || []);
|
|
55
|
+
const usedProofRoots = [
|
|
56
|
+
...new Set(
|
|
57
|
+
decisions
|
|
58
|
+
.map((decision) => decision.proofRoot)
|
|
59
|
+
.filter(Boolean)
|
|
60
|
+
.map((root) => exactRoot(root, "decision.proofRoot")),
|
|
61
|
+
),
|
|
62
|
+
].sort();
|
|
63
|
+
const usedFactRoots = [
|
|
64
|
+
...new Set(
|
|
65
|
+
decisions
|
|
66
|
+
.map((decision) => decision.factRoot)
|
|
67
|
+
.filter(Boolean)
|
|
68
|
+
.map((root) => exactRoot(root, "decision.factRoot")),
|
|
69
|
+
),
|
|
70
|
+
].sort();
|
|
71
|
+
const pathReceiptRoots = [
|
|
72
|
+
...new Set(
|
|
73
|
+
decisions
|
|
74
|
+
.map((decision) => decision.pathReceiptRoot)
|
|
75
|
+
.filter(Boolean)
|
|
76
|
+
.map((root) => exactRoot(root, "decision.pathReceiptRoot")),
|
|
77
|
+
),
|
|
78
|
+
].sort();
|
|
79
|
+
const body = {
|
|
80
|
+
schema: BUILDCHAIN_COMPATIBILITY_VERIFICATION_RECEIPT_SCHEMA,
|
|
81
|
+
operation: COMPATIBILITY_OPERATION,
|
|
82
|
+
direction: COMPATIBILITY_DIRECTION,
|
|
83
|
+
status: exactText(input.status, "status"),
|
|
84
|
+
compatible: input.compatible === true,
|
|
85
|
+
policy: exactText(input.policy, "policy"),
|
|
86
|
+
scope: clone(input.scope || {}),
|
|
87
|
+
acceptedContractDigest: text(input.acceptedContractDigest),
|
|
88
|
+
acceptedCompatibilityDigest: text(input.acceptedCompatibilityDigest),
|
|
89
|
+
acceptedFactRegistryRoot: text(input.acceptedFactRegistryRoot),
|
|
90
|
+
acceptedProofRegistryRoot: text(input.acceptedProofRegistryRoot),
|
|
91
|
+
currentContractDigest: text(input.currentContractDigest),
|
|
92
|
+
currentCompatibilityDigest: text(input.currentCompatibilityDigest),
|
|
93
|
+
currentFactRegistryRoot: exactRoot(
|
|
94
|
+
input.currentFactRegistryRoot,
|
|
95
|
+
"currentFactRegistryRoot",
|
|
96
|
+
),
|
|
97
|
+
currentFactCutRoot: exactRoot(
|
|
98
|
+
input.currentFactCutRoot,
|
|
99
|
+
"currentFactCutRoot",
|
|
100
|
+
),
|
|
101
|
+
currentProofRegistryRoot: text(input.currentProofRegistryRoot),
|
|
102
|
+
decisions,
|
|
103
|
+
usedProofRoots,
|
|
104
|
+
usedFactRoots,
|
|
105
|
+
pathReceiptRoots,
|
|
106
|
+
reasonCodes: [
|
|
107
|
+
...new Set(
|
|
108
|
+
(input.reasonCodes || []).map((reason) =>
|
|
109
|
+
exactText(reason, "reasonCode"),
|
|
110
|
+
),
|
|
111
|
+
),
|
|
112
|
+
].sort(),
|
|
113
|
+
};
|
|
114
|
+
return { ...body, receiptRoot: contentRoot(body) };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function createLegacyCompatibilityVerificationReceipt(input = {}) {
|
|
118
|
+
const decisions = clone(input.decisions || []);
|
|
119
|
+
const body = {
|
|
120
|
+
schema: BUILDCHAIN_LEGACY_COMPATIBILITY_VERIFICATION_RECEIPT_SCHEMA,
|
|
121
|
+
operation: COMPATIBILITY_OPERATION,
|
|
122
|
+
direction: COMPATIBILITY_DIRECTION,
|
|
123
|
+
status: exactText(input.status, "status"),
|
|
124
|
+
compatible: input.compatible === true,
|
|
125
|
+
policy: exactText(input.policy, "policy"),
|
|
126
|
+
scope: clone(input.scope || {}),
|
|
127
|
+
acceptedContractDigest: text(input.acceptedContractDigest),
|
|
128
|
+
acceptedCompatibilityDigest: text(input.acceptedCompatibilityDigest),
|
|
129
|
+
acceptedProofRegistryRoot: text(input.acceptedProofRegistryRoot),
|
|
130
|
+
currentContractDigest: text(input.currentContractDigest),
|
|
131
|
+
currentCompatibilityDigest: text(input.currentCompatibilityDigest),
|
|
132
|
+
currentProofRegistryRoot: text(input.currentProofRegistryRoot),
|
|
133
|
+
decisions,
|
|
134
|
+
usedProofRoots: [
|
|
135
|
+
...new Set(
|
|
136
|
+
decisions
|
|
137
|
+
.map((decision) => decision.proofRoot)
|
|
138
|
+
.filter(Boolean)
|
|
139
|
+
.map((entry) => exactRoot(entry, "decision.proofRoot")),
|
|
140
|
+
),
|
|
141
|
+
].sort(),
|
|
142
|
+
reasonCodes: [
|
|
143
|
+
...new Set(
|
|
144
|
+
(input.reasonCodes || []).map((reason) =>
|
|
145
|
+
exactText(reason, "reasonCode"),
|
|
146
|
+
),
|
|
147
|
+
),
|
|
148
|
+
].sort(),
|
|
149
|
+
};
|
|
150
|
+
return { ...body, receiptRoot: contentRoot(body) };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function verifyBuildchainCompatibilityVerificationReceipt(receiptInput) {
|
|
154
|
+
try {
|
|
155
|
+
const receipt = clone(receiptInput || {});
|
|
156
|
+
const normalized =
|
|
157
|
+
receipt.schema === BUILDCHAIN_COMPATIBILITY_VERIFICATION_RECEIPT_SCHEMA
|
|
158
|
+
? createBuildchainCompatibilityVerificationReceipt(receipt)
|
|
159
|
+
: receipt.schema ===
|
|
160
|
+
BUILDCHAIN_LEGACY_COMPATIBILITY_VERIFICATION_RECEIPT_SCHEMA
|
|
161
|
+
? createLegacyCompatibilityVerificationReceipt(receipt)
|
|
162
|
+
: null;
|
|
163
|
+
if (!normalized) return { ok: false, reason: "unsupported-schema" };
|
|
164
|
+
if (normalized.receiptRoot !== receipt.receiptRoot)
|
|
165
|
+
return { ok: false, reason: "receipt-root-drift" };
|
|
166
|
+
if (JSON.stringify(normalized) !== JSON.stringify(receipt))
|
|
167
|
+
return { ok: false, reason: "non-canonical-receipt" };
|
|
168
|
+
return {
|
|
169
|
+
ok: true,
|
|
170
|
+
reason: "exact-compatibility-verification-receipt",
|
|
171
|
+
receiptRoot: receipt.receiptRoot,
|
|
172
|
+
};
|
|
173
|
+
} catch (error) {
|
|
174
|
+
return { ok: false, reason: "invalid-receipt", error: error.message };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function contractSummary(
|
|
179
|
+
contractWorld,
|
|
180
|
+
runtimeRef = "",
|
|
181
|
+
runtimeSha = "",
|
|
182
|
+
) {
|
|
183
|
+
return {
|
|
184
|
+
ref: runtimeRef,
|
|
185
|
+
resolvedSha: runtimeSha,
|
|
186
|
+
contract: contractWorld.contract,
|
|
187
|
+
contractDigest: contractWorld.contractDigest,
|
|
188
|
+
compatibilityDigest: contractWorld.compatibilityDigest,
|
|
189
|
+
compatibilityFactRegistryRoot: contractWorld.compatibilityFactRegistryRoot,
|
|
190
|
+
compatibilityFactCutRoot: contractWorld.compatibilityFactCutRoot,
|
|
191
|
+
compatibilityProofRegistryRoot:
|
|
192
|
+
contractWorld.compatibilityProofRegistryRoot,
|
|
193
|
+
majorLine: contractWorld.majorLine,
|
|
194
|
+
surfaceCount: Array.isArray(contractWorld.surfaces)
|
|
195
|
+
? contractWorld.surfaces.length
|
|
196
|
+
: 0,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function evaluationReceipt({
|
|
201
|
+
status,
|
|
202
|
+
compatible,
|
|
203
|
+
policy,
|
|
204
|
+
accepted,
|
|
205
|
+
current,
|
|
206
|
+
decisions = [],
|
|
207
|
+
reasons = [],
|
|
208
|
+
}) {
|
|
209
|
+
return createBuildchainCompatibilityVerificationReceipt({
|
|
210
|
+
status,
|
|
211
|
+
compatible,
|
|
212
|
+
policy,
|
|
213
|
+
scope: { contract: current.contract, majorLine: current.majorLine },
|
|
214
|
+
acceptedContractDigest: accepted.contractDigest,
|
|
215
|
+
acceptedCompatibilityDigest: accepted.compatibilityDigest,
|
|
216
|
+
acceptedFactRegistryRoot: accepted.compatibilityFactRegistryRoot,
|
|
217
|
+
acceptedProofRegistryRoot: accepted.compatibilityProofRegistryRoot,
|
|
218
|
+
currentContractDigest: current.contractDigest,
|
|
219
|
+
currentCompatibilityDigest: current.compatibilityDigest,
|
|
220
|
+
currentFactRegistryRoot: current.compatibilityFactRegistryRoot,
|
|
221
|
+
currentFactCutRoot: current.compatibilityFactCutRoot,
|
|
222
|
+
currentProofRegistryRoot: current.compatibilityProofRegistryRoot,
|
|
223
|
+
decisions,
|
|
224
|
+
reasonCodes: reasons,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function contractPolicyReasons(accepted, current, policy) {
|
|
229
|
+
const reasons = [];
|
|
230
|
+
if (accepted.contract !== current.contract)
|
|
231
|
+
reasons.push(
|
|
232
|
+
`contract changed from ${accepted.contract || "(unknown)"} to ${current.contract}`,
|
|
233
|
+
);
|
|
234
|
+
if (accepted.majorLine && accepted.majorLine !== current.majorLine)
|
|
235
|
+
reasons.push(
|
|
236
|
+
`major line changed from ${accepted.majorLine} to ${current.majorLine}`,
|
|
237
|
+
);
|
|
238
|
+
if (policy === "exact" && accepted.contractDigest !== current.contractDigest)
|
|
239
|
+
reasons.push(
|
|
240
|
+
"exact policy requires the contract digest to remain unchanged",
|
|
241
|
+
);
|
|
242
|
+
if (!["major-compatible", "allow-additive", "exact"].includes(policy))
|
|
243
|
+
reasons.push(`unsupported compatibility policy: ${policy}`);
|
|
244
|
+
return reasons;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function evaluateSurfaceRelation({
|
|
248
|
+
oldSurface,
|
|
249
|
+
nextSurface,
|
|
250
|
+
proofRegistry,
|
|
251
|
+
majorLine,
|
|
252
|
+
}) {
|
|
253
|
+
if (!nextSurface) {
|
|
254
|
+
return {
|
|
255
|
+
reason: `surface removed: ${oldSurface.id}`,
|
|
256
|
+
decision: {
|
|
257
|
+
surfaceId: oldSurface.id,
|
|
258
|
+
status: "surface-removed",
|
|
259
|
+
sourceBreakingDigest: oldSurface.breakingDigest,
|
|
260
|
+
targetBreakingDigest: "",
|
|
261
|
+
reason: "surface-removed",
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
if (nextSurface.breakingDigest === oldSurface.breakingDigest) return {};
|
|
266
|
+
const resolution = resolveBuildchainCompatibilityProof({
|
|
267
|
+
registry: proofRegistry,
|
|
268
|
+
surface: nextSurface,
|
|
269
|
+
sourceBreakingDigest: oldSurface.breakingDigest,
|
|
270
|
+
majorLine,
|
|
271
|
+
});
|
|
272
|
+
if (!resolution.ok) {
|
|
273
|
+
return {
|
|
274
|
+
reason: `surface breaking digest changed without a valid compatibility proof: ${oldSurface.id} (${resolution.reason})`,
|
|
275
|
+
decision: {
|
|
276
|
+
surfaceId: oldSurface.id,
|
|
277
|
+
status: "rejected",
|
|
278
|
+
sourceBreakingDigest: oldSurface.breakingDigest,
|
|
279
|
+
targetBreakingDigest: nextSurface.breakingDigest,
|
|
280
|
+
reason: resolution.reason,
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
const proof = resolution.proof;
|
|
285
|
+
return {
|
|
286
|
+
decision: {
|
|
287
|
+
surfaceId: oldSurface.id,
|
|
288
|
+
status: "accepted",
|
|
289
|
+
sourceBreakingDigest: oldSurface.breakingDigest,
|
|
290
|
+
targetBreakingDigest: nextSurface.breakingDigest,
|
|
291
|
+
proofRoot: proof.proofRoot,
|
|
292
|
+
factRoot: resolution.factRoot,
|
|
293
|
+
pathReceipt: resolution.pathReceipt,
|
|
294
|
+
pathReceiptRoot: resolution.pathReceiptRoot,
|
|
295
|
+
direction: proof.direction,
|
|
296
|
+
operation: proof.operation,
|
|
297
|
+
scope: proof.scope,
|
|
298
|
+
scopeRoot: proof.scopeRoot,
|
|
299
|
+
evidence: proof.evidence,
|
|
300
|
+
evidenceRoot: proof.evidenceRoot,
|
|
301
|
+
authority: proof.authority,
|
|
302
|
+
authorityRoot: proof.authorityRoot,
|
|
303
|
+
cut: proof.cut,
|
|
304
|
+
cutRoot: proof.cutRoot,
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function evaluateChannelBinding({
|
|
310
|
+
lock,
|
|
311
|
+
workflowShellRef,
|
|
312
|
+
runtimeRef,
|
|
313
|
+
expectedChannel,
|
|
314
|
+
expectedMajor,
|
|
315
|
+
allowOpaqueRuntime,
|
|
316
|
+
}) {
|
|
317
|
+
if (!workflowShellRef && !expectedChannel && !expectedMajor) return null;
|
|
318
|
+
return evaluateBuildchainChannelBinding({
|
|
319
|
+
workflowShellRef,
|
|
320
|
+
runtimeRef,
|
|
321
|
+
lockRef: lock?.buildchain?.ref || "",
|
|
322
|
+
lockMajorLine: lock?.buildchain?.majorLine || "",
|
|
323
|
+
expectedChannel,
|
|
324
|
+
expectedMajor,
|
|
325
|
+
allowOpaqueRuntime,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function evaluateBuildchainContractLock({
|
|
330
|
+
lock,
|
|
331
|
+
current,
|
|
332
|
+
runtimeRef = "",
|
|
333
|
+
runtimeSha = "",
|
|
334
|
+
runtimeClass = "",
|
|
335
|
+
compatibilityPolicy = "",
|
|
336
|
+
workflowShellRef = "",
|
|
337
|
+
expectedChannel = "",
|
|
338
|
+
expectedMajor = "",
|
|
339
|
+
allowOpaqueRuntime = false,
|
|
340
|
+
} = {}) {
|
|
341
|
+
if (!current || current.contract !== BUILDCHAIN_CONTRACT) {
|
|
342
|
+
throw new Error("current must be a Buildchain runtime contract world");
|
|
343
|
+
}
|
|
344
|
+
assertBuildchainCompatibilityProjection(current);
|
|
345
|
+
const binding = evaluateChannelBinding({
|
|
346
|
+
lock,
|
|
347
|
+
workflowShellRef,
|
|
348
|
+
runtimeRef,
|
|
349
|
+
expectedChannel,
|
|
350
|
+
expectedMajor,
|
|
351
|
+
allowOpaqueRuntime,
|
|
352
|
+
});
|
|
353
|
+
if (binding?.ok === false) {
|
|
354
|
+
return {
|
|
355
|
+
ok: false,
|
|
356
|
+
status: binding.status,
|
|
357
|
+
drift: false,
|
|
358
|
+
compatible: false,
|
|
359
|
+
issueRecommended: false,
|
|
360
|
+
reasons: binding.reasons,
|
|
361
|
+
channelBinding: binding,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
if (!new Set(["stable", "alpha"]).has(runtimeClass)) {
|
|
365
|
+
return {
|
|
366
|
+
ok: true,
|
|
367
|
+
status: "non-floating-runtime",
|
|
368
|
+
drift: false,
|
|
369
|
+
compatible: true,
|
|
370
|
+
issueRecommended: false,
|
|
371
|
+
reason: `runtime class ${runtimeClass || "unknown"} is not a stable floating ref`,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
if (!lock) {
|
|
375
|
+
return {
|
|
376
|
+
ok: true,
|
|
377
|
+
status: "missing-lock",
|
|
378
|
+
drift: false,
|
|
379
|
+
compatible: true,
|
|
380
|
+
issueRecommended: false,
|
|
381
|
+
reason: "consumer repository has no Buildchain contract lock",
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
const accepted = lock.buildchain || {};
|
|
385
|
+
const policy =
|
|
386
|
+
compatibilityPolicy || accepted.compatibilityPolicy || "major-compatible";
|
|
387
|
+
const shaDrift =
|
|
388
|
+
!!accepted.resolvedSha &&
|
|
389
|
+
!!runtimeSha &&
|
|
390
|
+
accepted.resolvedSha !== runtimeSha;
|
|
391
|
+
const contractDrift =
|
|
392
|
+
!!accepted.contractDigest &&
|
|
393
|
+
accepted.contractDigest !== current.contractDigest;
|
|
394
|
+
if (!shaDrift && !contractDrift) {
|
|
395
|
+
const verificationReceipt = evaluationReceipt({
|
|
396
|
+
status: "unchanged",
|
|
397
|
+
compatible: true,
|
|
398
|
+
policy,
|
|
399
|
+
accepted,
|
|
400
|
+
current,
|
|
401
|
+
});
|
|
402
|
+
return {
|
|
403
|
+
ok: true,
|
|
404
|
+
status: "unchanged",
|
|
405
|
+
drift: false,
|
|
406
|
+
compatible: true,
|
|
407
|
+
issueRecommended: false,
|
|
408
|
+
policy,
|
|
409
|
+
accepted,
|
|
410
|
+
current: contractSummary(current, runtimeRef, runtimeSha),
|
|
411
|
+
compatibilityDecisions: [],
|
|
412
|
+
usedProofRoots: [],
|
|
413
|
+
usedFactRoots: [],
|
|
414
|
+
pathReceiptRoots: [],
|
|
415
|
+
verificationReceipt,
|
|
416
|
+
receiptRoot: verificationReceipt.receiptRoot,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
const reasons = contractPolicyReasons(accepted, current, policy);
|
|
420
|
+
const compatibilityDecisions = [];
|
|
421
|
+
const currentSurfaces = new Map(
|
|
422
|
+
current.surfaces.map((entry) => [entry.id, entry]),
|
|
423
|
+
);
|
|
424
|
+
const proofRegistry = createBuildchainCompatibilityProofRegistry({
|
|
425
|
+
proofs: current.compatibilityProofs,
|
|
426
|
+
surfaces: current.surfaces,
|
|
427
|
+
majorLine: current.majorLine,
|
|
428
|
+
});
|
|
429
|
+
for (const oldSurface of accepted.surfaces || []) {
|
|
430
|
+
const result = evaluateSurfaceRelation({
|
|
431
|
+
oldSurface,
|
|
432
|
+
nextSurface: currentSurfaces.get(oldSurface.id),
|
|
433
|
+
proofRegistry,
|
|
434
|
+
majorLine: current.majorLine,
|
|
435
|
+
});
|
|
436
|
+
if (result.reason) reasons.push(result.reason);
|
|
437
|
+
if (result.decision) compatibilityDecisions.push(result.decision);
|
|
438
|
+
}
|
|
439
|
+
const compatible = reasons.length === 0;
|
|
440
|
+
const status = compatible ? "compatible-drift" : "breaking-drift";
|
|
441
|
+
const verificationReceipt = evaluationReceipt({
|
|
442
|
+
status,
|
|
443
|
+
compatible,
|
|
444
|
+
policy,
|
|
445
|
+
accepted,
|
|
446
|
+
current,
|
|
447
|
+
decisions: compatibilityDecisions,
|
|
448
|
+
reasons,
|
|
449
|
+
});
|
|
450
|
+
return {
|
|
451
|
+
ok: compatible,
|
|
452
|
+
status,
|
|
453
|
+
drift: shaDrift || contractDrift,
|
|
454
|
+
shaDrift,
|
|
455
|
+
contractDrift,
|
|
456
|
+
compatible,
|
|
457
|
+
issueRecommended: true,
|
|
458
|
+
policy,
|
|
459
|
+
reasons,
|
|
460
|
+
accepted,
|
|
461
|
+
current: contractSummary(current, runtimeRef, runtimeSha),
|
|
462
|
+
compatibilityDecisions,
|
|
463
|
+
usedProofRoots: verificationReceipt.usedProofRoots,
|
|
464
|
+
usedFactRoots: verificationReceipt.usedFactRoots,
|
|
465
|
+
pathReceiptRoots: verificationReceipt.pathReceiptRoots,
|
|
466
|
+
verificationReceipt,
|
|
467
|
+
receiptRoot: verificationReceipt.receiptRoot,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function shown(value, fallback = "(unknown)") {
|
|
472
|
+
return value ? value : fallback;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function renderCompatibilityDecision(decision) {
|
|
476
|
+
const lines = [
|
|
477
|
+
`- Surface \`${decision.surfaceId}\`: ${decision.status}`,
|
|
478
|
+
` - Relation: \`${shown(decision.sourceBreakingDigest, "(missing)")}\` -> \`${shown(decision.targetBreakingDigest, "(missing)")}\``,
|
|
479
|
+
` - Reason: \`${shown(decision.reason, "proof-backed-compatible-relation")}\``,
|
|
480
|
+
];
|
|
481
|
+
if (!decision.proofRoot) return lines.join("\n");
|
|
482
|
+
lines.push(
|
|
483
|
+
` - Proof root: \`${decision.proofRoot}\``,
|
|
484
|
+
` - Fact root: \`${decision.factRoot}\``,
|
|
485
|
+
` - Temporal path receipt: \`${decision.pathReceiptRoot}\``,
|
|
486
|
+
` - Direction / operation: \`${decision.direction}\` / \`${decision.operation}\``,
|
|
487
|
+
` - Scope root: \`${decision.scopeRoot}\``,
|
|
488
|
+
` - Evidence: ${shown(decision.evidence && decision.evidence.pullRequest)} (\`${decision.evidenceRoot}\`)`,
|
|
489
|
+
` - Authority: \`${shown(decision.authority && decision.authority.repository)}@${shown(decision.authority && decision.authority.protectedBase)}\` (\`${decision.authorityRoot}\`)`,
|
|
490
|
+
` - Cut: \`${shown(decision.cut && decision.cut.commit)}\` (\`${decision.cutRoot}\`)`,
|
|
491
|
+
);
|
|
492
|
+
return lines.join("\n");
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function renderCompatibilityDecisions(decisions) {
|
|
496
|
+
if (decisions.length === 0)
|
|
497
|
+
return "- No breaking-digest relation required proof resolution.";
|
|
498
|
+
return decisions.map(renderCompatibilityDecision).join("\n");
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
export function renderBuildchainContractDriftIssueBody({
|
|
502
|
+
repository = "",
|
|
503
|
+
workflow = "",
|
|
504
|
+
runUrl = "",
|
|
505
|
+
lockPath = "",
|
|
506
|
+
evaluation,
|
|
507
|
+
} = {}) {
|
|
508
|
+
const accepted = evaluation.accepted || {};
|
|
509
|
+
const current = evaluation.current || {};
|
|
510
|
+
const decisions = evaluation.compatibilityDecisions || [];
|
|
511
|
+
const renderedDecisions = renderCompatibilityDecisions(decisions);
|
|
512
|
+
return [
|
|
513
|
+
"# Buildchain contract drift",
|
|
514
|
+
"",
|
|
515
|
+
"## Summary",
|
|
516
|
+
"",
|
|
517
|
+
`Buildchain detected ${evaluation.compatible ? "compatible" : "breaking"} contract drift for a floating runtime ref before expensive Buildchain work continued.`,
|
|
518
|
+
"",
|
|
519
|
+
"## Consumer",
|
|
520
|
+
"",
|
|
521
|
+
`- Repository: ${shown(repository)}`,
|
|
522
|
+
`- Workflow: ${shown(workflow)}`,
|
|
523
|
+
`- Run: ${shown(runUrl)}`,
|
|
524
|
+
`- Lock path: ${shown(lockPath)}`,
|
|
525
|
+
"",
|
|
526
|
+
"## Accepted Buildchain contract",
|
|
527
|
+
"",
|
|
528
|
+
`- Ref: ${shown(accepted.ref)}`,
|
|
529
|
+
`- SHA: ${shown(accepted.resolvedSha)}`,
|
|
530
|
+
`- Contract digest: ${shown(accepted.contractDigest)}`,
|
|
531
|
+
`- Compatibility digest: ${shown(accepted.compatibilityDigest)}`,
|
|
532
|
+
`- Compatibility Fact registry: ${shown(accepted.compatibilityFactRegistryRoot, "(legacy lock without Fact registry)")}`,
|
|
533
|
+
`- Compatibility proof registry: ${shown(accepted.compatibilityProofRegistryRoot, "(legacy lock without proof registry)")}`,
|
|
534
|
+
`- Policy: ${shown(evaluation.policy || accepted.compatibilityPolicy)}`,
|
|
535
|
+
"",
|
|
536
|
+
"## Current Buildchain contract",
|
|
537
|
+
"",
|
|
538
|
+
`- Ref: ${shown(current.ref)}`,
|
|
539
|
+
`- SHA: ${shown(current.resolvedSha)}`,
|
|
540
|
+
`- Contract digest: ${shown(current.contractDigest)}`,
|
|
541
|
+
`- Compatibility digest: ${shown(current.compatibilityDigest)}`,
|
|
542
|
+
`- Compatibility Fact registry: ${shown(current.compatibilityFactRegistryRoot)}`,
|
|
543
|
+
`- Compatibility Fact Cut: ${shown(current.compatibilityFactCutRoot)}`,
|
|
544
|
+
`- Compatibility proof registry: ${shown(current.compatibilityProofRegistryRoot)}`,
|
|
545
|
+
`- Major line: ${shown(current.majorLine)}`,
|
|
546
|
+
"",
|
|
547
|
+
"## Compatibility",
|
|
548
|
+
"",
|
|
549
|
+
`- Status: ${shown(evaluation.status)}`,
|
|
550
|
+
`- Compatible: ${evaluation.compatible ? "yes" : "no"}`,
|
|
551
|
+
evaluation.reasons && evaluation.reasons.length
|
|
552
|
+
? evaluation.reasons.map((reason) => `- ${reason}`).join("\n")
|
|
553
|
+
: "- No breaking drift detected.",
|
|
554
|
+
"",
|
|
555
|
+
"## Proof-backed decisions",
|
|
556
|
+
"",
|
|
557
|
+
renderedDecisions,
|
|
558
|
+
`- Verification receipt: \`${shown(evaluation.receiptRoot, "(not produced)")}\``,
|
|
559
|
+
"",
|
|
560
|
+
"## Suggested next action",
|
|
561
|
+
"",
|
|
562
|
+
evaluation.compatible
|
|
563
|
+
? "Review the Buildchain release notes, then update the consumer contract lock to the current SHA and contract digest."
|
|
564
|
+
: "Failing before heavy build is intentional. Review the Buildchain contract change, update the consumer workflow/configuration, or pin the previous Buildchain SHA.",
|
|
565
|
+
].join("\n");
|
|
566
|
+
}
|
|
@@ -236,6 +236,16 @@ export function normalizeBuildchainConfig(config) {
|
|
|
236
236
|
if (normalized.publication !== undefined) {
|
|
237
237
|
normalized.publication = normalizePublicationSection(normalized.publication);
|
|
238
238
|
}
|
|
239
|
+
if (normalized.publication_rehearsal !== undefined) {
|
|
240
|
+
normalized.publication_rehearsal = normalizePublicationRehearsalSection(
|
|
241
|
+
normalized.publication_rehearsal,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
if (normalized.adopter_delivery !== undefined) {
|
|
245
|
+
normalized.adopter_delivery = normalizeAdopterDeliverySection(
|
|
246
|
+
normalized.adopter_delivery,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
239
249
|
if (normalized.consumers !== undefined) {
|
|
240
250
|
normalized.consumers = normalizeConsumersSection(normalized.consumers);
|
|
241
251
|
}
|
|
@@ -700,6 +710,80 @@ function normalizePublicationSection(publication) {
|
|
|
700
710
|
};
|
|
701
711
|
}
|
|
702
712
|
|
|
713
|
+
function normalizePublicationRehearsalSection(rehearsal) {
|
|
714
|
+
assertPlainObject(rehearsal, "publication_rehearsal");
|
|
715
|
+
const allowed = ["contract", "capsule_path", "candidate_root", "state_path", "evidence_path", "effect_default"];
|
|
716
|
+
const unknown = Object.keys(rehearsal).filter((key) => !allowed.includes(key));
|
|
717
|
+
if (unknown.length > 0) {
|
|
718
|
+
throw new Error(`publication_rehearsal contains unsupported fields: ${unknown.join(", ")}`);
|
|
719
|
+
}
|
|
720
|
+
const normalized = {
|
|
721
|
+
contract: assertString(rehearsal.contract, "publication_rehearsal.contract"),
|
|
722
|
+
capsulePath: posixPath(assertString(rehearsal.capsule_path, "publication_rehearsal.capsule_path")),
|
|
723
|
+
candidateRoot: posixPath(assertString(rehearsal.candidate_root, "publication_rehearsal.candidate_root")),
|
|
724
|
+
statePath: posixPath(assertString(rehearsal.state_path, "publication_rehearsal.state_path")),
|
|
725
|
+
evidencePath: posixPath(assertString(rehearsal.evidence_path, "publication_rehearsal.evidence_path")),
|
|
726
|
+
effectDefault: assertString(rehearsal.effect_default, "publication_rehearsal.effect_default"),
|
|
727
|
+
};
|
|
728
|
+
if (
|
|
729
|
+
normalized.contract !== "buildchain-v4-publication-rehearsal-capsule/v1" ||
|
|
730
|
+
normalized.effectDefault !== "disabled"
|
|
731
|
+
) {
|
|
732
|
+
throw new Error("publication_rehearsal must use the v4 capsule contract with effect_default = disabled");
|
|
733
|
+
}
|
|
734
|
+
for (const [key, value] of Object.entries({
|
|
735
|
+
capsule_path: normalized.capsulePath,
|
|
736
|
+
candidate_root: normalized.candidateRoot,
|
|
737
|
+
state_path: normalized.statePath,
|
|
738
|
+
evidence_path: normalized.evidencePath,
|
|
739
|
+
})) {
|
|
740
|
+
if (value.startsWith("/") || value.includes("\\") || value.split("/").includes("..")) {
|
|
741
|
+
throw new Error(`publication_rehearsal.${key} must be repository-relative`);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return normalized;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
function normalizeAdopterDeliverySection(delivery) {
|
|
748
|
+
assertPlainObject(delivery, "adopter_delivery");
|
|
749
|
+
const allowed = ["contract", "input_path", "readback_path", "bootstrap_path", "archive_path", "result_path", "driver_selector", "artifact_profile_selector"];
|
|
750
|
+
const unknown = Object.keys(delivery).filter((key) => !allowed.includes(key));
|
|
751
|
+
if (unknown.length > 0) {
|
|
752
|
+
throw new Error(`adopter_delivery contains unsupported fields: ${unknown.join(", ")}`);
|
|
753
|
+
}
|
|
754
|
+
const normalized = {
|
|
755
|
+
contract: assertString(delivery.contract, "adopter_delivery.contract"),
|
|
756
|
+
inputPath: posixPath(assertString(delivery.input_path, "adopter_delivery.input_path")),
|
|
757
|
+
readbackPath: posixPath(assertString(delivery.readback_path, "adopter_delivery.readback_path")),
|
|
758
|
+
bootstrapPath: posixPath(assertString(delivery.bootstrap_path, "adopter_delivery.bootstrap_path")),
|
|
759
|
+
archivePath: posixPath(assertString(delivery.archive_path, "adopter_delivery.archive_path")),
|
|
760
|
+
resultPath: posixPath(assertString(delivery.result_path, "adopter_delivery.result_path")),
|
|
761
|
+
driverSelector: assertString(delivery.driver_selector, "adopter_delivery.driver_selector"),
|
|
762
|
+
artifactProfileSelector: assertString(delivery.artifact_profile_selector, "adopter_delivery.artifact_profile_selector"),
|
|
763
|
+
};
|
|
764
|
+
if (normalized.contract !== "kungfu-buildchain-v4-adopter-delivery/v1") {
|
|
765
|
+
throw new Error("adopter_delivery contract is unsupported");
|
|
766
|
+
}
|
|
767
|
+
if (
|
|
768
|
+
!["json-assertion", "kfd-category", "legacy-kfd"].includes(normalized.driverSelector) ||
|
|
769
|
+
!["git-commit", "package"].includes(normalized.artifactProfileSelector)
|
|
770
|
+
) {
|
|
771
|
+
throw new Error("adopter_delivery selector is unsupported");
|
|
772
|
+
}
|
|
773
|
+
for (const [key, value] of Object.entries({
|
|
774
|
+
input_path: normalized.inputPath,
|
|
775
|
+
readback_path: normalized.readbackPath,
|
|
776
|
+
bootstrap_path: normalized.bootstrapPath,
|
|
777
|
+
archive_path: normalized.archivePath,
|
|
778
|
+
result_path: normalized.resultPath,
|
|
779
|
+
})) {
|
|
780
|
+
if (value.startsWith("/") || value.includes("\\") || value.split("/").includes("..")) {
|
|
781
|
+
throw new Error(`adopter_delivery.${key} must be repository-relative`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return normalized;
|
|
785
|
+
}
|
|
786
|
+
|
|
703
787
|
function normalizePublicationArchive(archive = undefined) {
|
|
704
788
|
if (archive === undefined) {
|
|
705
789
|
return undefined;
|