@kungfu-tech/buildchain 3.0.8 → 3.0.9-alpha.1

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.
Files changed (79) hide show
  1. package/actions/promote-buildchain-ref/README.md +8 -7
  2. package/bin/buildchain.mjs +34 -33
  3. package/bin/internal/trust-release-release-handlers.mjs +5 -0
  4. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +16 -0
  5. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +57 -0
  6. package/contracts/release-cut-v1.schema.json +1 -0
  7. package/contracts/release-train-transition-v1.schema.json +1 -0
  8. package/dist/site/agent-index.json +0 -1
  9. package/dist/site/artifact-schemas.json +0 -2
  10. package/dist/site/badge-endpoint-registry.json +9 -9
  11. package/dist/site/badges/v1/kfd-8/aligned.json +1 -1
  12. package/dist/site/badges/v1/kfd-8/declared.json +1 -1
  13. package/dist/site/badges/v1/kfd-8/downgraded.json +1 -1
  14. package/dist/site/badges/v1/kfd-8/draft.json +1 -1
  15. package/dist/site/badges/v1/kfd-8/failed.json +1 -1
  16. package/dist/site/badges/v1/kfd-8/missing.json +1 -1
  17. package/dist/site/badges/v1/kfd-8/passed.json +1 -1
  18. package/dist/site/badges/v1/kfd-8/planned.json +1 -1
  19. package/dist/site/buildchain-contract.json +38 -34
  20. package/dist/site/buildchain-site.json +59 -39
  21. package/dist/site/capability-registry.json +1 -1
  22. package/dist/site/cli-registry.json +8 -7
  23. package/dist/site/controller-registry.json +13 -5
  24. package/dist/site/kfd-claims.json +35 -8
  25. package/dist/site/kfd-upstream-aggregate.json +10 -10
  26. package/dist/site/manual-registry.json +9 -9
  27. package/dist/site/node-api-registry.json +1143 -320
  28. package/dist/site/page-registry.json +46 -26
  29. package/dist/site/public-surface-audit.json +19 -9
  30. package/dist/site/publication-registry.json +4 -4
  31. package/dist/site/release-passport-check-manifest.json +33 -5
  32. package/dist/site/release-provenance.json +2 -1
  33. package/dist/site/schemas/release-passport-v1.schema.json +3 -0
  34. package/dist/site/site-manifest.json +13 -13
  35. package/dist/site/workflow-registry.json +19 -9
  36. package/docs/aws-us-elastic-runner-burst-plane.md +15 -3
  37. package/docs/cli-reference.md +6 -6
  38. package/docs/cli.md +2 -3
  39. package/docs/dev-alpha-candidate-patrol.md +45 -20
  40. package/docs/dev-delivery-warrant.md +12 -2
  41. package/docs/kfd-support.md +7 -7
  42. package/docs/node-api-reference.md +295 -246
  43. package/docs/release-passport.md +25 -12
  44. package/docs/release-train.md +54 -4
  45. package/docs/reusable-build-surface.md +26 -9
  46. package/docs/runtime-train-validation.md +6 -0
  47. package/docs/versioning.md +3 -2
  48. package/package.json +4 -3
  49. package/packages/core/artifact-verification-envelope.js +114 -0
  50. package/packages/core/buildchain-channel-identity.js +119 -0
  51. package/packages/core/buildchain-compatibility-proof.js +32 -0
  52. package/packages/core/buildchain-contract.js +7 -0
  53. package/packages/core/controller-evidence.js +2 -1
  54. package/packages/core/dev-alpha-active-release-train.js +460 -0
  55. package/packages/core/dev-alpha-candidate-selection.js +181 -0
  56. package/packages/core/dev-delivery-warrant.js +15 -4
  57. package/packages/core/index.js +4 -5
  58. package/packages/core/kfd-adopter-manifest.js +390 -0
  59. package/packages/core/kfd-product-gates.js +0 -300
  60. package/packages/core/release-blocker-priority.js +192 -0
  61. package/packages/core/release-passport-contract.js +219 -6
  62. package/packages/core/release-passport.js +139 -242
  63. package/packages/core/release-train.js +220 -0
  64. package/scripts/aws-macos-jit-controller-core.mjs +76 -0
  65. package/scripts/aws-macos-jit-controller.mjs +29 -0
  66. package/scripts/aws-macos-jit-source-rebind.mjs +503 -0
  67. package/scripts/buildchain-channel-router.mjs +18 -2
  68. package/scripts/buildchain-cli-help.mjs +4 -2
  69. package/scripts/buildchain-contract-lock.mjs +11 -1
  70. package/scripts/check-inventory.mjs +4 -4
  71. package/scripts/dev-alpha-candidate-patrol.mjs +196 -201
  72. package/scripts/generate-buildchain-kfd-witnesses.mjs +88 -94
  73. package/scripts/generate-channel-build-workflow.mjs +78 -31
  74. package/scripts/generate-site-bundle.mjs +1 -8
  75. package/scripts/promotion-channel-router.mjs +5 -3
  76. package/scripts/release-train-self-dogfood.mjs +579 -0
  77. package/scripts/runtime-ref-core.mjs +4 -17
  78. package/scripts/site-capability-metadata.mjs +3 -1
  79. package/dist/site/schemas/kfd-support-projection-v1.schema.json +0 -106
@@ -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
+ }
@@ -56,6 +56,17 @@ function source(values) {
56
56
  };
57
57
  }
58
58
 
59
+ function previousSource(values) {
60
+ return {
61
+ sha: exactSha(values.previousSourceSha),
62
+ ref: exact(
63
+ values.previousSourceRef || values.sourceRef,
64
+ /^refs\/heads\/[A-Za-z0-9._/-]+$/,
65
+ "previousSourceRef",
66
+ ),
67
+ };
68
+ }
69
+
59
70
  function campaignId(value) {
60
71
  return exact(value, /^[a-z0-9][a-z0-9-]{2,47}$/, "campaignId");
61
72
  }
@@ -192,6 +203,71 @@ export function createMacosJitCampaignPlan(values = {}) {
192
203
  return { ...plan, digest: digest(plan) };
193
204
  }
194
205
 
206
+ export function createMacosJitSourceRebindPlan(values = {}) {
207
+ const id = campaignId(values.campaignId);
208
+ const boundSource = source(values);
209
+ const priorSource = previousSource(values);
210
+ if (boundSource.sha === priorSource.sha) {
211
+ throw new Error("sourceSha must differ from previousSourceSha");
212
+ }
213
+ if (boundSource.ref !== priorSource.ref) {
214
+ throw new Error("sourceRef must remain unchanged during campaign rebind");
215
+ }
216
+ const aws = commonAws(values);
217
+ const regionConfig = macosJitRegionConfig(aws.region);
218
+ const workflowId = exact(values.workflowId, /^\d+$/, "workflowId");
219
+ if (workflowId !== MACOS_EC2_JIT.workflowId) {
220
+ throw new Error(`workflowId must be ${MACOS_EC2_JIT.workflowId}`);
221
+ }
222
+ const plan = {
223
+ schemaVersion: 1,
224
+ contract: AWS_MACOS_JIT_CONTROLLER_CONTRACT,
225
+ kind: "campaign-source-rebind-plan",
226
+ repository: repository(values.repository),
227
+ account: {
228
+ id: exact(values.accountId, /^\d{12}$/, "accountId"),
229
+ },
230
+ campaign: { id },
231
+ previousSource: priorSource,
232
+ source: boundSource,
233
+ github: {
234
+ workflowId,
235
+ requiredState: "disabled_manually",
236
+ },
237
+ aws: {
238
+ ...aws,
239
+ hostId: exact(values.hostId, /^h-[0-9a-f]+$/, "hostId"),
240
+ instanceId: exact(values.instanceId, /^i-[0-9a-f]+$/, "instanceId"),
241
+ },
242
+ safety: {
243
+ applyMode: values.execute === true ? "execute" : "dry-run",
244
+ noAllocation: true,
245
+ noDispatch: true,
246
+ forwardOnlySource: true,
247
+ sameSourceRefRequired: true,
248
+ workflowDisabledRequired: true,
249
+ zeroPriorJobsRequired: true,
250
+ zeroPriorArtifactsRequired: true,
251
+ zeroJitResidueRequired: true,
252
+ zeroEvidenceRequired: true,
253
+ exactCampaignResourcesRequired: true,
254
+ compensatedRollbackRequired: true,
255
+ budget: {
256
+ name: regionConfig.budgetName,
257
+ limitUsd: MACOS_EC2_JIT.budgetLimitUsd,
258
+ metrics: ["UnblendedCost"],
259
+ dimensionFilter: {
260
+ usageTypes: regionConfig.budgetUsageTypes,
261
+ operation: MACOS_EC2_JIT.budgetOperation,
262
+ regions: regionConfig.budgetRegions,
263
+ },
264
+ requiredActualThresholds: [80, 95],
265
+ },
266
+ },
267
+ };
268
+ return { ...plan, digest: digest(plan) };
269
+ }
270
+
195
271
  export function macosAllocateHostsArgs(plan) {
196
272
  if (
197
273
  plan?.contract !== AWS_MACOS_JIT_CONTROLLER_CONTRACT ||
@@ -7,12 +7,14 @@ import {
7
7
  createMacosJitCampaignPlan,
8
8
  createMacosJitClosePlan,
9
9
  createMacosJitJobPlan,
10
+ createMacosJitSourceRebindPlan,
10
11
  macosAllocateHostsArgs,
11
12
  macosReleaseHostsArgs,
12
13
  macosRunInstancesArgs,
13
14
  } from "./aws-macos-jit-controller-core.mjs";
14
15
  import { MACOS_EC2_JIT_REGIONS } from "./aws-macos-jit-core.mjs";
15
16
  import { executeMacosJitJob } from "./aws-macos-jit-job-controller.mjs";
17
+ import { executeMacosJitSourceRebind } from "./aws-macos-jit-source-rebind.mjs";
16
18
  import {
17
19
  assertDryRun,
18
20
  assertAllowedPolicySimulation,
@@ -498,6 +500,22 @@ function confirm(plan) {
498
500
  if (arg("confirm-campaign-id") !== plan.campaign.id) {
499
501
  throw new Error("--confirm-campaign-id must equal the campaign id");
500
502
  }
503
+ if (plan.kind === "campaign-source-rebind-plan") {
504
+ if (arg("confirm-previous-source-sha") !== plan.previousSource.sha) {
505
+ throw new Error(
506
+ "--confirm-previous-source-sha must equal previousSourceSha",
507
+ );
508
+ }
509
+ if (arg("confirm-host-id") !== plan.aws.hostId) {
510
+ throw new Error("--confirm-host-id must equal the exact host id");
511
+ }
512
+ if (arg("confirm-instance-id") !== plan.aws.instanceId) {
513
+ throw new Error("--confirm-instance-id must equal the exact instance id");
514
+ }
515
+ if (!flag("confirm-zero-allocation")) {
516
+ throw new Error("--confirm-zero-allocation is required");
517
+ }
518
+ }
501
519
  }
502
520
 
503
521
  function emit(plan, mode, execute, operation) {
@@ -523,6 +541,17 @@ export function main() {
523
541
  executeMacosJitCampaignLaunch(plan, { profile: arg("aws-profile") }),
524
542
  );
525
543
  }
544
+ if (["plan-rebind", "rebind-campaign"].includes(mode)) {
545
+ const plan = createMacosJitSourceRebindPlan({
546
+ ...commonValues(execute),
547
+ previousSourceSha: arg("previous-source-sha"),
548
+ previousSourceRef: arg("previous-source-ref"),
549
+ workflowId: arg("workflow-id"),
550
+ hostId: arg("host-id"),
551
+ instanceId: arg("instance-id"),
552
+ });
553
+ return emit(plan, mode, execute, () => executeMacosJitSourceRebind(plan, { profile: arg("aws-profile") }));
554
+ }
526
555
  if (["plan-job", "run-job"].includes(mode)) {
527
556
  const plan = createMacosJitJobPlan({
528
557
  ...commonValues(execute),