@kungfu-tech/buildchain 3.0.9-alpha.0 → 3.0.9-alpha.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/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +16 -0
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +57 -0
- package/contracts/release-cut-v1.schema.json +1 -0
- package/contracts/release-train-transition-v1.schema.json +1 -0
- package/dist/site/buildchain-contract.json +27 -28
- package/dist/site/buildchain-site.json +37 -27
- package/dist/site/controller-registry.json +8 -4
- package/dist/site/kfd-claims.json +3 -3
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +7 -7
- package/dist/site/node-api-registry.json +370 -73
- package/dist/site/page-registry.json +26 -16
- package/dist/site/public-surface-audit.json +2 -2
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +11 -11
- package/dist/site/workflow-registry.json +4 -4
- package/docs/dev-delivery-warrant.md +12 -2
- package/docs/node-api-reference.md +116 -106
- package/docs/release-governance.md +5 -3
- package/docs/release-passport.md +7 -0
- package/docs/release-train.md +46 -4
- package/docs/reusable-build-surface.md +26 -9
- package/docs/runtime-train-validation.md +6 -0
- package/docs/versioning.md +3 -2
- package/package.json +1 -1
- package/packages/core/buildchain-channel-identity.js +119 -0
- package/packages/core/buildchain-compatibility-proof.js +32 -0
- package/packages/core/buildchain-contract.js +7 -0
- package/packages/core/controller-evidence.js +2 -1
- package/packages/core/dev-delivery-warrant.js +15 -4
- package/packages/core/kfd-adopter-manifest.js +3 -3
- package/packages/core/release-blocker-priority.js +192 -0
- package/packages/core/release-train.js +220 -0
- package/scripts/buildchain-channel-router.mjs +18 -2
- package/scripts/buildchain-contract-lock.mjs +11 -1
- package/scripts/check-inventory.mjs +2 -2
- package/scripts/generate-buildchain-kfd-witnesses.mjs +88 -94
- package/scripts/generate-channel-build-workflow.mjs +78 -31
- package/scripts/promotion-channel-router.mjs +5 -3
- package/scripts/release-train-self-dogfood.mjs +579 -0
- package/scripts/runtime-ref-core.mjs +4 -17
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
devDeliveryContentRoot,
|
|
5
|
+
devDeliveryExactRoot as exactRoot,
|
|
6
|
+
devDeliveryExactSha as exactSha,
|
|
7
|
+
devDeliveryPositiveInteger as positiveInteger,
|
|
8
|
+
devDeliveryProtectedBase as protectedBase,
|
|
9
|
+
devDeliveryRepository as repository,
|
|
10
|
+
devDeliveryText as text,
|
|
11
|
+
devDeliveryTimestamp as timestamp,
|
|
12
|
+
} from "./dev-delivery-common.js";
|
|
13
|
+
import { validateReleaseBlockerRepair } from "./release-train.js";
|
|
14
|
+
|
|
15
|
+
export const RELEASE_BLOCKER_PRIORITY_CLAIM_SCHEMA =
|
|
16
|
+
"kungfu.buildchain.release-blocker-priority-claim/v1";
|
|
17
|
+
|
|
18
|
+
function exactFields(value, fields, label) {
|
|
19
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
20
|
+
throw new Error(`${label} must be an object`);
|
|
21
|
+
}
|
|
22
|
+
const actual = Object.keys(value).sort();
|
|
23
|
+
const expected = [...fields].sort();
|
|
24
|
+
if (
|
|
25
|
+
actual.length !== expected.length ||
|
|
26
|
+
actual.some((field, index) => field !== expected[index])
|
|
27
|
+
) {
|
|
28
|
+
throw new Error(`${label} has an invalid field set`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function createReleaseBlockerPriorityClaim(repairInput, input = {}) {
|
|
33
|
+
const repair = validateReleaseBlockerRepair(repairInput);
|
|
34
|
+
if (!repair.publication.eligible || repair.devLanding.status !== "landed") {
|
|
35
|
+
throw new Error(
|
|
36
|
+
"release-blocker priority requires an exact settled dev landing",
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
const body = {
|
|
40
|
+
schema: RELEASE_BLOCKER_PRIORITY_CLAIM_SCHEMA,
|
|
41
|
+
repository: repository(repair.successorTrain.releaseCut.repository),
|
|
42
|
+
protectedBase: protectedBase(repair.successorTrain.releaseCut.sourceBranch),
|
|
43
|
+
assignmentRoot: exactRoot(input.assignmentRoot, "assignmentRoot"),
|
|
44
|
+
initiativeRoot: exactRoot(input.initiativeRoot, "initiativeRoot"),
|
|
45
|
+
repairRoot: exactRoot(repair.repairRoot, "repairRoot"),
|
|
46
|
+
priorCutRoot: exactRoot(
|
|
47
|
+
repair.priorTrain.releaseCut.cutRoot,
|
|
48
|
+
"priorCutRoot",
|
|
49
|
+
),
|
|
50
|
+
successorCutRoot: exactRoot(
|
|
51
|
+
repair.successorTrain.releaseCut.cutRoot,
|
|
52
|
+
"successorCutRoot",
|
|
53
|
+
),
|
|
54
|
+
candidateGeneration: positiveInteger(
|
|
55
|
+
repair.successorTrain.releaseCut.generation,
|
|
56
|
+
"candidateGeneration",
|
|
57
|
+
),
|
|
58
|
+
cutCandidateSha: exactSha(repair.cutLanding.landedSha, "cutCandidateSha"),
|
|
59
|
+
sourceHead: exactSha(repair.devLanding.landedSha, "sourceHead"),
|
|
60
|
+
sourcePatchRoot: exactRoot(repair.patchRoot, "sourcePatchRoot"),
|
|
61
|
+
cutLandingEvidenceRoot: exactRoot(
|
|
62
|
+
repair.cutLanding.evidenceRoot,
|
|
63
|
+
"cutLandingEvidenceRoot",
|
|
64
|
+
),
|
|
65
|
+
devLandingEvidenceRoot: exactRoot(
|
|
66
|
+
repair.devLanding.evidenceRoot,
|
|
67
|
+
"devLandingEvidenceRoot",
|
|
68
|
+
),
|
|
69
|
+
publicationGateRoot: exactRoot(
|
|
70
|
+
repair.publication.gateRoot,
|
|
71
|
+
"publicationGateRoot",
|
|
72
|
+
),
|
|
73
|
+
issuedAt: timestamp(input.issuedAt, "issuedAt"),
|
|
74
|
+
};
|
|
75
|
+
return { ...body, claimRoot: devDeliveryContentRoot(body) };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function normalizeReleaseBlockerPriorityClaim(
|
|
79
|
+
input,
|
|
80
|
+
candidate,
|
|
81
|
+
expected,
|
|
82
|
+
) {
|
|
83
|
+
exactFields(
|
|
84
|
+
input,
|
|
85
|
+
[
|
|
86
|
+
"schema",
|
|
87
|
+
"repository",
|
|
88
|
+
"protectedBase",
|
|
89
|
+
"assignmentRoot",
|
|
90
|
+
"initiativeRoot",
|
|
91
|
+
"repairRoot",
|
|
92
|
+
"priorCutRoot",
|
|
93
|
+
"successorCutRoot",
|
|
94
|
+
"candidateGeneration",
|
|
95
|
+
"cutCandidateSha",
|
|
96
|
+
"sourceHead",
|
|
97
|
+
"sourcePatchRoot",
|
|
98
|
+
"cutLandingEvidenceRoot",
|
|
99
|
+
"devLandingEvidenceRoot",
|
|
100
|
+
"publicationGateRoot",
|
|
101
|
+
"issuedAt",
|
|
102
|
+
"claimRoot",
|
|
103
|
+
],
|
|
104
|
+
"releaseBlockerPriority",
|
|
105
|
+
);
|
|
106
|
+
const body = {
|
|
107
|
+
schema: text(input.schema),
|
|
108
|
+
repository: repository(input.repository),
|
|
109
|
+
protectedBase: protectedBase(input.protectedBase),
|
|
110
|
+
assignmentRoot: exactRoot(
|
|
111
|
+
input.assignmentRoot,
|
|
112
|
+
"releaseBlockerPriority.assignmentRoot",
|
|
113
|
+
),
|
|
114
|
+
initiativeRoot: exactRoot(
|
|
115
|
+
input.initiativeRoot,
|
|
116
|
+
"releaseBlockerPriority.initiativeRoot",
|
|
117
|
+
),
|
|
118
|
+
repairRoot: exactRoot(
|
|
119
|
+
input.repairRoot,
|
|
120
|
+
"releaseBlockerPriority.repairRoot",
|
|
121
|
+
),
|
|
122
|
+
priorCutRoot: exactRoot(
|
|
123
|
+
input.priorCutRoot,
|
|
124
|
+
"releaseBlockerPriority.priorCutRoot",
|
|
125
|
+
),
|
|
126
|
+
successorCutRoot: exactRoot(
|
|
127
|
+
input.successorCutRoot,
|
|
128
|
+
"releaseBlockerPriority.successorCutRoot",
|
|
129
|
+
),
|
|
130
|
+
candidateGeneration: positiveInteger(
|
|
131
|
+
input.candidateGeneration,
|
|
132
|
+
"releaseBlockerPriority.candidateGeneration",
|
|
133
|
+
),
|
|
134
|
+
cutCandidateSha: exactSha(
|
|
135
|
+
input.cutCandidateSha,
|
|
136
|
+
"releaseBlockerPriority.cutCandidateSha",
|
|
137
|
+
),
|
|
138
|
+
sourceHead: exactSha(input.sourceHead, "releaseBlockerPriority.sourceHead"),
|
|
139
|
+
sourcePatchRoot: exactRoot(
|
|
140
|
+
input.sourcePatchRoot,
|
|
141
|
+
"releaseBlockerPriority.sourcePatchRoot",
|
|
142
|
+
),
|
|
143
|
+
cutLandingEvidenceRoot: exactRoot(
|
|
144
|
+
input.cutLandingEvidenceRoot,
|
|
145
|
+
"releaseBlockerPriority.cutLandingEvidenceRoot",
|
|
146
|
+
),
|
|
147
|
+
devLandingEvidenceRoot: exactRoot(
|
|
148
|
+
input.devLandingEvidenceRoot,
|
|
149
|
+
"releaseBlockerPriority.devLandingEvidenceRoot",
|
|
150
|
+
),
|
|
151
|
+
publicationGateRoot: exactRoot(
|
|
152
|
+
input.publicationGateRoot,
|
|
153
|
+
"releaseBlockerPriority.publicationGateRoot",
|
|
154
|
+
),
|
|
155
|
+
issuedAt: timestamp(input.issuedAt, "releaseBlockerPriority.issuedAt"),
|
|
156
|
+
};
|
|
157
|
+
if (body.schema !== RELEASE_BLOCKER_PRIORITY_CLAIM_SCHEMA) {
|
|
158
|
+
throw new Error("releaseBlockerPriority schema is unsupported");
|
|
159
|
+
}
|
|
160
|
+
if (input.claimRoot !== devDeliveryContentRoot(body)) {
|
|
161
|
+
throw new Error("releaseBlockerPriority claimRoot drift");
|
|
162
|
+
}
|
|
163
|
+
if (
|
|
164
|
+
body.repository !== expected.repository ||
|
|
165
|
+
body.protectedBase !== expected.protectedBase
|
|
166
|
+
) {
|
|
167
|
+
throw new Error("releaseBlockerPriority queue route mismatch");
|
|
168
|
+
}
|
|
169
|
+
if (
|
|
170
|
+
body.assignmentRoot !== candidate.assignmentRoot ||
|
|
171
|
+
body.initiativeRoot !== candidate.initiativeRoot
|
|
172
|
+
) {
|
|
173
|
+
throw new Error("releaseBlockerPriority Work identity mismatch");
|
|
174
|
+
}
|
|
175
|
+
if (
|
|
176
|
+
body.sourceHead !== candidate.sourceHead ||
|
|
177
|
+
body.sourcePatchRoot !== candidate.sourcePatchRoot
|
|
178
|
+
) {
|
|
179
|
+
throw new Error("releaseBlockerPriority source identity mismatch");
|
|
180
|
+
}
|
|
181
|
+
return { ...body, claimRoot: input.claimRoot };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function compareReleaseBlockerPriority(left, right) {
|
|
185
|
+
const leftBlocker =
|
|
186
|
+
left.priority.releaseBlocker && left.candidate.priority === "ordinary";
|
|
187
|
+
const rightBlocker =
|
|
188
|
+
right.priority.releaseBlocker && right.candidate.priority === "ordinary";
|
|
189
|
+
if (leftBlocker === rightBlocker) return 0;
|
|
190
|
+
const peer = leftBlocker ? right : left;
|
|
191
|
+
return peer.candidate.priority === "ordinary" ? (leftBlocker ? -1 : 1) : 0;
|
|
192
|
+
}
|
|
@@ -8,6 +8,8 @@ export const RELEASE_TRAIN_TRANSITION_CONTRACT =
|
|
|
8
8
|
"kungfu-buildchain-release-train-transition/v1";
|
|
9
9
|
export const RELEASE_TRAIN_OBSERVATION_CONTRACT =
|
|
10
10
|
"kungfu-buildchain-release-train-observation/v1";
|
|
11
|
+
export const RELEASE_BLOCKER_REPAIR_CONTRACT =
|
|
12
|
+
"kungfu-buildchain-release-blocker-repair/v1";
|
|
11
13
|
export const LEGACY_DEV_ALPHA_CANDIDATE_STATE_SCHEMA =
|
|
12
14
|
"kungfu-buildchain-dev-alpha-candidate-state/v1";
|
|
13
15
|
|
|
@@ -22,6 +24,7 @@ export const RELEASE_TRAIN_STATES = Object.freeze([
|
|
|
22
24
|
]);
|
|
23
25
|
|
|
24
26
|
export const RELEASE_TRAIN_SUPERSESSION_CAUSES = Object.freeze([
|
|
27
|
+
"release-blocker-repair",
|
|
25
28
|
"incompatible-semantics",
|
|
26
29
|
"alpha-base-incompatibility",
|
|
27
30
|
"invalid-authority",
|
|
@@ -518,3 +521,220 @@ export function readReleaseTrain(input) {
|
|
|
518
521
|
}
|
|
519
522
|
throw new Error("unsupported Release Train record");
|
|
520
523
|
}
|
|
524
|
+
|
|
525
|
+
function releaseBlockerLanding(input, label, { allowConflict = false } = {}) {
|
|
526
|
+
const status = text(input?.status, `${label}.status`);
|
|
527
|
+
if (status !== "landed" && !(allowConflict && status === "conflict")) {
|
|
528
|
+
throw new Error(
|
|
529
|
+
`${label}.status must be ${allowConflict ? "landed or conflict" : "landed"}`,
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
const landing = {
|
|
533
|
+
status,
|
|
534
|
+
baseSha: exactSha(input.baseSha, `${label}.baseSha`),
|
|
535
|
+
landedSha:
|
|
536
|
+
status === "landed"
|
|
537
|
+
? exactSha(input.landedSha, `${label}.landedSha`)
|
|
538
|
+
: "",
|
|
539
|
+
patchRoot: contentRoot(input.patchRoot, `${label}.patchRoot`),
|
|
540
|
+
evidenceRoot: contentRoot(input.evidenceRoot, `${label}.evidenceRoot`),
|
|
541
|
+
};
|
|
542
|
+
return landing;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function releaseBlockerPublication(patchRoot, devLanding) {
|
|
546
|
+
const eligible =
|
|
547
|
+
devLanding.status === "landed" && devLanding.patchRoot === patchRoot;
|
|
548
|
+
const body = {
|
|
549
|
+
eligible,
|
|
550
|
+
reason:
|
|
551
|
+
devLanding.status === "conflict"
|
|
552
|
+
? "dev-forward-port-conflict"
|
|
553
|
+
: eligible
|
|
554
|
+
? "exact-cut-dev-patch-settled"
|
|
555
|
+
: "cut-dev-patch-root-mismatch",
|
|
556
|
+
requiredPatchRoot: patchRoot,
|
|
557
|
+
observedDevPatchRoot: devLanding.patchRoot,
|
|
558
|
+
devLandingStatus: devLanding.status,
|
|
559
|
+
};
|
|
560
|
+
return { ...body, gateRoot: releaseTrainRoot(body) };
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function buildReleaseBlockerRepair(activeTrainInput, input = {}) {
|
|
564
|
+
const activeTrain = validateReleaseTrain(activeTrainInput);
|
|
565
|
+
if (activeTrain.state.status !== "repair-required") {
|
|
566
|
+
throw new Error(
|
|
567
|
+
"release-blocker repair requires the active train to be repair-required",
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
const expectedStateRoot = contentRoot(
|
|
571
|
+
input.expectedStateRoot,
|
|
572
|
+
"expectedStateRoot",
|
|
573
|
+
);
|
|
574
|
+
if (expectedStateRoot !== activeTrain.state.stateRoot) {
|
|
575
|
+
throw new Error("release-blocker repair compare-and-swap failed");
|
|
576
|
+
}
|
|
577
|
+
const patchRoot = contentRoot(input.patchRoot, "patchRoot");
|
|
578
|
+
const blockerRoot = contentRoot(input.blockerRoot, "blockerRoot");
|
|
579
|
+
const authorityRoots = sortedRoots(input.authorityRoots, "authorityRoots");
|
|
580
|
+
const createdAt = timestamp(input.createdAt, "createdAt");
|
|
581
|
+
const cutLanding = releaseBlockerLanding(
|
|
582
|
+
{
|
|
583
|
+
status: "landed",
|
|
584
|
+
baseSha: activeTrain.releaseCut.candidateSha,
|
|
585
|
+
landedSha: input.cutCandidateSha,
|
|
586
|
+
patchRoot: input.cutPatchRoot,
|
|
587
|
+
evidenceRoot: input.cutLandingEvidenceRoot,
|
|
588
|
+
},
|
|
589
|
+
"cutLanding",
|
|
590
|
+
);
|
|
591
|
+
if (cutLanding.patchRoot !== patchRoot) {
|
|
592
|
+
throw new Error(
|
|
593
|
+
"cut landing must preserve the declared release-blocker patch root",
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
const devLanding = releaseBlockerLanding(
|
|
597
|
+
{
|
|
598
|
+
status: input.devLandingStatus,
|
|
599
|
+
baseSha: input.devBaseSha,
|
|
600
|
+
landedSha: input.devLandingSha,
|
|
601
|
+
patchRoot: input.devPatchRoot,
|
|
602
|
+
evidenceRoot: input.devLandingEvidenceRoot,
|
|
603
|
+
},
|
|
604
|
+
"devLanding",
|
|
605
|
+
{ allowConflict: true },
|
|
606
|
+
);
|
|
607
|
+
const priorCut = activeTrain.releaseCut;
|
|
608
|
+
const successorTrain = createReleaseTrain({
|
|
609
|
+
repository: priorCut.repository,
|
|
610
|
+
sourceBranch: priorCut.sourceBranch,
|
|
611
|
+
targetBranch: priorCut.targetBranch,
|
|
612
|
+
originDevSha: priorCut.originDevSha,
|
|
613
|
+
candidateSha: cutLanding.landedSha,
|
|
614
|
+
candidateTreeSha: exactSha(
|
|
615
|
+
input.cutCandidateTreeSha,
|
|
616
|
+
"cutCandidateTreeSha",
|
|
617
|
+
),
|
|
618
|
+
alphaBaseSha: priorCut.alphaBaseSha,
|
|
619
|
+
buildchainRuntimeSha: priorCut.buildchainRuntimeSha,
|
|
620
|
+
generation: priorCut.generation + 1,
|
|
621
|
+
authorityRoots: [
|
|
622
|
+
...new Set([...priorCut.authorityRoots, ...authorityRoots]),
|
|
623
|
+
].sort(),
|
|
624
|
+
supersession: {
|
|
625
|
+
cause: "release-blocker-repair",
|
|
626
|
+
priorCutRoot: priorCut.cutRoot,
|
|
627
|
+
},
|
|
628
|
+
createdAt,
|
|
629
|
+
});
|
|
630
|
+
const supersededTrain = transitionReleaseTrain(activeTrain, {
|
|
631
|
+
to: "superseded",
|
|
632
|
+
expectedStateRoot,
|
|
633
|
+
event: "release-blocker-successor-cut",
|
|
634
|
+
reason:
|
|
635
|
+
"a rooted release-blocker repair advanced the active candidate generation",
|
|
636
|
+
authorityRoots,
|
|
637
|
+
supersessionCause: "release-blocker-repair",
|
|
638
|
+
replacementCutRoot: successorTrain.releaseCut.cutRoot,
|
|
639
|
+
replacementCandidateSha: successorTrain.releaseCut.candidateSha,
|
|
640
|
+
recordedAt: createdAt,
|
|
641
|
+
});
|
|
642
|
+
const publication = releaseBlockerPublication(patchRoot, devLanding);
|
|
643
|
+
const body = {
|
|
644
|
+
schemaVersion: 1,
|
|
645
|
+
contract: RELEASE_BLOCKER_REPAIR_CONTRACT,
|
|
646
|
+
blockerRoot,
|
|
647
|
+
patchRoot,
|
|
648
|
+
priorTrain: activeTrain,
|
|
649
|
+
supersededTrain,
|
|
650
|
+
successorTrain,
|
|
651
|
+
cutLanding,
|
|
652
|
+
devLanding,
|
|
653
|
+
candidateBuild: {
|
|
654
|
+
eligible: true,
|
|
655
|
+
candidateSha: successorTrain.releaseCut.candidateSha,
|
|
656
|
+
candidateTreeSha: successorTrain.releaseCut.candidateTreeSha,
|
|
657
|
+
generation: successorTrain.releaseCut.generation,
|
|
658
|
+
cutRoot: successorTrain.releaseCut.cutRoot,
|
|
659
|
+
reason: "rooted-cut-landing",
|
|
660
|
+
},
|
|
661
|
+
publication,
|
|
662
|
+
authorityRoots,
|
|
663
|
+
createdAt,
|
|
664
|
+
};
|
|
665
|
+
return { ...body, repairRoot: releaseTrainRoot(body) };
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
export function createReleaseBlockerRepair(activeTrainInput, input = {}) {
|
|
669
|
+
return buildReleaseBlockerRepair(activeTrainInput, input);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
export function validateReleaseBlockerRepair(input) {
|
|
673
|
+
if (
|
|
674
|
+
input?.contract !== RELEASE_BLOCKER_REPAIR_CONTRACT ||
|
|
675
|
+
Number(input?.schemaVersion) !== 1
|
|
676
|
+
) {
|
|
677
|
+
throw new Error(
|
|
678
|
+
`release-blocker repair must use ${RELEASE_BLOCKER_REPAIR_CONTRACT}`,
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
const rebuilt = buildReleaseBlockerRepair(input.priorTrain, {
|
|
682
|
+
expectedStateRoot: input.priorTrain?.state?.stateRoot,
|
|
683
|
+
blockerRoot: input.blockerRoot,
|
|
684
|
+
patchRoot: input.patchRoot,
|
|
685
|
+
cutCandidateSha: input.cutLanding?.landedSha,
|
|
686
|
+
cutCandidateTreeSha: input.successorTrain?.releaseCut?.candidateTreeSha,
|
|
687
|
+
cutPatchRoot: input.cutLanding?.patchRoot,
|
|
688
|
+
cutLandingEvidenceRoot: input.cutLanding?.evidenceRoot,
|
|
689
|
+
devLandingStatus: input.devLanding?.status,
|
|
690
|
+
devBaseSha: input.devLanding?.baseSha,
|
|
691
|
+
devLandingSha: input.devLanding?.landedSha,
|
|
692
|
+
devPatchRoot: input.devLanding?.patchRoot,
|
|
693
|
+
devLandingEvidenceRoot: input.devLanding?.evidenceRoot,
|
|
694
|
+
authorityRoots: input.authorityRoots,
|
|
695
|
+
createdAt: input.createdAt,
|
|
696
|
+
});
|
|
697
|
+
if (input.repairRoot !== rebuilt.repairRoot) {
|
|
698
|
+
throw new Error("release-blocker repair root does not match its content");
|
|
699
|
+
}
|
|
700
|
+
if (releaseTrainRoot(input) !== releaseTrainRoot(rebuilt)) {
|
|
701
|
+
throw new Error(
|
|
702
|
+
"release-blocker repair content drifted from its rooted contract",
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
return clone(input);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
export function settleReleaseBlockerDevLanding(repairInput, input = {}) {
|
|
709
|
+
const repair = validateReleaseBlockerRepair(repairInput);
|
|
710
|
+
if (
|
|
711
|
+
contentRoot(input.expectedRepairRoot, "expectedRepairRoot") !==
|
|
712
|
+
repair.repairRoot
|
|
713
|
+
) {
|
|
714
|
+
throw new Error(
|
|
715
|
+
"release-blocker repair settlement compare-and-swap failed",
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
const patchRoot = contentRoot(input.patchRoot, "patchRoot");
|
|
719
|
+
if (patchRoot !== repair.patchRoot) {
|
|
720
|
+
throw new Error(
|
|
721
|
+
"dev landing settlement must preserve the release-blocker patch root",
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
return buildReleaseBlockerRepair(repair.priorTrain, {
|
|
725
|
+
expectedStateRoot: repair.priorTrain.state.stateRoot,
|
|
726
|
+
blockerRoot: repair.blockerRoot,
|
|
727
|
+
patchRoot: repair.patchRoot,
|
|
728
|
+
cutCandidateSha: repair.cutLanding.landedSha,
|
|
729
|
+
cutCandidateTreeSha: repair.successorTrain.releaseCut.candidateTreeSha,
|
|
730
|
+
cutPatchRoot: repair.cutLanding.patchRoot,
|
|
731
|
+
cutLandingEvidenceRoot: repair.cutLanding.evidenceRoot,
|
|
732
|
+
devLandingStatus: "landed",
|
|
733
|
+
devBaseSha: input.devBaseSha,
|
|
734
|
+
devLandingSha: input.devLandingSha,
|
|
735
|
+
devPatchRoot: patchRoot,
|
|
736
|
+
devLandingEvidenceRoot: input.devLandingEvidenceRoot,
|
|
737
|
+
authorityRoots: repair.authorityRoots,
|
|
738
|
+
createdAt: repair.createdAt,
|
|
739
|
+
});
|
|
740
|
+
}
|
|
@@ -105,8 +105,24 @@ export function resolveBuildchainChannel({
|
|
|
105
105
|
if (channel !== "auto" && explicitRef.kind !== "override" && channel !== explicitRef.kind) {
|
|
106
106
|
throw new Error(`buildchain-channel=${channel} conflicts with buildchain-ref=${explicitRef.ref}`);
|
|
107
107
|
}
|
|
108
|
-
if (
|
|
109
|
-
|
|
108
|
+
if (explicitRef.kind === "override") {
|
|
109
|
+
const lane = resolveBuildchainChannel({
|
|
110
|
+
requestedChannel: channel,
|
|
111
|
+
requestedRef: "",
|
|
112
|
+
publishChannel,
|
|
113
|
+
eventName,
|
|
114
|
+
gitRef,
|
|
115
|
+
releasePrerelease,
|
|
116
|
+
routerRef: `v${major}`,
|
|
117
|
+
packageVersion,
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
...lane,
|
|
121
|
+
major,
|
|
122
|
+
buildchainRef: explicitRef.ref,
|
|
123
|
+
selectionSource: "explicit-buildchain-ref+channel-evidence",
|
|
124
|
+
reason: `trusted runtime override ${explicitRef.ref} bound to ${lane.channel}`,
|
|
125
|
+
};
|
|
110
126
|
}
|
|
111
127
|
return {
|
|
112
128
|
channel: explicitRef.kind,
|
|
@@ -69,6 +69,10 @@ export function checkBuildchainContractLock({
|
|
|
69
69
|
runtimeSha = env("BUILDCHAIN_RUNTIME_SHA"),
|
|
70
70
|
runtimeClass = env("BUILDCHAIN_RUNTIME_CLASS"),
|
|
71
71
|
compatibilityPolicy = env("BUILDCHAIN_CONTRACT_COMPATIBILITY_POLICY"),
|
|
72
|
+
workflowShellRef = env("BUILDCHAIN_WORKFLOW_SHELL_REF"),
|
|
73
|
+
expectedChannel = env("BUILDCHAIN_EXPECTED_CHANNEL"),
|
|
74
|
+
expectedMajor = env("BUILDCHAIN_EXPECTED_MAJOR"),
|
|
75
|
+
allowOpaqueRuntime = boolEnv("BUILDCHAIN_ALLOW_OPAQUE_RUNTIME"),
|
|
72
76
|
issueMode = env("BUILDCHAIN_CONTRACT_DRIFT_ISSUE_MODE", "compatible-and-breaking"),
|
|
73
77
|
issueBodyPath = env("BUILDCHAIN_CONTRACT_DRIFT_ISSUE_BODY", ".buildchain/contract-drift/issue-body.md"),
|
|
74
78
|
repository = env("GITHUB_REPOSITORY"),
|
|
@@ -84,6 +88,10 @@ export function checkBuildchainContractLock({
|
|
|
84
88
|
runtimeSha,
|
|
85
89
|
runtimeClass,
|
|
86
90
|
compatibilityPolicy,
|
|
91
|
+
workflowShellRef,
|
|
92
|
+
expectedChannel,
|
|
93
|
+
expectedMajor,
|
|
94
|
+
allowOpaqueRuntime,
|
|
87
95
|
});
|
|
88
96
|
const shouldIssue = issueModeAllows(issueMode, evaluation);
|
|
89
97
|
if (shouldIssue) {
|
|
@@ -104,6 +112,8 @@ export function checkBuildchainContractLock({
|
|
|
104
112
|
`- Compatible: \`${evaluation.compatible ? "true" : "false"}\``,
|
|
105
113
|
`- Runtime ref: \`${runtimeRef || "(unknown)"}\``,
|
|
106
114
|
`- Runtime SHA: \`${runtimeSha || "(unknown)"}\``,
|
|
115
|
+
`- Workflow shell ref: \`${workflowShellRef || "(unknown)"}\``,
|
|
116
|
+
evaluation.channelBinding ? `- Bound channel: \`${evaluation.channelBinding.channel || "(unknown)"}\`` : "",
|
|
107
117
|
`- Contract digest: \`${current.contractDigest}\``,
|
|
108
118
|
`- Compatibility digest: \`${current.compatibilityDigest}\``,
|
|
109
119
|
`- Compatibility proof registry: \`${current.compatibilityProofRegistryRoot || "(legacy)"}\``,
|
|
@@ -128,7 +138,7 @@ export function checkBuildchainContractLock({
|
|
|
128
138
|
"current-buildchain-sha": runtimeSha || "",
|
|
129
139
|
});
|
|
130
140
|
if (!evaluation.ok) {
|
|
131
|
-
throw new Error(`Buildchain contract
|
|
141
|
+
throw new Error(`Buildchain contract lock rejected: ${(evaluation.reasons || []).join("; ")}`);
|
|
132
142
|
}
|
|
133
143
|
return { evaluation, current, shouldIssue };
|
|
134
144
|
}
|
|
@@ -215,7 +215,7 @@ const selfDogfoodAlphaEvaluation = evaluateBuildchainContractLock({
|
|
|
215
215
|
}),
|
|
216
216
|
runtimeRef: `v${selfDogfoodMajor}-alpha`,
|
|
217
217
|
runtimeSha: "current-development-contract",
|
|
218
|
-
runtimeClass: "alpha",
|
|
218
|
+
runtimeClass: "alpha", workflowShellRef: `v${selfDogfoodMajor}-alpha`,
|
|
219
219
|
});
|
|
220
220
|
if (
|
|
221
221
|
!canAdmitSelfDogfoodLockEvaluation({
|
|
@@ -304,7 +304,7 @@ if (!promotionOverrideAuthorization.includes("promotion runtime override is only
|
|
|
304
304
|
}
|
|
305
305
|
for (const requiredSnippet of [
|
|
306
306
|
"buildchain-channel:",
|
|
307
|
-
"uses:
|
|
307
|
+
"uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v3-alpha", "uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v3\n",
|
|
308
308
|
"needs.resolve-channel.outputs.buildchain-ref",
|
|
309
309
|
"needs.resolve-channel.outputs.contract-lock-path",
|
|
310
310
|
]) {
|