@kungfu-tech/buildchain 3.0.2 → 3.0.3-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 (127) hide show
  1. package/AGENTS.md +1 -1
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +27 -1
  4. package/actions/promote-buildchain-ref/README.md +90 -19
  5. package/actions/report-buildchain-issue/README.md +20 -2
  6. package/actions/run-lifecycle/README.md +5 -0
  7. package/actions/validate-config/README.md +19 -1
  8. package/bin/buildchain.mjs +56 -2
  9. package/bin/internal/command-registry.mjs +82 -0
  10. package/bin/internal/trust-release-cli.mjs +1 -0
  11. package/contracts/auditable-demo-media-profiles-v1.json +116 -1
  12. package/contracts/buildchain-v2-residuals-v1.json +146 -0
  13. package/contracts/evidence/auditable-demo-responsive-web-delivery-v1.json +143 -0
  14. package/contracts/evidence/auditable-demo-web-delivery-v1.json +7 -3
  15. package/contracts/fixtures/auditable-demo-responsive-web-delivery-v1/complete-transcript.txt +2 -0
  16. package/contracts/fixtures/auditable-demo-responsive-web-delivery-v1/public-projection.json +16 -0
  17. package/contracts/fixtures/auditable-demo-responsive-web-delivery-v1/scene.json +12 -0
  18. package/dist/site/buildchain-contract.json +188 -46
  19. package/dist/site/buildchain-site.json +181 -90
  20. package/dist/site/capability-registry.json +5 -5
  21. package/dist/site/cli-registry.json +860 -98
  22. package/dist/site/controller-registry.json +118 -5
  23. package/dist/site/kfd-claims.json +233 -19
  24. package/dist/site/kfd-upstream-aggregate.json +1 -1
  25. package/dist/site/manual-registry.json +20 -20
  26. package/dist/site/node-api-registry.json +374 -59
  27. package/dist/site/page-registry.json +156 -65
  28. package/dist/site/public-surface-audit.json +231 -22
  29. package/dist/site/publication-registry.json +4 -4
  30. package/dist/site/release-provenance.json +3 -0
  31. package/dist/site/site-manifest.json +24 -24
  32. package/dist/site/workflow-registry.json +575 -79
  33. package/docs/MAP.md +22 -4
  34. package/docs/auditable-demo.md +54 -13
  35. package/docs/aws-us-elastic-runner-burst-plane.md +381 -0
  36. package/docs/cli.md +119 -12
  37. package/docs/consumer-issue-reporting.md +20 -2
  38. package/docs/dev-alpha-candidate-patrol.md +24 -10
  39. package/docs/github-governance-authority.md +24 -31
  40. package/docs/lifecycle-protocol.md +20 -2
  41. package/docs/migration-inventory.md +24 -5
  42. package/docs/observed-evidence-patrol.md +28 -12
  43. package/docs/ownership.md +19 -1
  44. package/docs/publication-artifacts.md +158 -18
  45. package/docs/publication-authority.md +4 -4
  46. package/docs/publish-transaction.md +78 -31
  47. package/docs/release-candidate.md +24 -0
  48. package/docs/release-flow.md +50 -32
  49. package/docs/release-governance.md +39 -32
  50. package/docs/release-passport.md +4 -0
  51. package/docs/release-propagation.md +57 -12
  52. package/docs/reusable-build-surface.md +218 -90
  53. package/docs/runtime-train-validation.md +20 -2
  54. package/docs/shifu-gate-profiles.md +7 -1
  55. package/docs/stable-candidate-patrol.md +6 -6
  56. package/docs/toolkit-observability.md +22 -0
  57. package/docs/versioning.md +46 -24
  58. package/docs/web-surface-deployments.md +82 -1
  59. package/package.json +7 -5
  60. package/packages/core/README.md +60 -4
  61. package/packages/core/artifact-signing.js +1 -0
  62. package/packages/core/buildchain-contract.js +9 -4
  63. package/packages/core/cache-evidence.js +1 -0
  64. package/packages/core/controller-evidence.js +2 -0
  65. package/packages/core/diagnostics.js +105 -30
  66. package/packages/core/github-governance-authority.js +25 -17
  67. package/packages/core/index.js +40 -0
  68. package/packages/core/paper.js +3226 -0
  69. package/packages/core/public-surface-audit.js +3 -0
  70. package/packages/core/publication-artifact.js +1 -6
  71. package/packages/core/publication-authority.js +1 -0
  72. package/packages/core/publication-control-plane-audit.js +28 -0
  73. package/packages/core/publication-reproducibility.js +947 -0
  74. package/packages/core/publication-sealed-bundle.js +190 -0
  75. package/packages/core/publish-transaction.js +147 -17
  76. package/packages/core/release-candidate.js +234 -0
  77. package/packages/core/release-passport.js +519 -228
  78. package/packages/core/release-propagation.js +175 -1
  79. package/packages/core/stable-release-gate.js +4 -1
  80. package/scripts/artifact-signing-delegation.mjs +268 -0
  81. package/scripts/audit-github-governance.mjs +32 -13
  82. package/scripts/audit-publication-control-plane.mjs +17 -8
  83. package/scripts/auditable-demo.mjs +266 -7
  84. package/scripts/aws-codebuild-toolchain.mjs +285 -0
  85. package/scripts/aws-macos-jit-core.mjs +378 -0
  86. package/scripts/aws-macos-jit.mjs +123 -0
  87. package/scripts/aws-runner-burst-core.mjs +248 -0
  88. package/scripts/aws-runner-burst.mjs +79 -0
  89. package/scripts/aws-windows-jit-core.mjs +374 -0
  90. package/scripts/aws-windows-jit.mjs +121 -0
  91. package/scripts/build-contract-core.mjs +69 -6
  92. package/scripts/build-standalone-binary.mjs +23 -0
  93. package/scripts/buildchain-contract-lock.mjs +1 -1
  94. package/scripts/buildchain-patrol.mjs +1 -1
  95. package/scripts/check-internal-architecture.mjs +69 -2
  96. package/scripts/check-inventory.mjs +70 -2
  97. package/scripts/check-javascript-syntax.mjs +31 -0
  98. package/scripts/check-maintainability.mjs +371 -0
  99. package/scripts/compiler-cache-evidence.mjs +221 -0
  100. package/scripts/dev-alpha-candidate-patrol.mjs +288 -43
  101. package/scripts/dispatch-artifact-signing-authority.mjs +7 -1
  102. package/scripts/finalize-native-artifact-signing-result.mjs +185 -31
  103. package/scripts/gate-profile-core.mjs +6 -1
  104. package/scripts/generate-channel-promotion-workflow.mjs +3 -12
  105. package/scripts/generate-release-candidate-passport.mjs +4 -0
  106. package/scripts/generate-site-bundle.mjs +84 -18
  107. package/scripts/import-artifact-signing-results.mjs +64 -5
  108. package/scripts/inspect-artifact-signing-requests.mjs +52 -14
  109. package/scripts/maintainability-metrics.mjs +339 -0
  110. package/scripts/npm-publish-transaction.mjs +57 -6
  111. package/scripts/observed-evidence.mjs +151 -33
  112. package/scripts/paper.mjs +645 -0
  113. package/scripts/promotion-channel-router.mjs +16 -4
  114. package/scripts/publication-reproducibility.mjs +62 -0
  115. package/scripts/reconcile-github-governance.mjs +8 -1
  116. package/scripts/release-propagation.mjs +39 -0
  117. package/scripts/resolve-artifact-signing-upload-route.mjs +55 -0
  118. package/scripts/resolve-build-contract.mjs +6 -0
  119. package/scripts/route-offline-runners.mjs +236 -0
  120. package/scripts/run-candidate-body-prefix-renderer.mjs +187 -0
  121. package/scripts/run-lifecycle-core.mjs +62 -1
  122. package/scripts/runtime-ref-core.mjs +36 -4
  123. package/scripts/seal-artifact-signing-requests.mjs +64 -12
  124. package/scripts/stable-candidate-qualification.mjs +50 -0
  125. package/scripts/verify-artifact-signing-results.mjs +26 -1
  126. package/scripts/web-surface-core.mjs +272 -86
  127. package/scripts/web-surface-production-decision.mjs +19 -3
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import {
6
+ createMacosJitEvidence,
7
+ macosEc2JitPlan,
8
+ renderMacosJitBootstrap,
9
+ verifyMacosEc2JitQualification,
10
+ } from "./aws-macos-jit-core.mjs";
11
+
12
+ function arg(name, fallback = "") {
13
+ const index = process.argv.indexOf(`--${name}`);
14
+ return index === -1 ? fallback : process.argv[index + 1] || "";
15
+ }
16
+
17
+ function readJson(file) {
18
+ return JSON.parse(fs.readFileSync(path.resolve(file), "utf8"));
19
+ }
20
+
21
+ function writeJson(file, value) {
22
+ const resolved = path.resolve(file);
23
+ fs.mkdirSync(path.dirname(resolved), { recursive: true });
24
+ fs.writeFileSync(resolved, `${JSON.stringify(value, null, 2)}\n`);
25
+ return resolved;
26
+ }
27
+
28
+ export function main() {
29
+ const mode = process.argv[2] || "plan";
30
+ if (mode === "plan") {
31
+ const result = macosEc2JitPlan();
32
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
33
+ return result;
34
+ }
35
+ if (mode === "evidence") {
36
+ const result = createMacosJitEvidence({
37
+ repository: process.env.GITHUB_REPOSITORY,
38
+ sourceSha:
39
+ process.env.BUILDCHAIN_EXPECTED_SOURCE_SHA || process.env.GITHUB_SHA,
40
+ sourceRef:
41
+ process.env.BUILDCHAIN_EXPECTED_SOURCE_REF || process.env.GITHUB_REF,
42
+ githubRunId: process.env.GITHUB_RUN_ID,
43
+ githubRunAttempt: process.env.GITHUB_RUN_ATTEMPT,
44
+ githubJob: process.env.GITHUB_JOB,
45
+ runnerName: process.env.RUNNER_NAME,
46
+ runnerLabels: JSON.parse(
47
+ process.env.BUILDCHAIN_RUNNER_LABELS_JSON || "[]",
48
+ ),
49
+ hostId: process.env.AWS_EC2_MAC_HOST_ID,
50
+ instanceId: process.env.AWS_EC2_INSTANCE_ID,
51
+ instanceType: process.env.AWS_EC2_INSTANCE_TYPE,
52
+ amiId: process.env.AWS_EC2_AMI_ID,
53
+ amiName: process.env.AWS_EC2_AMI_NAME,
54
+ availabilityZone: process.env.AWS_EC2_AVAILABILITY_ZONE,
55
+ hostAllocatedAt: process.env.AWS_EC2_MAC_HOST_ALLOCATED_AT,
56
+ instanceLaunchedAt: process.env.AWS_EC2_LAUNCHED_AT,
57
+ runnerStartedAt: process.env.AWS_EC2_RUNNER_STARTED_AT,
58
+ runnerExitedAt:
59
+ process.env.AWS_EC2_RUNNER_EXITED_AT || new Date().toISOString(),
60
+ cacheMode: process.env.BUILDCHAIN_CHECKOUT_CACHE_MODE || "off",
61
+ });
62
+ const output = writeJson(
63
+ process.env.BUILDCHAIN_MACOS_JIT_EVIDENCE_PATH ||
64
+ ".buildchain/aws-macos-jit.json",
65
+ result,
66
+ );
67
+ process.stdout.write(`${output}\n`);
68
+ return result;
69
+ }
70
+ if (mode === "render-bootstrap") {
71
+ const template = arg(
72
+ "template",
73
+ "infra/aws-us-elastic-runner-burst-plane/macos-jit-bootstrap.sh",
74
+ );
75
+ const output = arg("output");
76
+ if (!output) throw new Error("render-bootstrap requires --output");
77
+ const result = renderMacosJitBootstrap(
78
+ fs.readFileSync(path.resolve(template), "utf8"),
79
+ {
80
+ region: arg("region", "us-east-1"),
81
+ jitParameterName: arg("jit-parameter"),
82
+ evidenceBucket: arg("evidence-bucket"),
83
+ runnerLabel: arg("runner-label"),
84
+ sourceSha: arg("source-sha"),
85
+ githubRunId: arg("github-run-id"),
86
+ githubRunAttempt: arg("github-run-attempt", "1"),
87
+ amiId: arg("ami-id"),
88
+ amiName: arg("ami-name"),
89
+ hostId: arg("host-id"),
90
+ instanceType: arg("instance-type", "mac2.metal"),
91
+ hostAllocatedAt: arg("host-allocated-at"),
92
+ },
93
+ );
94
+ const resolved = path.resolve(output);
95
+ fs.mkdirSync(path.dirname(resolved), { recursive: true });
96
+ fs.writeFileSync(resolved, result);
97
+ process.stdout.write(`${resolved}\n`);
98
+ return resolved;
99
+ }
100
+ if (mode === "verify") {
101
+ const input = arg("input");
102
+ if (!input) throw new Error("verify requires --input");
103
+ const result = verifyMacosEc2JitQualification(readJson(input));
104
+ const output = arg("output");
105
+ if (output) writeJson(output, result);
106
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
107
+ if (!result.qualifying) process.exitCode = 1;
108
+ return result;
109
+ }
110
+ throw new Error(`unsupported aws-macos-jit mode: ${mode}`);
111
+ }
112
+
113
+ if (
114
+ process.argv[1] &&
115
+ import.meta.url === pathToFileURL(process.argv[1]).href
116
+ ) {
117
+ try {
118
+ main();
119
+ } catch (error) {
120
+ console.error(`::error::${error.message || error}`);
121
+ process.exitCode = 1;
122
+ }
123
+ }
@@ -0,0 +1,248 @@
1
+ import crypto from "node:crypto";
2
+
3
+ export const AWS_RUNNER_BURST_CONTRACT =
4
+ "kungfu-buildchain-aws-runner-burst/v1";
5
+
6
+ export const LINUX_CODEBUILD_POC = Object.freeze({
7
+ phase: "linux-codebuild-poc-under-usd-50",
8
+ region: "us-east-1",
9
+ project: "kungfu-buildchain-linux-burst-poc",
10
+ computeType: "BUILD_GENERAL1_XLARGE",
11
+ pricePerMinuteUsd: 0.08,
12
+ timeoutMinutes: 40,
13
+ maxConcurrentBuilds: 2,
14
+ maxAcceptedBuilds: 12,
15
+ budgetLimitUsd: 49,
16
+ minimumAcceptedJobs: 10,
17
+ minimumObservedConcurrency: 2,
18
+ maximumP95QueueStartSeconds: 300,
19
+ });
20
+
21
+ function exactSha(value, label) {
22
+ const normalized = String(value || "").trim().toLowerCase();
23
+ if (!/^[0-9a-f]{40}$/.test(normalized)) {
24
+ throw new Error(`${label} must be an exact 40-character Git SHA`);
25
+ }
26
+ return normalized;
27
+ }
28
+
29
+ function finiteNumber(value, label) {
30
+ const parsed = Number(value);
31
+ if (!Number.isFinite(parsed) || parsed < 0) {
32
+ throw new Error(`${label} must be a non-negative finite number`);
33
+ }
34
+ return parsed;
35
+ }
36
+
37
+ function percentile(values, quantile) {
38
+ if (values.length === 0) return Number.POSITIVE_INFINITY;
39
+ const ordered = [...values].sort((left, right) => left - right);
40
+ const index = Math.max(
41
+ 0,
42
+ Math.min(ordered.length - 1, Math.ceil(quantile * ordered.length) - 1),
43
+ );
44
+ return ordered[index];
45
+ }
46
+
47
+ export function canonicalJson(value) {
48
+ if (Array.isArray(value)) {
49
+ return `[${value.map(canonicalJson).join(",")}]`;
50
+ }
51
+ if (value && typeof value === "object") {
52
+ return `{${Object.keys(value)
53
+ .sort()
54
+ .map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`)
55
+ .join(",")}}`;
56
+ }
57
+ return JSON.stringify(value);
58
+ }
59
+
60
+ export function digest(value) {
61
+ return `sha256:${crypto
62
+ .createHash("sha256")
63
+ .update(canonicalJson(value))
64
+ .digest("hex")}`;
65
+ }
66
+
67
+ export function linuxCodeBuildPlan(overrides = {}) {
68
+ const config = { ...LINUX_CODEBUILD_POC, ...overrides };
69
+ const money = (value) => Math.round(value * 100) / 100;
70
+ const maximumCommittedComputeUsd = money(
71
+ config.pricePerMinuteUsd *
72
+ config.timeoutMinutes *
73
+ config.maxAcceptedBuilds,
74
+ );
75
+ const maximumRaceStopUsd = money(
76
+ config.pricePerMinuteUsd *
77
+ config.timeoutMinutes *
78
+ config.maxConcurrentBuilds,
79
+ );
80
+ const maximumBoundedSpendUsd = money(
81
+ maximumCommittedComputeUsd + maximumRaceStopUsd,
82
+ );
83
+ if (maximumBoundedSpendUsd >= config.budgetLimitUsd) {
84
+ throw new Error(
85
+ `bounded CodeBuild spend ${maximumBoundedSpendUsd.toFixed(2)} must remain below budget ${config.budgetLimitUsd.toFixed(2)}`,
86
+ );
87
+ }
88
+ const plan = {
89
+ schemaVersion: 1,
90
+ contract: AWS_RUNNER_BURST_CONTRACT,
91
+ kind: "phase-plan",
92
+ config,
93
+ costEnvelope: {
94
+ currency: "USD",
95
+ maximumCommittedComputeUsd,
96
+ maximumRaceStopUsd,
97
+ maximumBoundedSpendUsd,
98
+ assumption:
99
+ "At most maxConcurrentBuilds over-cap builds can race; the envelope charges both for their full timeout.",
100
+ },
101
+ invariants: {
102
+ trustedExactSourceOnly: true,
103
+ forkPullRequestsRejectedBeforeCloudRunnerSelection: true,
104
+ ephemeralSingleJobCompute: true,
105
+ signingAndPublicationCredentialsForbidden: true,
106
+ staticAwsCredentialsForbidden: true,
107
+ githubLongLivedCredentialsForbidden: true,
108
+ staleOrMissingCostTelemetryFailsClosed: true,
109
+ zeroIdleCompute: true,
110
+ },
111
+ };
112
+ return { ...plan, digest: digest(plan) };
113
+ }
114
+
115
+ export function createRunnerEvidence({
116
+ provider,
117
+ project,
118
+ repository,
119
+ sourceSha,
120
+ sourceRef,
121
+ runId,
122
+ runAttempt,
123
+ job,
124
+ codeBuildBuildId,
125
+ codeBuildBuildArn,
126
+ codeBuildInitiator,
127
+ observedAt = new Date().toISOString(),
128
+ } = {}) {
129
+ if (provider !== "aws-codebuild") {
130
+ throw new Error("runner evidence provider must be aws-codebuild");
131
+ }
132
+ if (project !== LINUX_CODEBUILD_POC.project) {
133
+ throw new Error(`runner evidence project must be ${LINUX_CODEBUILD_POC.project}`);
134
+ }
135
+ if (!String(repository || "").includes("/")) {
136
+ throw new Error("runner evidence repository must be owner/name");
137
+ }
138
+ const evidence = {
139
+ schemaVersion: 1,
140
+ contract: AWS_RUNNER_BURST_CONTRACT,
141
+ kind: "runner-evidence",
142
+ phase: LINUX_CODEBUILD_POC.phase,
143
+ provider,
144
+ project,
145
+ repository,
146
+ source: {
147
+ sha: exactSha(sourceSha, "sourceSha"),
148
+ ref: String(sourceRef || "").trim(),
149
+ },
150
+ github: {
151
+ runId: String(runId || "").trim(),
152
+ runAttempt: String(runAttempt || "").trim(),
153
+ job: String(job || "").trim(),
154
+ },
155
+ aws: {
156
+ region: process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || "",
157
+ buildId: String(codeBuildBuildId || "").trim(),
158
+ buildArn: String(codeBuildBuildArn || "").trim(),
159
+ initiator: String(codeBuildInitiator || "").trim(),
160
+ },
161
+ observedAt: new Date(observedAt).toISOString(),
162
+ };
163
+ for (const [label, value] of Object.entries({
164
+ runId: evidence.github.runId,
165
+ runAttempt: evidence.github.runAttempt,
166
+ job: evidence.github.job,
167
+ buildId: evidence.aws.buildId,
168
+ buildArn: evidence.aws.buildArn,
169
+ })) {
170
+ if (!value) throw new Error(`runner evidence ${label} is required`);
171
+ }
172
+ return { ...evidence, digest: digest(evidence) };
173
+ }
174
+
175
+ export function verifyLinuxCodeBuildQualification({
176
+ jobs = [],
177
+ actualIncrementalSpendUsd,
178
+ idleBuilds = [],
179
+ activeCloudResources = [],
180
+ telemetryObservedAt,
181
+ observedAt = new Date().toISOString(),
182
+ } = {}) {
183
+ const plan = linuxCodeBuildPlan();
184
+ const issues = [];
185
+ const now = new Date(observedAt).getTime();
186
+ const telemetryTime = new Date(telemetryObservedAt || "").getTime();
187
+ if (
188
+ !Number.isFinite(telemetryTime) ||
189
+ telemetryTime > now ||
190
+ now - telemetryTime > 6 * 60 * 60 * 1000
191
+ ) {
192
+ issues.push("cost-telemetry-missing-or-stale");
193
+ }
194
+ const spend = finiteNumber(
195
+ actualIncrementalSpendUsd,
196
+ "actualIncrementalSpendUsd",
197
+ );
198
+ if (spend >= plan.config.budgetLimitUsd) {
199
+ issues.push("actual-spend-not-below-budget");
200
+ }
201
+ const accepted = jobs.filter(
202
+ (job) =>
203
+ job?.trusted === true &&
204
+ job?.exactSource === true &&
205
+ job?.status === "succeeded",
206
+ );
207
+ if (accepted.length < plan.config.minimumAcceptedJobs) {
208
+ issues.push("insufficient-trusted-exact-source-jobs");
209
+ }
210
+ const queueSeconds = accepted.map((job) =>
211
+ finiteNumber(job.queueStartSeconds, "job.queueStartSeconds"),
212
+ );
213
+ const p95QueueStartSeconds = percentile(queueSeconds, 0.95);
214
+ if (p95QueueStartSeconds > plan.config.maximumP95QueueStartSeconds) {
215
+ issues.push("queue-start-p95-exceeds-five-minutes");
216
+ }
217
+ const peakConcurrency = jobs.reduce(
218
+ (peak, job) => Math.max(peak, Number(job?.observedConcurrency || 0)),
219
+ 0,
220
+ );
221
+ if (peakConcurrency < plan.config.minimumObservedConcurrency) {
222
+ issues.push("concurrency-two-not-observed");
223
+ }
224
+ if (idleBuilds.length > 0) issues.push("idle-builds-remain");
225
+ if (activeCloudResources.length > 0) {
226
+ issues.push("active-cloud-resources-remain");
227
+ }
228
+ const result = {
229
+ schemaVersion: 1,
230
+ contract: AWS_RUNNER_BURST_CONTRACT,
231
+ kind: "phase-verification",
232
+ phase: plan.config.phase,
233
+ status: issues.length === 0 ? "passed" : "failed",
234
+ qualifying: issues.length === 0,
235
+ metrics: {
236
+ acceptedJobs: accepted.length,
237
+ peakConcurrency,
238
+ p95QueueStartSeconds,
239
+ actualIncrementalSpendUsd: spend,
240
+ idleBuilds: idleBuilds.length,
241
+ activeCloudResources: activeCloudResources.length,
242
+ },
243
+ issues,
244
+ planDigest: plan.digest,
245
+ observedAt: new Date(observedAt).toISOString(),
246
+ };
247
+ return { ...result, digest: digest(result) };
248
+ }
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import {
6
+ createRunnerEvidence,
7
+ linuxCodeBuildPlan,
8
+ verifyLinuxCodeBuildQualification,
9
+ } from "./aws-runner-burst-core.mjs";
10
+
11
+ function readArg(name, fallback = "") {
12
+ const index = process.argv.indexOf(`--${name}`);
13
+ return index === -1 ? fallback : process.argv[index + 1] || "";
14
+ }
15
+
16
+ function writeJson(outputPath, value) {
17
+ const resolved = path.resolve(outputPath);
18
+ fs.mkdirSync(path.dirname(resolved), { recursive: true });
19
+ fs.writeFileSync(resolved, `${JSON.stringify(value, null, 2)}\n`);
20
+ return resolved;
21
+ }
22
+
23
+ function readJson(inputPath) {
24
+ return JSON.parse(fs.readFileSync(path.resolve(inputPath), "utf8"));
25
+ }
26
+
27
+ export function main() {
28
+ const mode = process.argv[2] || "plan";
29
+ if (mode === "plan") {
30
+ const result = linuxCodeBuildPlan();
31
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
32
+ return result;
33
+ }
34
+ if (mode === "evidence") {
35
+ const result = createRunnerEvidence({
36
+ provider: process.env.BUILDCHAIN_BURST_PROVIDER,
37
+ project: process.env.BUILDCHAIN_BURST_PROJECT,
38
+ repository: process.env.BUILDCHAIN_BURST_SOURCE_REPOSITORY,
39
+ sourceSha: process.env.BUILDCHAIN_BURST_SOURCE_SHA,
40
+ sourceRef: process.env.BUILDCHAIN_BURST_SOURCE_REF,
41
+ runId: process.env.BUILDCHAIN_BURST_RUN_ID,
42
+ runAttempt: process.env.BUILDCHAIN_BURST_RUN_ATTEMPT,
43
+ job: process.env.BUILDCHAIN_BURST_JOB,
44
+ codeBuildBuildId: process.env.CODEBUILD_BUILD_ID,
45
+ codeBuildBuildArn: process.env.CODEBUILD_BUILD_ARN,
46
+ codeBuildInitiator: process.env.CODEBUILD_INITIATOR,
47
+ });
48
+ const output = writeJson(
49
+ process.env.BUILDCHAIN_BURST_EVIDENCE_PATH ||
50
+ ".buildchain/aws-runner-burst.json",
51
+ result,
52
+ );
53
+ process.stdout.write(`${output}\n`);
54
+ return result;
55
+ }
56
+ if (mode === "verify-linux") {
57
+ const inputPath = readArg("input");
58
+ if (!inputPath) throw new Error("verify-linux requires --input");
59
+ const result = verifyLinuxCodeBuildQualification(readJson(inputPath));
60
+ const outputPath = readArg("output");
61
+ if (outputPath) writeJson(outputPath, result);
62
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
63
+ if (!result.qualifying) process.exitCode = 1;
64
+ return result;
65
+ }
66
+ throw new Error(`unsupported aws-runner-burst mode: ${mode}`);
67
+ }
68
+
69
+ if (
70
+ process.argv[1] &&
71
+ import.meta.url === pathToFileURL(process.argv[1]).href
72
+ ) {
73
+ try {
74
+ main();
75
+ } catch (error) {
76
+ console.error(`::error::${error.message || error}`);
77
+ process.exitCode = 1;
78
+ }
79
+ }