@kungfu-tech/buildchain 3.0.9-alpha.1 → 3.0.9-alpha.10
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/bin/buildchain.mjs +9 -1
- package/contracts/dev-delivery-authority-v2.schema.json +253 -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/next-development-request-v1.schema.json +66 -0
- package/contracts/next-development-transition-v1.schema.json +292 -0
- package/dist/site/agent-index.json +1 -0
- package/dist/site/artifact-schemas.json +2 -0
- package/dist/site/buildchain-contract.json +74 -14
- package/dist/site/buildchain-site.json +334 -36
- package/dist/site/capability-registry.json +7 -6
- package/dist/site/cli-registry.json +41 -2
- package/dist/site/kfd-claims.json +193 -9
- package/dist/site/kfd-upstream-aggregate.json +9 -9
- package/dist/site/manual-registry.json +23 -7
- package/dist/site/node-api-registry.json +7789 -4648
- package/dist/site/page-registry.json +315 -25
- package/dist/site/public-surface-audit.json +353 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-passport-check-manifest.json +1 -0
- package/dist/site/release-provenance.json +12 -0
- package/dist/site/schemas/dev-delivery-authority-v2.schema.json +442 -0
- package/dist/site/schemas/release-passport-v1.schema.json +3 -0
- package/dist/site/site-manifest.json +19 -11
- package/dist/site/workflow-registry.json +15 -7
- package/docs/MAP.md +5 -2
- package/docs/adopter-delivery-gate.md +106 -0
- package/docs/aws-us-elastic-runner-burst-plane.md +30 -3
- package/docs/cli-reference.md +200 -13
- package/docs/dev-alpha-candidate-patrol.md +8 -0
- package/docs/dev-delivery-qualification-landing-adr.md +137 -0
- package/docs/dev-delivery-warrant.md +149 -22
- package/docs/kfd-support.md +7 -2
- package/docs/next-development-transition.md +119 -0
- package/docs/node-api-reference.md +381 -204
- package/docs/release-governance.md +10 -7
- package/docs/release-passport.md +8 -2
- package/docs/versioning.md +4 -1
- package/package.json +21 -6
- package/packages/core/README.md +25 -0
- package/packages/core/adopter-delivery-gate.js +576 -0
- package/packages/core/adopter-delivery-json.js +66 -0
- 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/artifact-verification-envelope.js +5 -5
- package/packages/core/buildchain-agent-manuals.js +1 -0
- package/packages/core/buildchain-channel-identity.js +2 -3
- package/packages/core/buildchain-compatibility-proof.js +22 -0
- package/packages/core/buildchain-delivery-bootstrap.js +240 -0
- package/packages/core/buildchain-delivery-infrastructure.js +164 -0
- package/packages/core/channel-candidate.js +8 -0
- package/packages/core/channel-promotion-baseline.js +52 -0
- package/packages/core/dev-alpha-candidate-selection.js +10 -2
- package/packages/core/dev-delivery-authority.js +996 -0
- package/packages/core/dev-delivery-native-proof.js +418 -0
- package/packages/core/dev-delivery-warrant-settlement.js +67 -20
- package/packages/core/dev-delivery-warrant.js +128 -46
- package/packages/core/kfd-adopter-category-driver.js +167 -0
- package/packages/core/kfd-adopter-manifest.js +49 -50
- 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 +726 -0
- package/packages/core/next-development-projection.js +288 -0
- package/packages/core/next-development-transition.js +738 -0
- package/packages/core/paper-agent-entry.js +7 -4
- package/packages/core/paper.js +14 -7
- package/packages/core/release-passport-contract.js +10 -6
- package/packages/core/release-passport.js +29 -12
- package/scripts/aws-macos-jit-controller-core.mjs +239 -4
- package/scripts/aws-macos-jit-controller.mjs +198 -2
- package/scripts/aws-macos-jit-instance-rehydrate.mjs +260 -0
- package/scripts/aws-macos-jit-source-rebind.mjs +235 -14
- package/scripts/aws-macos-jit.mjs +12 -0
- package/scripts/buildchain-cli-help.mjs +9 -2
- package/scripts/check-internal-architecture.mjs +1 -0
- package/scripts/check-inventory.mjs +12 -1
- package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
- package/scripts/dev-delivery-authority.mjs +368 -0
- package/scripts/dev-delivery-native-run.mjs +235 -0
- package/scripts/dev-delivery-proof.mjs +64 -2
- package/scripts/dev-delivery-two-phase-resume.mjs +90 -0
- package/scripts/dev-delivery-two-phase.mjs +577 -0
- package/scripts/dev-delivery-warrant.mjs +47 -5
- package/scripts/dev-pr-delivery-warrant.mjs +19 -14
- package/scripts/generate-buildchain-kfd-witnesses.mjs +2 -2
- package/scripts/generate-channel-build-workflow.mjs +4 -0
- package/scripts/generate-next-development-guidance.mjs +49 -0
- package/scripts/generate-site-bundle.mjs +8 -3
- package/scripts/init-repo.mjs +135 -62
- package/scripts/next-development-self-dogfood-harness.mjs +409 -0
- package/scripts/next-development-self-dogfood.mjs +532 -0
- package/scripts/next-development-transition.mjs +47 -0
- package/scripts/release-candidate-tail-reseal.mjs +426 -0
- package/scripts/seal-artifact-signing-requests.mjs +7 -4
- package/scripts/site-capability-metadata.mjs +23 -1
- package/scripts/stable-candidate-qualification.mjs +7 -7
- package/scripts/workflow-call-contract.mjs +1 -1
- package/templates/native-dev-delivery.yml +86 -0
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
AWS_MACOS_JIT_CONTROLLER_CONTRACT,
|
|
7
7
|
createMacosJitCampaignPlan,
|
|
8
8
|
createMacosJitClosePlan,
|
|
9
|
+
createMacosJitInstanceRehydratePlan,
|
|
9
10
|
createMacosJitJobPlan,
|
|
10
11
|
createMacosJitSourceRebindPlan,
|
|
11
12
|
macosAllocateHostsArgs,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
macosRunInstancesArgs,
|
|
14
15
|
} from "./aws-macos-jit-controller-core.mjs";
|
|
15
16
|
import { MACOS_EC2_JIT_REGIONS } from "./aws-macos-jit-core.mjs";
|
|
17
|
+
import { executeMacosJitInstanceRehydrate } from "./aws-macos-jit-instance-rehydrate.mjs";
|
|
16
18
|
import { executeMacosJitJob } from "./aws-macos-jit-job-controller.mjs";
|
|
17
19
|
import { executeMacosJitSourceRebind } from "./aws-macos-jit-source-rebind.mjs";
|
|
18
20
|
import {
|
|
@@ -36,6 +38,19 @@ function flag(name) {
|
|
|
36
38
|
return process.argv.includes(`--${name}`);
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
function jsonArrayArg(name) {
|
|
42
|
+
let parsed;
|
|
43
|
+
try {
|
|
44
|
+
parsed = JSON.parse(arg(name));
|
|
45
|
+
} catch {
|
|
46
|
+
throw new Error(`--${name} must be valid JSON`);
|
|
47
|
+
}
|
|
48
|
+
if (!Array.isArray(parsed)) {
|
|
49
|
+
throw new Error(`--${name} must be a JSON array`);
|
|
50
|
+
}
|
|
51
|
+
return parsed;
|
|
52
|
+
}
|
|
53
|
+
|
|
39
54
|
function assertCampaignLaunchPreflight(plan, profile) {
|
|
40
55
|
const launchGate = assertMacosBudgetLaunchGate(plan, profile);
|
|
41
56
|
const commit = ghJson(
|
|
@@ -493,6 +508,20 @@ function commonValues(execute) {
|
|
|
493
508
|
};
|
|
494
509
|
}
|
|
495
510
|
|
|
511
|
+
function confirmHistoricalPreflightFailures(plan) {
|
|
512
|
+
const confirmed = flag("confirm-historical-preflight-failure-runs-json")
|
|
513
|
+
? jsonArrayArg("confirm-historical-preflight-failure-runs-json")
|
|
514
|
+
: [];
|
|
515
|
+
if (
|
|
516
|
+
JSON.stringify(confirmed) !==
|
|
517
|
+
JSON.stringify(plan.github.historicalPreflightFailureRuns)
|
|
518
|
+
) {
|
|
519
|
+
throw new Error(
|
|
520
|
+
"--confirm-historical-preflight-failure-runs-json must equal the exact historical preflight failure inventory",
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
496
525
|
function confirm(plan) {
|
|
497
526
|
if (arg("confirm-source-sha") !== plan.source.sha) {
|
|
498
527
|
throw new Error("--confirm-source-sha must equal the exact source SHA");
|
|
@@ -515,6 +544,115 @@ function confirm(plan) {
|
|
|
515
544
|
if (!flag("confirm-zero-allocation")) {
|
|
516
545
|
throw new Error("--confirm-zero-allocation is required");
|
|
517
546
|
}
|
|
547
|
+
if (plan.github.priorRunPolicy === "terminal-failure") {
|
|
548
|
+
const confirmed = jsonArrayArg(
|
|
549
|
+
"confirm-terminal-failure-run-ids-json",
|
|
550
|
+
).map(String);
|
|
551
|
+
if (
|
|
552
|
+
JSON.stringify(confirmed) !==
|
|
553
|
+
JSON.stringify(plan.github.terminalFailureRunIds)
|
|
554
|
+
) {
|
|
555
|
+
throw new Error(
|
|
556
|
+
"--confirm-terminal-failure-run-ids-json must equal the exact failed run inventory",
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
const confirmedStartupFailures = flag("confirm-startup-failure-runs-json")
|
|
560
|
+
? jsonArrayArg("confirm-startup-failure-runs-json")
|
|
561
|
+
: [];
|
|
562
|
+
if (
|
|
563
|
+
JSON.stringify(confirmedStartupFailures) !==
|
|
564
|
+
JSON.stringify(plan.github.startupFailureRuns)
|
|
565
|
+
) {
|
|
566
|
+
throw new Error(
|
|
567
|
+
"--confirm-startup-failure-runs-json must equal the exact startup failure inventory",
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
const confirmedHistoricalTerminalFailures = flag(
|
|
571
|
+
"confirm-historical-terminal-failure-runs-json",
|
|
572
|
+
)
|
|
573
|
+
? jsonArrayArg("confirm-historical-terminal-failure-runs-json")
|
|
574
|
+
: [];
|
|
575
|
+
if (
|
|
576
|
+
JSON.stringify(confirmedHistoricalTerminalFailures) !==
|
|
577
|
+
JSON.stringify(plan.github.historicalTerminalFailureRuns)
|
|
578
|
+
) {
|
|
579
|
+
throw new Error(
|
|
580
|
+
"--confirm-historical-terminal-failure-runs-json must equal the exact historical terminal failure inventory",
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
const confirmedSuccessfulRuns = flag("confirm-successful-run-ids-json")
|
|
584
|
+
? jsonArrayArg("confirm-successful-run-ids-json").map(String)
|
|
585
|
+
: [];
|
|
586
|
+
if (
|
|
587
|
+
JSON.stringify(confirmedSuccessfulRuns) !==
|
|
588
|
+
JSON.stringify(plan.github.successfulRunIds)
|
|
589
|
+
) {
|
|
590
|
+
throw new Error(
|
|
591
|
+
"--confirm-successful-run-ids-json must equal the exact successful run inventory",
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
} else if (plan.github.priorRunPolicy === "preflight-failure") {
|
|
595
|
+
const confirmed = jsonArrayArg(
|
|
596
|
+
"confirm-preflight-failure-run-ids-json",
|
|
597
|
+
).map(String);
|
|
598
|
+
if (
|
|
599
|
+
JSON.stringify(confirmed) !==
|
|
600
|
+
JSON.stringify(plan.github.preflightFailureRunIds)
|
|
601
|
+
) {
|
|
602
|
+
throw new Error(
|
|
603
|
+
"--confirm-preflight-failure-run-ids-json must equal the exact preflight failure inventory",
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
const confirmedStartupFailures = flag("confirm-startup-failure-runs-json")
|
|
607
|
+
? jsonArrayArg("confirm-startup-failure-runs-json")
|
|
608
|
+
: [];
|
|
609
|
+
if (
|
|
610
|
+
JSON.stringify(confirmedStartupFailures) !==
|
|
611
|
+
JSON.stringify(plan.github.startupFailureRuns)
|
|
612
|
+
) {
|
|
613
|
+
throw new Error(
|
|
614
|
+
"--confirm-startup-failure-runs-json must equal the exact startup failure inventory",
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
const confirmedHistoricalTerminalFailures = flag(
|
|
618
|
+
"confirm-historical-terminal-failure-runs-json",
|
|
619
|
+
)
|
|
620
|
+
? jsonArrayArg("confirm-historical-terminal-failure-runs-json")
|
|
621
|
+
: [];
|
|
622
|
+
if (
|
|
623
|
+
JSON.stringify(confirmedHistoricalTerminalFailures) !==
|
|
624
|
+
JSON.stringify(plan.github.historicalTerminalFailureRuns)
|
|
625
|
+
) {
|
|
626
|
+
throw new Error(
|
|
627
|
+
"--confirm-historical-terminal-failure-runs-json must equal the exact historical terminal failure inventory",
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
const confirmedSuccessfulRuns = flag("confirm-successful-run-ids-json")
|
|
631
|
+
? jsonArrayArg("confirm-successful-run-ids-json").map(String)
|
|
632
|
+
: [];
|
|
633
|
+
if (
|
|
634
|
+
JSON.stringify(confirmedSuccessfulRuns) !==
|
|
635
|
+
JSON.stringify(plan.github.successfulRunIds)
|
|
636
|
+
) {
|
|
637
|
+
throw new Error(
|
|
638
|
+
"--confirm-successful-run-ids-json must equal the exact successful run inventory",
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
confirmHistoricalPreflightFailures(plan);
|
|
643
|
+
}
|
|
644
|
+
if (plan.kind === "instance-rehydrate-plan") {
|
|
645
|
+
if (arg("confirm-host-id") !== plan.aws.hostId) {
|
|
646
|
+
throw new Error("--confirm-host-id must equal the exact host id");
|
|
647
|
+
}
|
|
648
|
+
if (arg("confirm-replaces-instance-id") !== plan.aws.replacesInstanceId) {
|
|
649
|
+
throw new Error(
|
|
650
|
+
"--confirm-replaces-instance-id must equal replacesInstanceId",
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
if (!flag("confirm-no-host-allocation")) {
|
|
654
|
+
throw new Error("--confirm-no-host-allocation is required");
|
|
655
|
+
}
|
|
518
656
|
}
|
|
519
657
|
}
|
|
520
658
|
|
|
@@ -541,7 +679,32 @@ export function main() {
|
|
|
541
679
|
executeMacosJitCampaignLaunch(plan, { profile: arg("aws-profile") }),
|
|
542
680
|
);
|
|
543
681
|
}
|
|
544
|
-
if (["plan-
|
|
682
|
+
if (["plan-rehydrate", "rehydrate-instance"].includes(mode)) {
|
|
683
|
+
const plan = createMacosJitInstanceRehydratePlan({
|
|
684
|
+
...commonValues(execute),
|
|
685
|
+
hostId: arg("host-id"),
|
|
686
|
+
hostAllocatedAt: arg("host-allocated-at"),
|
|
687
|
+
replacesInstanceId: arg("replaces-instance-id"),
|
|
688
|
+
rehydrationId: arg("rehydration-id"),
|
|
689
|
+
});
|
|
690
|
+
return emit(plan, mode, execute, () =>
|
|
691
|
+
executeMacosJitInstanceRehydrate(plan, {
|
|
692
|
+
profile: arg("aws-profile"),
|
|
693
|
+
}),
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
if (
|
|
697
|
+
[
|
|
698
|
+
"plan-rebind",
|
|
699
|
+
"rebind-campaign",
|
|
700
|
+
"plan-rebind-after-failure",
|
|
701
|
+
"rebind-campaign-after-failure",
|
|
702
|
+
"plan-rebind-after-preflight-failure",
|
|
703
|
+
"rebind-campaign-after-preflight-failure",
|
|
704
|
+
].includes(mode)
|
|
705
|
+
) {
|
|
706
|
+
const afterFailure = mode.endsWith("after-failure");
|
|
707
|
+
const afterPreflightFailure = mode.endsWith("after-preflight-failure");
|
|
545
708
|
const plan = createMacosJitSourceRebindPlan({
|
|
546
709
|
...commonValues(execute),
|
|
547
710
|
previousSourceSha: arg("previous-source-sha"),
|
|
@@ -549,8 +712,41 @@ export function main() {
|
|
|
549
712
|
workflowId: arg("workflow-id"),
|
|
550
713
|
hostId: arg("host-id"),
|
|
551
714
|
instanceId: arg("instance-id"),
|
|
715
|
+
priorRunPolicy: afterPreflightFailure
|
|
716
|
+
? "preflight-failure"
|
|
717
|
+
: afterFailure
|
|
718
|
+
? "terminal-failure"
|
|
719
|
+
: "unused",
|
|
720
|
+
terminalFailureRunIds: afterFailure
|
|
721
|
+
? jsonArrayArg("terminal-failure-run-ids-json")
|
|
722
|
+
: [],
|
|
723
|
+
preflightFailureRunIds: afterPreflightFailure
|
|
724
|
+
? jsonArrayArg("preflight-failure-run-ids-json")
|
|
725
|
+
: [],
|
|
726
|
+
startupFailureRuns:
|
|
727
|
+
(afterFailure || afterPreflightFailure) &&
|
|
728
|
+
flag("startup-failure-runs-json")
|
|
729
|
+
? jsonArrayArg("startup-failure-runs-json")
|
|
730
|
+
: [],
|
|
731
|
+
historicalTerminalFailureRuns:
|
|
732
|
+
(afterFailure || afterPreflightFailure) &&
|
|
733
|
+
flag("historical-terminal-failure-runs-json")
|
|
734
|
+
? jsonArrayArg("historical-terminal-failure-runs-json")
|
|
735
|
+
: [],
|
|
736
|
+
historicalPreflightFailureRuns:
|
|
737
|
+
(afterFailure || afterPreflightFailure) &&
|
|
738
|
+
flag("historical-preflight-failure-runs-json")
|
|
739
|
+
? jsonArrayArg("historical-preflight-failure-runs-json")
|
|
740
|
+
: [],
|
|
741
|
+
successfulRunIds:
|
|
742
|
+
(afterFailure || afterPreflightFailure) &&
|
|
743
|
+
flag("successful-run-ids-json")
|
|
744
|
+
? jsonArrayArg("successful-run-ids-json")
|
|
745
|
+
: [],
|
|
552
746
|
});
|
|
553
|
-
return emit(plan, mode, execute, () =>
|
|
747
|
+
return emit(plan, mode, execute, () =>
|
|
748
|
+
executeMacosJitSourceRebind(plan, { profile: arg("aws-profile") }),
|
|
749
|
+
);
|
|
554
750
|
}
|
|
555
751
|
if (["plan-job", "run-job"].includes(mode)) {
|
|
556
752
|
const plan = createMacosJitJobPlan({
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
AWS_MACOS_JIT_CONTROLLER_CONTRACT,
|
|
5
|
+
macosRunInstancesArgs,
|
|
6
|
+
} from "./aws-macos-jit-controller-core.mjs";
|
|
7
|
+
import { MACOS_EC2_JIT_REGIONS } from "./aws-macos-jit-core.mjs";
|
|
8
|
+
import {
|
|
9
|
+
assertDryRun,
|
|
10
|
+
assertMacosBudgetLaunchGate,
|
|
11
|
+
assertOwnership,
|
|
12
|
+
awsArgs,
|
|
13
|
+
awsJson,
|
|
14
|
+
commandResult,
|
|
15
|
+
ghJson,
|
|
16
|
+
requireSuccess,
|
|
17
|
+
} from "./aws-macos-jit-controller-runtime.mjs";
|
|
18
|
+
|
|
19
|
+
function assertInstanceRehydratePreflight(plan, profile) {
|
|
20
|
+
const launchGate = assertMacosBudgetLaunchGate(plan, profile);
|
|
21
|
+
const commit = ghJson(
|
|
22
|
+
["api", `repos/${plan.repository}/commits/${plan.source.sha}`],
|
|
23
|
+
undefined,
|
|
24
|
+
"GitHub exact-source preflight",
|
|
25
|
+
);
|
|
26
|
+
if (String(commit.sha || "").toLowerCase() !== plan.source.sha) {
|
|
27
|
+
throw new Error("GitHub did not resolve the exact campaign source SHA");
|
|
28
|
+
}
|
|
29
|
+
const host = awsJson(
|
|
30
|
+
plan,
|
|
31
|
+
profile,
|
|
32
|
+
[
|
|
33
|
+
"ec2",
|
|
34
|
+
"describe-hosts",
|
|
35
|
+
"--host-ids",
|
|
36
|
+
plan.aws.hostId,
|
|
37
|
+
"--output",
|
|
38
|
+
"json",
|
|
39
|
+
],
|
|
40
|
+
"macOS rehydration host preflight",
|
|
41
|
+
).Hosts?.[0];
|
|
42
|
+
if (
|
|
43
|
+
!host ||
|
|
44
|
+
host.State !== "available" ||
|
|
45
|
+
host.AvailabilityZone !== plan.aws.availabilityZone ||
|
|
46
|
+
host.HostProperties?.InstanceType !== plan.aws.instanceType ||
|
|
47
|
+
(host.Instances || []).length !== 0 ||
|
|
48
|
+
new Date(host.AllocationTime).toISOString() !== plan.aws.hostAllocatedAt
|
|
49
|
+
) {
|
|
50
|
+
throw new Error("macOS rehydration host identity or capacity mismatch");
|
|
51
|
+
}
|
|
52
|
+
assertOwnership(host.Tags, plan);
|
|
53
|
+
|
|
54
|
+
const regionalPlans = Object.keys(MACOS_EC2_JIT_REGIONS).map((region) => ({
|
|
55
|
+
...plan,
|
|
56
|
+
aws: { ...plan.aws, region },
|
|
57
|
+
}));
|
|
58
|
+
const otherHosts = regionalPlans.flatMap((regionalPlan) =>
|
|
59
|
+
(
|
|
60
|
+
awsJson(
|
|
61
|
+
regionalPlan,
|
|
62
|
+
profile,
|
|
63
|
+
[
|
|
64
|
+
"ec2",
|
|
65
|
+
"describe-hosts",
|
|
66
|
+
"--filter",
|
|
67
|
+
"Name=tag:kungfu:plane,Values=aws-us-elastic-runner-burst",
|
|
68
|
+
"Name=tag:kungfu:provider,Values=macos-ec2-jit",
|
|
69
|
+
"Name=state,Values=available,pending,under-assessment",
|
|
70
|
+
"--output",
|
|
71
|
+
"json",
|
|
72
|
+
],
|
|
73
|
+
`macOS rehydration host ceiling in ${regionalPlan.aws.region}`,
|
|
74
|
+
).Hosts || []
|
|
75
|
+
).filter((candidate) => candidate.HostId !== plan.aws.hostId),
|
|
76
|
+
);
|
|
77
|
+
if (otherHosts.length !== 0) {
|
|
78
|
+
throw new Error("macOS rehydration found another active Dedicated Host");
|
|
79
|
+
}
|
|
80
|
+
const activeInstances = regionalPlans.flatMap((regionalPlan) => {
|
|
81
|
+
const active = awsJson(
|
|
82
|
+
regionalPlan,
|
|
83
|
+
profile,
|
|
84
|
+
[
|
|
85
|
+
"ec2",
|
|
86
|
+
"describe-instances",
|
|
87
|
+
"--filters",
|
|
88
|
+
"Name=tag:kungfu:plane,Values=aws-us-elastic-runner-burst",
|
|
89
|
+
"Name=tag:kungfu:provider,Values=macos-ec2-jit",
|
|
90
|
+
"Name=instance-state-name,Values=pending,running,stopping,stopped,shutting-down",
|
|
91
|
+
"--output",
|
|
92
|
+
"json",
|
|
93
|
+
],
|
|
94
|
+
`active macOS JIT instance preflight in ${regionalPlan.aws.region}`,
|
|
95
|
+
);
|
|
96
|
+
return (active.Reservations || []).flatMap(
|
|
97
|
+
(reservation) => reservation.Instances || [],
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
if (activeInstances.length !== 0) {
|
|
101
|
+
throw new Error("macOS rehydration requires zero active JIT instances");
|
|
102
|
+
}
|
|
103
|
+
const runners = ghJson(
|
|
104
|
+
["api", `repos/${plan.repository}/actions/runners?per_page=100`],
|
|
105
|
+
undefined,
|
|
106
|
+
"macOS rehydration runner preflight",
|
|
107
|
+
).runners;
|
|
108
|
+
if (
|
|
109
|
+
(runners || []).some((runner) =>
|
|
110
|
+
(runner.labels || []).some((label) =>
|
|
111
|
+
String(label.name || label).startsWith("aws-us-ec2-macos-jit-"),
|
|
112
|
+
),
|
|
113
|
+
)
|
|
114
|
+
) {
|
|
115
|
+
throw new Error("macOS rehydration requires zero JIT runner residue");
|
|
116
|
+
}
|
|
117
|
+
const image = awsJson(
|
|
118
|
+
plan,
|
|
119
|
+
profile,
|
|
120
|
+
[
|
|
121
|
+
"ec2",
|
|
122
|
+
"describe-images",
|
|
123
|
+
"--image-ids",
|
|
124
|
+
plan.aws.amiId,
|
|
125
|
+
"--output",
|
|
126
|
+
"json",
|
|
127
|
+
],
|
|
128
|
+
"macOS rehydration AMI preflight",
|
|
129
|
+
).Images?.[0];
|
|
130
|
+
if (
|
|
131
|
+
!image ||
|
|
132
|
+
image.Name !== plan.aws.amiName ||
|
|
133
|
+
image.Architecture !== "arm64_mac" ||
|
|
134
|
+
image.State !== "available"
|
|
135
|
+
) {
|
|
136
|
+
throw new Error("macOS AMI identity or availability mismatch");
|
|
137
|
+
}
|
|
138
|
+
const subnet = awsJson(
|
|
139
|
+
plan,
|
|
140
|
+
profile,
|
|
141
|
+
[
|
|
142
|
+
"ec2",
|
|
143
|
+
"describe-subnets",
|
|
144
|
+
"--subnet-ids",
|
|
145
|
+
plan.aws.subnetId,
|
|
146
|
+
"--output",
|
|
147
|
+
"json",
|
|
148
|
+
],
|
|
149
|
+
"macOS rehydration subnet preflight",
|
|
150
|
+
).Subnets?.[0];
|
|
151
|
+
if (!subnet || subnet.AvailabilityZone !== plan.aws.availabilityZone) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
"macOS subnet and Dedicated Host availability zone mismatch",
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
...launchGate,
|
|
158
|
+
exactCommit: plan.source.sha,
|
|
159
|
+
hostId: host.HostId,
|
|
160
|
+
hostState: host.State,
|
|
161
|
+
activeInstances: 0,
|
|
162
|
+
otherActiveHosts: 0,
|
|
163
|
+
amiOwnerId: image.OwnerId,
|
|
164
|
+
subnetAvailabilityZone: subnet.AvailabilityZone,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function executeMacosJitInstanceRehydrate(plan, { profile = "" } = {}) {
|
|
169
|
+
if (
|
|
170
|
+
plan?.contract !== AWS_MACOS_JIT_CONTROLLER_CONTRACT ||
|
|
171
|
+
plan.kind !== "instance-rehydrate-plan"
|
|
172
|
+
) {
|
|
173
|
+
throw new Error("macOS JIT instance rehydrate plan contract is invalid");
|
|
174
|
+
}
|
|
175
|
+
const preflight = assertInstanceRehydratePreflight(plan, profile);
|
|
176
|
+
assertDryRun(
|
|
177
|
+
commandResult(
|
|
178
|
+
"aws",
|
|
179
|
+
awsArgs(
|
|
180
|
+
plan,
|
|
181
|
+
profile,
|
|
182
|
+
macosRunInstancesArgs(plan, { hostId: plan.aws.hostId, dryRun: true }),
|
|
183
|
+
),
|
|
184
|
+
),
|
|
185
|
+
"EC2 same-host RunInstances DryRun",
|
|
186
|
+
);
|
|
187
|
+
const launched = awsJson(
|
|
188
|
+
plan,
|
|
189
|
+
profile,
|
|
190
|
+
macosRunInstancesArgs(plan, { hostId: plan.aws.hostId }),
|
|
191
|
+
"EC2 same-host RunInstances",
|
|
192
|
+
);
|
|
193
|
+
const instanceId = launched.Instances?.[0]?.InstanceId;
|
|
194
|
+
if (!/^i-[0-9a-f]+$/.test(String(instanceId || ""))) {
|
|
195
|
+
throw new Error("EC2 same-host RunInstances returned no instance identity");
|
|
196
|
+
}
|
|
197
|
+
requireSuccess(
|
|
198
|
+
commandResult(
|
|
199
|
+
"aws",
|
|
200
|
+
awsArgs(plan, profile, [
|
|
201
|
+
"ec2",
|
|
202
|
+
"wait",
|
|
203
|
+
"instance-running",
|
|
204
|
+
"--instance-ids",
|
|
205
|
+
instanceId,
|
|
206
|
+
]),
|
|
207
|
+
),
|
|
208
|
+
"macOS rehydrated instance-running waiter",
|
|
209
|
+
);
|
|
210
|
+
const instance = awsJson(
|
|
211
|
+
plan,
|
|
212
|
+
profile,
|
|
213
|
+
[
|
|
214
|
+
"ec2",
|
|
215
|
+
"describe-instances",
|
|
216
|
+
"--instance-ids",
|
|
217
|
+
instanceId,
|
|
218
|
+
"--output",
|
|
219
|
+
"json",
|
|
220
|
+
],
|
|
221
|
+
"macOS rehydrated instance readback",
|
|
222
|
+
).Reservations?.[0]?.Instances?.[0];
|
|
223
|
+
if (
|
|
224
|
+
!instance ||
|
|
225
|
+
instance.State?.Name !== "running" ||
|
|
226
|
+
instance.Placement?.HostId !== plan.aws.hostId ||
|
|
227
|
+
instance.ImageId !== plan.aws.amiId ||
|
|
228
|
+
instance.InstanceType !== plan.aws.instanceType
|
|
229
|
+
) {
|
|
230
|
+
throw new Error("macOS rehydrated instance identity or placement mismatch");
|
|
231
|
+
}
|
|
232
|
+
assertOwnership(instance.Tags, plan);
|
|
233
|
+
return {
|
|
234
|
+
schemaVersion: 1,
|
|
235
|
+
contract: AWS_MACOS_JIT_CONTROLLER_CONTRACT,
|
|
236
|
+
kind: "instance-rehydrate-result",
|
|
237
|
+
status: "rehydrated",
|
|
238
|
+
repository: plan.repository,
|
|
239
|
+
campaign: plan.campaign,
|
|
240
|
+
source: plan.source,
|
|
241
|
+
replacement: {
|
|
242
|
+
rehydrationId: plan.aws.rehydrationId,
|
|
243
|
+
replacesInstanceId: plan.aws.replacesInstanceId,
|
|
244
|
+
},
|
|
245
|
+
aws: {
|
|
246
|
+
region: plan.aws.region,
|
|
247
|
+
availabilityZone: plan.aws.availabilityZone,
|
|
248
|
+
hostId: plan.aws.hostId,
|
|
249
|
+
hostAllocatedAt: plan.aws.hostAllocatedAt,
|
|
250
|
+
instanceId,
|
|
251
|
+
instanceType: instance.InstanceType,
|
|
252
|
+
imageId: instance.ImageId,
|
|
253
|
+
launchTime: new Date(instance.LaunchTime).toISOString(),
|
|
254
|
+
},
|
|
255
|
+
preflight,
|
|
256
|
+
dryRun: "RunInstances:DryRunOperation",
|
|
257
|
+
hostAllocated: false,
|
|
258
|
+
planDigest: plan.digest,
|
|
259
|
+
};
|
|
260
|
+
}
|