@kungfu-tech/buildchain 3.0.9-alpha.9 → 4.0.0-alpha.0

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 (262) hide show
  1. package/AGENTS.md +39 -0
  2. package/README.md +17 -13
  3. package/actions/promote-buildchain-ref/README.md +0 -10
  4. package/actions/release-tail/README.md +6 -10
  5. package/architecture/internal-capabilities.json +502 -0
  6. package/architecture/maintainability-baseline.json +59 -0
  7. package/architecture/maintainability-policy.json +1384 -0
  8. package/architecture/release-tail-contract-inventory.json +587 -0
  9. package/architecture/v3-core-mechanism-inventory.json +1043 -0
  10. package/architecture/v3-core-mechanism-inventory.md +67 -0
  11. package/architecture/v4-adopter-delivery-parity.json +47 -0
  12. package/architecture/v4-architecture-constitution.md +210 -0
  13. package/architecture/v4-bootstrap-authority.json +56 -0
  14. package/architecture/v4-canonical-contract-fixtures.json +105 -0
  15. package/architecture/v4-capability-state-machine-manifest.json +480 -0
  16. package/architecture/v4-capability-state-machine-manifest.schema.json +202 -0
  17. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.json +830 -0
  18. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.md +223 -0
  19. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.schema.json +318 -0
  20. package/architecture/v4-delivery-warrant-shadow-fixtures.json +227 -0
  21. package/architecture/v4-delivery-warrant-shadow-fixtures.schema.json +67 -0
  22. package/architecture/v4-exception-ledger.json +5 -0
  23. package/architecture/v4-exception-ledger.schema.json +56 -0
  24. package/architecture/v4-partial-mutation-recovery-qualification.json +65 -0
  25. package/architecture/v4-platform-stage-checkpoints.json +97 -0
  26. package/architecture/v4-provider-operation-journal-contract.json +113 -0
  27. package/architecture/v4-release-activation-shadow-domain.json +61 -0
  28. package/architecture/v4-release-train-parity.json +68 -0
  29. package/architecture/v4-rust-libnode-bridge-evaluation.json +107 -0
  30. package/architecture/v4-rust-libnode-bridge-spike.md +85 -0
  31. package/architecture/v4-stable-publication-fence.json +66 -0
  32. package/architecture/v4-stage-capsule-contract.json +90 -0
  33. package/architecture/v4-stage-capsule-qualification.json +99 -0
  34. package/architecture/v4-stage-capsule-resume-planner.json +52 -0
  35. package/architecture/v4-stage-capsule-store-contract.json +72 -0
  36. package/bin/buildchain.mjs +15 -10
  37. package/contracts/auditable-demo-scenario-v1.schema.json +1 -1
  38. package/contracts/buildchain-v2-residuals-v1.json +2 -2
  39. package/contracts/fixtures/v4-delivery-warrant-trace-v1/golden.json +158 -0
  40. package/contracts/fixtures/v4-delivery-warrant-trace-v1/property.json +380 -0
  41. package/contracts/fixtures/v4-delivery-warrant-trace-v1/replay.json +163 -0
  42. package/contracts/fixtures/v4-partial-mutation-recovery-v1/shared.json +155 -0
  43. package/contracts/fixtures/v4-provider-operation-journal-v1/shared.json +457 -0
  44. package/contracts/fixtures/v4-provider-readback-idempotency-v1/shared.json +682 -0
  45. package/contracts/fixtures/v4-release-activation-shadow-v1/shared.json +53 -0
  46. package/contracts/fixtures/v4-stable-publication-fence-v1/shared.json +56 -0
  47. package/contracts/fixtures/v4-stage-capsule-resume-v1/late-platform-failure.json +176 -0
  48. package/contracts/fixtures/v4-stage-capsule-store-v1/shared.json +132 -0
  49. package/contracts/fixtures/v4-stage-capsule-v1/shared.json +111 -0
  50. package/contracts/v4-canonical-contracts-v1.schema.json +182 -0
  51. package/contracts/v4-delivery-warrant-semantic-diff-report-v1.schema.json +284 -0
  52. package/contracts/v4-delivery-warrant-trace-v1.schema.json +215 -0
  53. package/contracts/v4-host-contract-v1.schema.json +135 -0
  54. package/contracts/v4-partial-mutation-recovery-v1.schema.json +168 -0
  55. package/contracts/v4-provider-operation-journal-v1.schema.json +381 -0
  56. package/contracts/v4-release-activation-shadow-v1.schema.json +186 -0
  57. package/contracts/v4-stable-publication-fence-v1.schema.json +125 -0
  58. package/contracts/v4-stage-capsule-resume-v1.schema.json +225 -0
  59. package/contracts/v4-stage-capsule-store-v1.schema.json +154 -0
  60. package/contracts/v4-stage-capsule-v1.schema.json +178 -0
  61. package/crates/buildchain-v4-bridge/Cargo.lock +213 -0
  62. package/crates/buildchain-v4-bridge/Cargo.toml +12 -0
  63. package/crates/buildchain-v4-bridge/src/main.rs +495 -0
  64. package/dist/site/agent-index.json +0 -4
  65. package/dist/site/artifact-schemas.json +0 -8
  66. package/dist/site/buildchain-contract.json +103 -397
  67. package/dist/site/buildchain-site.json +302 -386
  68. package/dist/site/capability-registry.json +9 -11
  69. package/dist/site/cli-registry.json +8 -53
  70. package/dist/site/controller-registry.json +48 -8
  71. package/dist/site/kfd-claims.json +129 -167
  72. package/dist/site/kfd-upstream-aggregate.json +9 -9
  73. package/dist/site/manual-registry.json +18 -49
  74. package/dist/site/node-api-registry.json +4345 -7345
  75. package/dist/site/page-registry.json +278 -346
  76. package/dist/site/public-surface-audit.json +85 -404
  77. package/dist/site/publication-authority-registry.json +11 -1
  78. package/dist/site/publication-registry.json +4 -4
  79. package/dist/site/release-provenance.json +3 -9
  80. package/dist/site/schemas/kfd-support-projection-v1.schema.json +106 -0
  81. package/dist/site/site-manifest.json +22 -38
  82. package/dist/site/workflow-registry.json +78 -35
  83. package/docs/MAP.md +8 -5
  84. package/docs/auditable-demo.md +2 -2
  85. package/docs/aws-us-elastic-runner-burst-plane.md +7 -59
  86. package/docs/cli-reference.md +15 -213
  87. package/docs/cli.md +14 -14
  88. package/docs/dev-alpha-candidate-patrol.md +3 -11
  89. package/docs/dev-delivery-warrant.md +29 -194
  90. package/docs/engineering-housekeeper.md +12 -4
  91. package/docs/getting-started.md +1 -1
  92. package/docs/install.md +5 -5
  93. package/docs/kfd-support.md +2 -7
  94. package/docs/lifecycle-protocol.md +2 -4
  95. package/docs/node-api-reference.md +414 -554
  96. package/docs/release-flow.md +31 -31
  97. package/docs/release-governance.md +16 -28
  98. package/docs/release-passport.md +2 -15
  99. package/docs/release-tail-contract.md +13 -6
  100. package/docs/release-tail-provider-plane.md +13 -19
  101. package/docs/release-train.md +4 -4
  102. package/docs/reusable-build-surface.md +14 -27
  103. package/docs/site-bundle-contract.md +1 -5
  104. package/docs/stable-candidate-patrol.md +7 -7
  105. package/docs/v4-canonical-contracts.md +92 -0
  106. package/docs/v4-delivery-warrant-read-candidate.md +73 -0
  107. package/docs/v4-delivery-warrant-semantic-diff.md +75 -0
  108. package/docs/v4-production-release.md +63 -0
  109. package/docs/v4-stage-capsule.md +193 -0
  110. package/docs/versioning.md +3 -5
  111. package/package.json +19 -19
  112. package/packages/core/adopter-delivery-gate.js +576 -0
  113. package/packages/core/adopter-delivery-json.js +66 -0
  114. package/packages/core/artifact-signing.js +0 -61
  115. package/packages/core/artifact-verification-envelope.js +5 -5
  116. package/packages/core/build-facts.js +12 -4
  117. package/packages/core/buildchain-agent-manuals.js +0 -2
  118. package/packages/core/buildchain-channel-identity.js +3 -2
  119. package/packages/core/buildchain-config.js +4 -69
  120. package/packages/core/buildchain-contract.js +232 -46
  121. package/packages/core/buildchain-kfd-claims.js +1 -1
  122. package/packages/core/channel-candidate.js +0 -8
  123. package/packages/core/channel-promotion-baseline.js +0 -52
  124. package/packages/core/controller-evidence.js +3 -4
  125. package/packages/core/dev-alpha-candidate-selection.js +2 -10
  126. package/packages/core/dev-delivery-warrant-cancellation.js +0 -1
  127. package/packages/core/dev-delivery-warrant-settlement.js +20 -67
  128. package/packages/core/dev-delivery-warrant.js +50 -138
  129. package/packages/core/diagnostics.js +3 -8
  130. package/packages/core/github-governance-authority.js +3 -1
  131. package/packages/core/kfd-adopter-category-driver.js +167 -0
  132. package/packages/core/kfd-adopter-manifest.js +47 -46
  133. package/packages/core/kfd-gate.js +15 -45
  134. package/packages/core/paper-agent-entry.js +7 -16
  135. package/packages/core/paper-fleet.js +2 -2
  136. package/packages/core/paper-repository.js +2 -3
  137. package/packages/core/paper-scaffold-content.js +2 -23
  138. package/packages/core/paper.js +11 -43
  139. package/packages/core/publication-reproducibility.js +4 -4
  140. package/packages/core/release-line-bootstrap.js +21 -8
  141. package/packages/core/release-passport-contract.js +5 -5
  142. package/packages/core/release-passport.js +20 -131
  143. package/packages/core/self-dogfood-version.js +48 -35
  144. package/packages/core/spawn-command.js +232 -8
  145. package/packages/core/v4-adopter-delivery-parity.js +219 -0
  146. package/packages/core/v4-canonical-contracts.js +284 -0
  147. package/packages/core/v4-delivery-warrant-fixture-runner.js +371 -0
  148. package/packages/core/v4-delivery-warrant-read-candidate.js +279 -0
  149. package/packages/core/v4-delivery-warrant-semantic-diff-gate.js +352 -0
  150. package/packages/core/v4-delivery-warrant-shadow-adapter.js +447 -0
  151. package/packages/core/v4-partial-mutation-recovery-qualification.js +395 -0
  152. package/packages/core/v4-platform-stage-checkpoints.js +471 -0
  153. package/packages/core/v4-provider-operation-journal.js +531 -0
  154. package/packages/core/v4-provider-readback-idempotency.js +394 -0
  155. package/packages/core/v4-release-activation-shadow.js +519 -0
  156. package/packages/core/v4-stable-publication-fence.js +357 -0
  157. package/packages/core/v4-stage-capsule-local-store.js +426 -0
  158. package/packages/core/v4-stage-capsule-qualification-campaign.js +512 -0
  159. package/packages/core/v4-stage-capsule-qualification.js +456 -0
  160. package/packages/core/v4-stage-capsule-resume-planner.js +397 -0
  161. package/packages/core/v4-stage-capsule-store.js +401 -0
  162. package/packages/core/v4-stage-capsule.js +319 -0
  163. package/scripts/assemble-self-publication-admission.mjs +1 -1
  164. package/scripts/audit-publication-control-plane.mjs +5 -5
  165. package/scripts/auditable-demo-bundle-verification.mjs +3 -2
  166. package/scripts/auditable-demo-platform.mjs +2 -2
  167. package/scripts/auditable-demo-renditions.mjs +1 -1
  168. package/scripts/auditable-demo.mjs +2 -2
  169. package/scripts/aws-macos-jit-controller-core.mjs +13 -346
  170. package/scripts/aws-macos-jit-controller-runtime.mjs +2 -210
  171. package/scripts/aws-macos-jit-controller.mjs +37 -275
  172. package/scripts/aws-macos-jit-core.mjs +3 -41
  173. package/scripts/aws-macos-jit.mjs +0 -12
  174. package/scripts/aws-windows-jit-campaign.mjs +2 -2
  175. package/scripts/aws-windows-jit-controller-core.mjs +1 -1
  176. package/scripts/aws-windows-jit-controller.mjs +3 -3
  177. package/scripts/build-contract-core.mjs +3 -8
  178. package/scripts/build-standalone-binary.mjs +3 -14
  179. package/scripts/buildchain-channel-router.mjs +4 -0
  180. package/scripts/buildchain-cli-help.mjs +7 -16
  181. package/scripts/buildchain-contract-lock.mjs +0 -6
  182. package/scripts/check-action-bundles.mjs +3 -4
  183. package/scripts/check-internal-architecture.mjs +7 -6
  184. package/scripts/check-inventory.mjs +30 -25
  185. package/scripts/check-v4-public-dogfood-contract.mjs +270 -0
  186. package/scripts/create-release-bundle.mjs +8 -4
  187. package/scripts/dev-alpha-candidate-patrol.mjs +1 -22
  188. package/scripts/dev-delivery-proof.mjs +2 -64
  189. package/scripts/dev-delivery-warrant.mjs +61 -86
  190. package/scripts/dev-pr-auto-merge.mjs +4 -30
  191. package/scripts/dev-pr-delivery-warrant.mjs +0 -55
  192. package/scripts/dispatch-artifact-signing-authority.mjs +7 -4
  193. package/scripts/generate-buildchain-kfd-witnesses.mjs +94 -88
  194. package/scripts/generate-channel-build-workflow.mjs +54 -24
  195. package/scripts/generate-channel-promotion-workflow.mjs +15 -6
  196. package/scripts/generate-site-bundle.mjs +8 -27
  197. package/scripts/git-fetch-process-tree.mjs +34 -1
  198. package/scripts/init-repo.mjs +64 -159
  199. package/scripts/inspect-artifact-signing-requests.mjs +0 -6
  200. package/scripts/locked-source-checkout.mjs +3 -4
  201. package/scripts/maintainability-metrics.mjs +8 -2
  202. package/scripts/npm-publish-dry-run.mjs +2 -2
  203. package/scripts/npm-publish-transaction.mjs +2 -2
  204. package/scripts/publication-commit-evidence.mjs +23 -69
  205. package/scripts/release-candidate-resolver.mjs +11 -17
  206. package/scripts/release-tail.mjs +3 -49
  207. package/scripts/resume-from-candidate-run.mjs +9 -123
  208. package/scripts/run-lifecycle-core.mjs +12 -13
  209. package/scripts/seal-artifact-signing-requests.mjs +17 -21
  210. package/scripts/site-capability-metadata.mjs +3 -20
  211. package/scripts/stable-candidate-qualification.mjs +10 -10
  212. package/scripts/v4-architecture.mjs +12 -2
  213. package/scripts/v4-bridge-bootstrap.mjs +141 -0
  214. package/scripts/v4-bridge-evidence.mjs +141 -0
  215. package/scripts/v4-host-adapter.mjs +182 -0
  216. package/scripts/v4-platform-stage-checkpoint-rehearsal.mjs +207 -0
  217. package/scripts/v4-stage-capsule-qualification.mjs +528 -0
  218. package/scripts/v4-stage-capsule-resume-rehearsal.mjs +57 -0
  219. package/scripts/v4-warrant-shadow-plan.mjs +601 -0
  220. package/scripts/verify-golden-path.mjs +5 -5
  221. package/scripts/web-surface-cloudfront-rewrite.mjs +15 -15
  222. package/scripts/web-surface-core.mjs +2 -8
  223. package/scripts/workflow-call-contract.mjs +5 -184
  224. package/contracts/dev-delivery-authority-v2.schema.json +0 -253
  225. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +0 -16
  226. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +0 -57
  227. package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +0 -47
  228. package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +0 -47
  229. package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +0 -40
  230. package/contracts/next-development-request-v1.schema.json +0 -66
  231. package/contracts/next-development-transition-v1.schema.json +0 -292
  232. package/contracts/publication-rehearsal-capsule-v1.schema.json +0 -173
  233. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +0 -442
  234. package/dist/site/schemas/publication-rehearsal-capsule-v1.schema.json +0 -269
  235. package/dist/site/schemas/release-tail-capabilities-v1.schema.json +0 -353
  236. package/dist/site/schemas/release-tail-provider-bindings-v1.schema.json +0 -94
  237. package/docs/dev-delivery-qualification-landing-adr.md +0 -137
  238. package/docs/next-development-transition.md +0 -119
  239. package/docs/publication-rehearsal.md +0 -94
  240. package/packages/core/buildchain-compatibility-proof.js +0 -631
  241. package/packages/core/dev-delivery-authority.js +0 -996
  242. package/packages/core/dev-delivery-native-proof.js +0 -418
  243. package/packages/core/dev-delivery-warrant-shadow.js +0 -502
  244. package/packages/core/next-development-candidate-reservation.js +0 -186
  245. package/packages/core/next-development-controller.js +0 -726
  246. package/packages/core/next-development-projection.js +0 -288
  247. package/packages/core/next-development-transition.js +0 -738
  248. package/packages/core/publication-rehearsal-projection.js +0 -173
  249. package/packages/core/publication-rehearsal-runtime.js +0 -921
  250. package/scripts/aws-macos-jit-instance-rehydrate.mjs +0 -260
  251. package/scripts/aws-macos-jit-source-rebind.mjs +0 -724
  252. package/scripts/dev-delivery-authority.mjs +0 -368
  253. package/scripts/dev-delivery-native-run.mjs +0 -235
  254. package/scripts/dev-delivery-two-phase-resume.mjs +0 -90
  255. package/scripts/dev-delivery-two-phase.mjs +0 -577
  256. package/scripts/generate-next-development-guidance.mjs +0 -49
  257. package/scripts/materialize-self-release-candidate-version.mjs +0 -137
  258. package/scripts/next-development-self-dogfood-harness.mjs +0 -409
  259. package/scripts/next-development-self-dogfood.mjs +0 -532
  260. package/scripts/next-development-transition.mjs +0 -47
  261. package/scripts/release-candidate-tail-reseal.mjs +0 -426
  262. package/templates/native-dev-delivery.yml +0 -86
@@ -1,724 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
-
3
- import { AWS_MACOS_JIT_CONTROLLER_CONTRACT } from "./aws-macos-jit-controller-core.mjs";
4
- import {
5
- assertMacosBudgetLaunchGate,
6
- assertOwnership,
7
- awsArgs,
8
- awsJson,
9
- commandResult,
10
- ghJson,
11
- requireSuccess,
12
- } from "./aws-macos-jit-controller-runtime.mjs";
13
-
14
- const MACOS_JIT_RUNNER_LABEL_PREFIX = "aws-us-ec2-macos-jit-";
15
-
16
- function sourceRefName(ref) {
17
- return ref.replace(/^refs\/heads\//, "");
18
- }
19
-
20
- function sourceRefApiPath(plan) {
21
- return `repos/${plan.repository}/git/refs/heads/${sourceRefName(plan.source.ref)}`;
22
- }
23
-
24
- function priorOwnershipPlan(plan) {
25
- return {
26
- campaign: plan.campaign,
27
- source: plan.previousSource,
28
- };
29
- }
30
-
31
- function assertRunMatchesPolicy(policy, run, jobs, jobCount, artifactCount) {
32
- if (policy === "unused") {
33
- if (jobCount !== 0 || artifactCount !== 0) {
34
- throw new Error(
35
- `macOS campaign run ${run.id} already has jobs or artifacts`,
36
- );
37
- }
38
- return;
39
- }
40
- if (policy === "preflight-failure") {
41
- const jobList = jobs.jobs || [];
42
- const failedJobs = jobList.filter((job) => job.conclusion === "failure");
43
- const nonFailedJobs = jobList.filter((job) => job.conclusion !== "failure");
44
- const hasJitLabel = jobList.some((job) =>
45
- (job.labels || []).some((label) =>
46
- String(label.name || label).startsWith(MACOS_JIT_RUNNER_LABEL_PREFIX),
47
- ),
48
- );
49
- if (
50
- run.status !== "completed" ||
51
- run.conclusion !== "failure" ||
52
- jobCount === 0 ||
53
- artifactCount === 0 ||
54
- jobList.some((job) => job.status !== "completed") ||
55
- failedJobs.length !== 1 ||
56
- !String(failedJobs[0].name || "").endsWith(" / Trust gate") ||
57
- nonFailedJobs.some((job) => job.conclusion !== "skipped") ||
58
- hasJitLabel
59
- ) {
60
- throw new Error(
61
- `macOS campaign run ${run.id} is not an exact preflight Trust gate failure`,
62
- );
63
- }
64
- return;
65
- }
66
- if (
67
- run.status !== "completed" ||
68
- run.conclusion !== "failure" ||
69
- jobCount === 0 ||
70
- artifactCount === 0 ||
71
- (jobs.jobs || []).some((job) => job.status !== "completed")
72
- ) {
73
- throw new Error(
74
- `macOS campaign run ${run.id} is not a terminal failed run with retained evidence`,
75
- );
76
- }
77
- }
78
-
79
- function assertExactStartupFailure(run, jobCount, artifactCount) {
80
- if (
81
- run.status !== "completed" ||
82
- run.conclusion !== "startup_failure" ||
83
- jobCount !== 0 ||
84
- artifactCount !== 0
85
- ) {
86
- throw new Error(
87
- `macOS campaign run ${run.id} is not an exact startup failure`,
88
- );
89
- }
90
- }
91
-
92
- function assertExactSuccessfulRun(run, jobs, jobCount, artifactCount) {
93
- if (
94
- run.status !== "completed" ||
95
- run.conclusion !== "success" ||
96
- jobCount === 0 ||
97
- artifactCount === 0 ||
98
- (jobs.jobs || []).some((job) => job.status !== "completed")
99
- ) {
100
- throw new Error(
101
- `macOS campaign run ${run.id} is not a terminal successful run with retained evidence`,
102
- );
103
- }
104
- }
105
-
106
- function classifyCampaignRun(plan, run) {
107
- const startupFailure = plan.github.startupFailureRuns.find(
108
- ({ runId }) => runId === String(run.id),
109
- );
110
- const historicalTerminalFailure =
111
- plan.github.historicalTerminalFailureRuns.find(
112
- ({ runId }) => runId === String(run.id),
113
- );
114
- const historicalPreflightFailure =
115
- plan.github.historicalPreflightFailureRuns.find(
116
- ({ runId }) => runId === String(run.id),
117
- );
118
- const preflightFailure = plan.github.preflightFailureRunIds.includes(
119
- String(run.id),
120
- );
121
- const successfulRun = plan.github.successfulRunIds.includes(String(run.id));
122
- return {
123
- startupFailure,
124
- historicalTerminalFailure,
125
- historicalPreflightFailure,
126
- successfulRun,
127
- expectedSourceSha:
128
- startupFailure?.sourceSha ||
129
- historicalTerminalFailure?.sourceSha ||
130
- historicalPreflightFailure?.sourceSha ||
131
- plan.previousSource.sha,
132
- classification: startupFailure
133
- ? "startup-failure"
134
- : historicalTerminalFailure
135
- ? "historical-terminal-failure"
136
- : historicalPreflightFailure
137
- ? "historical-preflight-failure"
138
- : successfulRun
139
- ? "successful-run"
140
- : preflightFailure
141
- ? "preflight-failure"
142
- : plan.github.priorRunPolicy,
143
- };
144
- }
145
-
146
- function exactReceiptInventory(receipts, classification, withSource = false) {
147
- return receipts
148
- .filter((receipt) => receipt.classification === classification)
149
- .map((receipt) =>
150
- withSource
151
- ? { runId: receipt.runId, sourceSha: receipt.sourceSha }
152
- : receipt.runId,
153
- )
154
- .sort(
155
- (left, right) =>
156
- Number(withSource ? left.runId : left) -
157
- Number(withSource ? right.runId : right),
158
- );
159
- }
160
-
161
- function assertExactReceiptInventories(plan, receipts) {
162
- if (plan.github.priorRunPolicy === "unused") return;
163
- const inventories = [
164
- ["terminal-failure", plan.github.terminalFailureRunIds, false],
165
- ["preflight-failure", plan.github.preflightFailureRunIds, false],
166
- ["successful-run", plan.github.successfulRunIds, false],
167
- ["startup-failure", plan.github.startupFailureRuns, true],
168
- [
169
- "historical-terminal-failure",
170
- plan.github.historicalTerminalFailureRuns,
171
- true,
172
- ],
173
- [
174
- "historical-preflight-failure",
175
- plan.github.historicalPreflightFailureRuns,
176
- true,
177
- ],
178
- ];
179
- for (const [classification, expected, withSource] of inventories) {
180
- const observed = exactReceiptInventory(
181
- receipts,
182
- classification,
183
- withSource,
184
- );
185
- if (JSON.stringify(observed) !== JSON.stringify(expected)) {
186
- throw new Error(
187
- `macOS campaign ${classification} inventory does not match the confirmed runs`,
188
- );
189
- }
190
- }
191
- }
192
-
193
- function campaignRunReceipts(plan) {
194
- const branch = sourceRefName(plan.source.ref);
195
- const response = ghJson(
196
- [
197
- "api",
198
- `repos/${plan.repository}/actions/workflows/${plan.github.workflowId}/runs?event=workflow_dispatch&branch=${encodeURIComponent(branch)}&per_page=100`,
199
- ],
200
- undefined,
201
- "macOS campaign workflow run lookup",
202
- );
203
- const matching = (response.workflow_runs || []).filter(
204
- (run) => run.head_branch === branch,
205
- );
206
- if (Number(response.total_count || matching.length) !== matching.length) {
207
- throw new Error("macOS campaign workflow run inventory is incomplete");
208
- }
209
- const receipts = [];
210
- for (const run of matching) {
211
- const classification = classifyCampaignRun(plan, run);
212
- if (run.head_sha !== classification.expectedSourceSha) {
213
- throw new Error(
214
- `macOS campaign run ${run.id} is bound to an unexpected source`,
215
- );
216
- }
217
- const jobs = ghJson(
218
- [
219
- "api",
220
- `repos/${plan.repository}/actions/runs/${run.id}/jobs?filter=all&per_page=100`,
221
- ],
222
- undefined,
223
- `macOS campaign run ${run.id} job lookup`,
224
- );
225
- const artifacts = ghJson(
226
- ["api", `repos/${plan.repository}/actions/runs/${run.id}/artifacts`],
227
- undefined,
228
- `macOS campaign run ${run.id} artifact lookup`,
229
- );
230
- const jobCount = Number(jobs.total_count || (jobs.jobs || []).length || 0);
231
- const artifactCount = Number(
232
- artifacts.total_count || (artifacts.artifacts || []).length || 0,
233
- );
234
- if (classification.startupFailure) {
235
- assertExactStartupFailure(run, jobCount, artifactCount);
236
- } else if (classification.successfulRun) {
237
- assertExactSuccessfulRun(run, jobs, jobCount, artifactCount);
238
- } else {
239
- assertRunMatchesPolicy(
240
- classification.historicalTerminalFailure
241
- ? "terminal-failure"
242
- : classification.historicalPreflightFailure
243
- ? "preflight-failure"
244
- : plan.github.priorRunPolicy,
245
- run,
246
- jobs,
247
- jobCount,
248
- artifactCount,
249
- );
250
- }
251
- receipts.push({
252
- runId: String(run.id),
253
- sourceSha: String(run.head_sha || ""),
254
- classification: classification.classification,
255
- status: String(run.status || "unknown"),
256
- conclusion: String(run.conclusion || ""),
257
- jobCount,
258
- artifactCount,
259
- artifacts: (artifacts.artifacts || []).map((artifact) => ({
260
- id: String(artifact.id),
261
- name: String(artifact.name || ""),
262
- sizeInBytes: Number(artifact.size_in_bytes || 0),
263
- digest: String(artifact.digest || ""),
264
- expired: artifact.expired === true,
265
- })),
266
- });
267
- }
268
- assertExactReceiptInventories(plan, receipts);
269
- return receipts;
270
- }
271
-
272
- function assertSourceLineage(plan) {
273
- const currentRef = ghJson(
274
- ["api", sourceRefApiPath(plan)],
275
- undefined,
276
- "macOS campaign source ref preflight",
277
- );
278
- if (currentRef.object?.sha !== plan.previousSource.sha) {
279
- throw new Error("macOS campaign source ref is not at previousSourceSha");
280
- }
281
- const nextCommit = ghJson(
282
- ["api", `repos/${plan.repository}/commits/${plan.source.sha}`],
283
- undefined,
284
- "macOS campaign replacement source preflight",
285
- );
286
- if (String(nextCommit.sha || "").toLowerCase() !== plan.source.sha) {
287
- throw new Error("GitHub did not resolve the replacement source SHA");
288
- }
289
- const comparison = ghJson(
290
- [
291
- "api",
292
- `repos/${plan.repository}/compare/${plan.previousSource.sha}...${plan.source.sha}`,
293
- ],
294
- undefined,
295
- "macOS campaign forward-source preflight",
296
- );
297
- if (
298
- comparison.status !== "ahead" ||
299
- comparison.merge_base_commit?.sha !== plan.previousSource.sha
300
- ) {
301
- throw new Error(
302
- "replacement source must be a strict descendant of previousSourceSha",
303
- );
304
- }
305
- return {
306
- currentRefSha: currentRef.object.sha,
307
- comparisonStatus: comparison.status,
308
- };
309
- }
310
-
311
- function readOwnedCampaignResources(plan, profile) {
312
- const priorPlan = priorOwnershipPlan(plan);
313
- const host = awsJson(
314
- plan,
315
- profile,
316
- [
317
- "ec2",
318
- "describe-hosts",
319
- "--host-ids",
320
- plan.aws.hostId,
321
- "--output",
322
- "json",
323
- ],
324
- "macOS campaign source rebind host preflight",
325
- ).Hosts?.[0];
326
- if (!host || host.State !== "available") {
327
- throw new Error("macOS campaign source rebind host is not available");
328
- }
329
- assertOwnership(host.Tags, priorPlan);
330
- const instance = awsJson(
331
- plan,
332
- profile,
333
- [
334
- "ec2",
335
- "describe-instances",
336
- "--instance-ids",
337
- plan.aws.instanceId,
338
- "--output",
339
- "json",
340
- ],
341
- "macOS campaign source rebind instance preflight",
342
- ).Reservations?.[0]?.Instances?.[0];
343
- if (
344
- !instance ||
345
- instance.State?.Name !== "running" ||
346
- instance.Placement?.HostId !== plan.aws.hostId
347
- ) {
348
- throw new Error("macOS campaign source rebind instance identity mismatch");
349
- }
350
- assertOwnership(instance.Tags, priorPlan);
351
- if (
352
- (instance.Tags || []).some(
353
- (entry) =>
354
- entry.Key === "kungfu:jit-parameter" && String(entry.Value || ""),
355
- )
356
- ) {
357
- throw new Error("macOS campaign instance already has JIT residue");
358
- }
359
- const volumeIds = (instance.BlockDeviceMappings || [])
360
- .map((mapping) => mapping.Ebs?.VolumeId)
361
- .filter(Boolean);
362
- if (volumeIds.length !== 1) {
363
- throw new Error("macOS campaign must have exactly one attached volume");
364
- }
365
- const volume = awsJson(
366
- plan,
367
- profile,
368
- [
369
- "ec2",
370
- "describe-volumes",
371
- "--volume-ids",
372
- volumeIds[0],
373
- "--output",
374
- "json",
375
- ],
376
- "macOS campaign source rebind volume preflight",
377
- ).Volumes?.[0];
378
- if (!volume || volume.State !== "in-use" || volume.Encrypted !== true) {
379
- throw new Error("macOS campaign source rebind volume identity mismatch");
380
- }
381
- assertOwnership(volume.Tags, priorPlan);
382
- return { host, instance, volume };
383
- }
384
-
385
- function assertExactCampaignInventory(plan, profile, resources) {
386
- const ownedHosts =
387
- awsJson(
388
- plan,
389
- profile,
390
- [
391
- "ec2",
392
- "describe-hosts",
393
- "--filter",
394
- `Name=tag:kungfu:campaign-id,Values=${plan.campaign.id}`,
395
- "--output",
396
- "json",
397
- ],
398
- "macOS campaign owned host inventory",
399
- ).Hosts || [];
400
- const ownedInstances = (
401
- awsJson(
402
- plan,
403
- profile,
404
- [
405
- "ec2",
406
- "describe-instances",
407
- "--filters",
408
- `Name=tag:kungfu:campaign-id,Values=${plan.campaign.id}`,
409
- "Name=instance-state-name,Values=pending,running,stopping,stopped,shutting-down",
410
- "--output",
411
- "json",
412
- ],
413
- "macOS campaign owned instance inventory",
414
- ).Reservations || []
415
- ).flatMap((reservation) => reservation.Instances || []);
416
- const ownedVolumes =
417
- awsJson(
418
- plan,
419
- profile,
420
- [
421
- "ec2",
422
- "describe-volumes",
423
- "--filters",
424
- `Name=tag:kungfu:campaign-id,Values=${plan.campaign.id}`,
425
- "--output",
426
- "json",
427
- ],
428
- "macOS campaign owned volume inventory",
429
- ).Volumes || [];
430
- if (
431
- ownedHosts.length !== 1 ||
432
- ownedHosts[0].HostId !== resources.host.HostId ||
433
- ownedInstances.length !== 1 ||
434
- ownedInstances[0].InstanceId !== resources.instance.InstanceId ||
435
- ownedVolumes.length !== 1 ||
436
- ownedVolumes[0].VolumeId !== resources.volume.VolumeId
437
- ) {
438
- throw new Error("macOS campaign resource inventory is not exact");
439
- }
440
- }
441
-
442
- function assertZeroCampaignResidue(plan, profile) {
443
- const parameters =
444
- awsJson(
445
- plan,
446
- profile,
447
- [
448
- "ssm",
449
- "describe-parameters",
450
- "--parameter-filters",
451
- `Key=Name,Option=BeginsWith,Values=/kungfu/burst/macos/${plan.campaign.id}/`,
452
- "--output",
453
- "json",
454
- ],
455
- "macOS campaign JIT parameter inventory",
456
- ).Parameters || [];
457
- if (parameters.length !== 0) {
458
- throw new Error("macOS campaign already has SSM JIT parameters");
459
- }
460
- const runners =
461
- ghJson(
462
- ["api", `repos/${plan.repository}/actions/runners?per_page=100`],
463
- undefined,
464
- "macOS campaign runner inventory",
465
- ).runners || [];
466
- const matchingRunners = runners.filter((runner) =>
467
- (runner.labels || []).some((label) =>
468
- String(label.name || label).startsWith(MACOS_JIT_RUNNER_LABEL_PREFIX),
469
- ),
470
- );
471
- if (matchingRunners.length !== 0) {
472
- throw new Error("macOS campaign already has a registered JIT runner");
473
- }
474
- const evidenceObjects = {};
475
- const historicalTerminalSources =
476
- plan.github.historicalTerminalFailureRuns.map(({ sourceSha }) => sourceSha);
477
- const historicalPreflightSources =
478
- plan.github.historicalPreflightFailureRuns.map(
479
- ({ sourceSha }) => sourceSha,
480
- );
481
- const evidenceSources = [
482
- ...new Set([
483
- plan.previousSource.sha,
484
- ...historicalTerminalSources,
485
- ...historicalPreflightSources,
486
- plan.source.sha,
487
- ]),
488
- ];
489
- for (const sourceSha of evidenceSources) {
490
- const evidence = awsJson(
491
- plan,
492
- profile,
493
- [
494
- "s3api",
495
- "list-objects-v2",
496
- "--bucket",
497
- plan.aws.evidenceBucket,
498
- "--prefix",
499
- `macos/${sourceSha}/`,
500
- "--max-keys",
501
- "1000",
502
- "--output",
503
- "json",
504
- ],
505
- `macOS campaign evidence inventory for ${sourceSha}`,
506
- );
507
- if (evidence.IsTruncated === true) {
508
- throw new Error(
509
- `macOS campaign source ${sourceSha} evidence inventory is incomplete`,
510
- );
511
- }
512
- const objects = (evidence.Contents || []).map((object) => ({
513
- key: String(object.Key || ""),
514
- size: Number(object.Size || 0),
515
- etag: String(object.ETag || ""),
516
- }));
517
- const count = Number(
518
- evidence.KeyCount || (evidence.Contents || []).length || 0,
519
- );
520
- const terminalEvidenceSource =
521
- (sourceSha === plan.previousSource.sha &&
522
- plan.github.priorRunPolicy === "terminal-failure") ||
523
- historicalTerminalSources.includes(sourceSha);
524
- if (
525
- (!terminalEvidenceSource && count !== 0) ||
526
- (terminalEvidenceSource && count === 0)
527
- ) {
528
- throw new Error(
529
- `macOS campaign source ${sourceSha} bootstrap evidence does not match the rebind policy`,
530
- );
531
- }
532
- evidenceObjects[sourceSha] = objects;
533
- }
534
- return {
535
- priorRuns: campaignRunReceipts(plan),
536
- jitParameterCount: parameters.length,
537
- runnerCount: matchingRunners.length,
538
- evidenceObjects,
539
- };
540
- }
541
-
542
- function assertMacosJitSourceRebindPreflight(plan, profile) {
543
- const controlPlane = assertMacosBudgetLaunchGate(plan, profile);
544
- const source = assertSourceLineage(plan);
545
- const resources = readOwnedCampaignResources(plan, profile);
546
- assertExactCampaignInventory(plan, profile, resources);
547
- const residue = assertZeroCampaignResidue(plan, profile);
548
- return {
549
- ...controlPlane,
550
- ...source,
551
- ...residue,
552
- resourceIds: {
553
- hostId: resources.host.HostId,
554
- instanceId: resources.instance.InstanceId,
555
- volumeId: resources.volume.VolumeId,
556
- },
557
- };
558
- }
559
- function setCampaignSourceTags(plan, profile, sourceSha) {
560
- requireSuccess(
561
- commandResult(
562
- "aws",
563
- awsArgs(plan, profile, [
564
- "ec2",
565
- "create-tags",
566
- "--resources",
567
- plan.aws.hostId,
568
- plan.aws.instanceId,
569
- plan.aws.volumeId,
570
- "--tags",
571
- `Key=kungfu:source-sha,Value=${sourceSha}`,
572
- ]),
573
- ),
574
- `macOS campaign source tag update to ${sourceSha}`,
575
- );
576
- }
577
-
578
- function updateCampaignSourceRef(plan, sourceSha, force) {
579
- requireSuccess(
580
- commandResult("gh", [
581
- "api",
582
- "--method",
583
- "PATCH",
584
- sourceRefApiPath(plan),
585
- "-f",
586
- `sha=${sourceSha}`,
587
- "-F",
588
- `force=${force ? "true" : "false"}`,
589
- ]),
590
- `macOS campaign source ref update to ${sourceSha}`,
591
- );
592
- }
593
-
594
- function assertReboundResources(plan, profile) {
595
- const resources = [
596
- awsJson(
597
- plan,
598
- profile,
599
- [
600
- "ec2",
601
- "describe-hosts",
602
- "--host-ids",
603
- plan.aws.hostId,
604
- "--output",
605
- "json",
606
- ],
607
- "macOS campaign rebound host readback",
608
- ).Hosts?.[0],
609
- awsJson(
610
- plan,
611
- profile,
612
- [
613
- "ec2",
614
- "describe-instances",
615
- "--instance-ids",
616
- plan.aws.instanceId,
617
- "--output",
618
- "json",
619
- ],
620
- "macOS campaign rebound instance readback",
621
- ).Reservations?.[0]?.Instances?.[0],
622
- awsJson(
623
- plan,
624
- profile,
625
- [
626
- "ec2",
627
- "describe-volumes",
628
- "--volume-ids",
629
- plan.aws.volumeId,
630
- "--output",
631
- "json",
632
- ],
633
- "macOS campaign rebound volume readback",
634
- ).Volumes?.[0],
635
- ];
636
- for (const resource of resources) {
637
- if (!resource)
638
- throw new Error("macOS campaign source rebind readback failed");
639
- assertOwnership(resource.Tags, plan);
640
- }
641
- const ref = ghJson(
642
- ["api", sourceRefApiPath(plan)],
643
- undefined,
644
- "macOS campaign rebound source ref readback",
645
- );
646
- if (ref.object?.sha !== plan.source.sha) {
647
- throw new Error("macOS campaign rebound source ref readback mismatch");
648
- }
649
- }
650
-
651
- export function executeMacosJitSourceRebind(plan, { profile = "" } = {}) {
652
- if (
653
- plan?.contract !== AWS_MACOS_JIT_CONTROLLER_CONTRACT ||
654
- plan.kind !== "campaign-source-rebind-plan"
655
- ) {
656
- throw new Error("macOS JIT source rebind plan contract is invalid");
657
- }
658
- const preflight = assertMacosJitSourceRebindPreflight(plan, profile);
659
- const runtimePlan = {
660
- ...plan,
661
- aws: { ...plan.aws, volumeId: preflight.resourceIds.volumeId },
662
- };
663
- let tagsUpdated = false;
664
- let refUpdated = false;
665
- try {
666
- setCampaignSourceTags(runtimePlan, profile, plan.source.sha);
667
- tagsUpdated = true;
668
- updateCampaignSourceRef(runtimePlan, plan.source.sha, false);
669
- refUpdated = true;
670
- assertReboundResources(runtimePlan, profile);
671
- } catch (error) {
672
- const rollbackFailures = [];
673
- const rollback = (operation) => {
674
- try {
675
- operation();
676
- } catch (rollbackError) {
677
- rollbackFailures.push(rollbackError.message || String(rollbackError));
678
- }
679
- };
680
- if (refUpdated) {
681
- rollback(() =>
682
- updateCampaignSourceRef(runtimePlan, plan.previousSource.sha, true),
683
- );
684
- }
685
- if (tagsUpdated) {
686
- rollback(() =>
687
- setCampaignSourceTags(runtimePlan, profile, plan.previousSource.sha),
688
- );
689
- }
690
- const detail = rollbackFailures.length
691
- ? `; compensated rollback failed: ${rollbackFailures.join("; ")}`
692
- : "; compensated rollback completed";
693
- throw new Error(`${error.message || error}${detail}`);
694
- }
695
- return {
696
- schemaVersion: 1,
697
- contract: AWS_MACOS_JIT_CONTROLLER_CONTRACT,
698
- kind: "campaign-source-rebind-result",
699
- status:
700
- plan.github.priorRunPolicy === "terminal-failure"
701
- ? "rebound-after-terminal-failure-zero-allocation"
702
- : plan.github.priorRunPolicy === "preflight-failure"
703
- ? "rebound-after-preflight-failure-zero-allocation"
704
- : "rebound-zero-allocation",
705
- repository: plan.repository,
706
- campaign: plan.campaign,
707
- previousSource: plan.previousSource,
708
- source: plan.source,
709
- github: {
710
- workflowId: plan.github.workflowId,
711
- workflowState: preflight.workflowState,
712
- sourceRefUpdated: true,
713
- },
714
- aws: {
715
- region: plan.aws.region,
716
- ...preflight.resourceIds,
717
- sourceTagsUpdated: true,
718
- },
719
- preflight,
720
- paidCapacityCreated: false,
721
- jobDispatched: false,
722
- planDigest: plan.digest,
723
- };
724
- }