@kungfu-tech/buildchain 3.0.5-alpha.7 → 3.0.5

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 (62) hide show
  1. package/README.md +0 -31
  2. package/actions/macos-credential-island/README.md +0 -8
  3. package/bin/buildchain.mjs +1 -1
  4. package/contracts/auditable-demo-media-profiles-v1.json +0 -61
  5. package/contracts/evidence/auditable-demo-responsive-web-delivery-v1.json +3 -3
  6. package/contracts/evidence/auditable-demo-web-delivery-v1.json +3 -3
  7. package/dist/site/buildchain-contract.json +25 -86
  8. package/dist/site/buildchain-site.json +38 -64
  9. package/dist/site/capability-registry.json +1 -1
  10. package/dist/site/cli-registry.json +2 -6
  11. package/dist/site/controller-registry.json +3 -11
  12. package/dist/site/kfd-claims.json +11 -53
  13. package/dist/site/kfd-upstream-aggregate.json +1 -1
  14. package/dist/site/manual-registry.json +7 -7
  15. package/dist/site/node-api-registry.json +31 -213
  16. package/dist/site/page-registry.json +20 -50
  17. package/dist/site/public-surface-audit.json +8 -98
  18. package/dist/site/publication-authority-registry.json +1 -42
  19. package/dist/site/publication-registry.json +4 -4
  20. package/dist/site/site-manifest.json +11 -11
  21. package/dist/site/workflow-registry.json +2 -67
  22. package/docs/auditable-demo.md +7 -74
  23. package/docs/aws-us-elastic-runner-burst-plane.md +16 -151
  24. package/docs/cli-reference.md +2 -24
  25. package/docs/node-api-reference.md +72 -90
  26. package/docs/publish-transaction.md +5 -7
  27. package/docs/release-flow.md +0 -4
  28. package/docs/release-governance.md +0 -39
  29. package/docs/reusable-build-surface.md +0 -9
  30. package/package.json +1 -1
  31. package/packages/core/buildchain-contract.js +1 -36
  32. package/packages/core/buildchain-publication-authority.js +0 -2
  33. package/packages/core/public-surface-audit.js +35 -4
  34. package/packages/core/release-propagation-release.js +18 -61
  35. package/packages/core/release-propagation-work.js +3 -7
  36. package/packages/core/release-propagation.js +2 -12
  37. package/scripts/auditable-demo-renditions.mjs +2 -7
  38. package/scripts/auditable-demo.mjs +21 -27
  39. package/scripts/aws-windows-jit-controller-core.mjs +2 -20
  40. package/scripts/aws-windows-jit-controller.mjs +109 -184
  41. package/scripts/aws-windows-jit-core.mjs +3 -21
  42. package/scripts/aws-windows-jit.mjs +0 -2
  43. package/scripts/build-standalone-binary.mjs +0 -4
  44. package/scripts/buildchain-cli-help.mjs +1 -2
  45. package/scripts/capture-package-release-propagation.mjs +1 -24
  46. package/scripts/check-inventory.mjs +0 -5
  47. package/scripts/finalize-native-artifact-signing-result.mjs +15 -37
  48. package/scripts/generate-site-bundle.mjs +0 -2
  49. package/scripts/locked-source-checkout.mjs +3 -17
  50. package/scripts/release-propagation.mjs +2 -77
  51. package/contracts/auditable-demo-scenario-v1.schema.json +0 -153
  52. package/packages/core/release-propagation-pickup.js +0 -386
  53. package/packages/core/workflow-call-contract.js +0 -308
  54. package/packages/core/workflow-yaml-contract.js +0 -272
  55. package/scripts/auditable-demo-platform.mjs +0 -506
  56. package/scripts/aws-macos-jit-controller-core.mjs +0 -389
  57. package/scripts/aws-macos-jit-controller-runtime.mjs +0 -82
  58. package/scripts/aws-macos-jit-controller.mjs +0 -558
  59. package/scripts/aws-macos-jit-job-controller.mjs +0 -401
  60. package/scripts/aws-windows-jit-campaign-core.mjs +0 -296
  61. package/scripts/aws-windows-jit-campaign.mjs +0 -202
  62. package/scripts/workflow-call-contract.mjs +0 -133
@@ -1,389 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
-
3
- import { digest } from "./aws-runner-burst-core.mjs";
4
- import {
5
- MACOS_EC2_JIT,
6
- macosJitRunnerLabel,
7
- macosJitRunnerLabels,
8
- } from "./aws-macos-jit-core.mjs";
9
-
10
- export const AWS_MACOS_JIT_CONTROLLER_CONTRACT =
11
- "kungfu-buildchain-aws-macos-jit-controller/v1";
12
-
13
- function exact(value, pattern, label) {
14
- const normalized = String(value || "").trim();
15
- if (!pattern.test(normalized)) throw new Error(`${label} is invalid`);
16
- return normalized;
17
- }
18
-
19
- function exactSha(value) {
20
- return exact(value, /^[0-9a-f]{40}$/i, "sourceSha").toLowerCase();
21
- }
22
-
23
- function iso(value, label) {
24
- const parsed = new Date(value || "");
25
- if (!Number.isFinite(parsed.getTime())) {
26
- throw new Error(`${label} must be an ISO timestamp`);
27
- }
28
- return parsed.toISOString();
29
- }
30
-
31
- function tag(key, value) {
32
- return { Key: key, Value: String(value) };
33
- }
34
-
35
- function repository(value) {
36
- const resolved = exact(
37
- value || MACOS_EC2_JIT.repository,
38
- /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/,
39
- "repository",
40
- );
41
- if (resolved !== MACOS_EC2_JIT.repository) {
42
- throw new Error(`repository must be ${MACOS_EC2_JIT.repository}`);
43
- }
44
- return resolved;
45
- }
46
-
47
- function source(values) {
48
- return {
49
- sha: exactSha(values.sourceSha),
50
- ref: exact(
51
- values.sourceRef,
52
- /^refs\/heads\/[A-Za-z0-9._/-]+$/,
53
- "sourceRef",
54
- ),
55
- };
56
- }
57
-
58
- function campaignId(value) {
59
- return exact(value, /^[a-z0-9][a-z0-9-]{2,47}$/, "campaignId");
60
- }
61
-
62
- function qualificationId(value) {
63
- return exact(value, /^mac-(?:smoke-0[12]|full-01)$/, "qualificationId");
64
- }
65
-
66
- function commonAws(values) {
67
- const region = exact(
68
- values.region || MACOS_EC2_JIT.region,
69
- /^us-[a-z]+-\d$/,
70
- "region",
71
- );
72
- if (region !== MACOS_EC2_JIT.region) {
73
- throw new Error(`region must be ${MACOS_EC2_JIT.region}`);
74
- }
75
- const instanceType = exact(
76
- values.instanceType || MACOS_EC2_JIT.instanceType,
77
- /^mac2\.metal$/,
78
- "instanceType",
79
- );
80
- if (instanceType !== MACOS_EC2_JIT.instanceType) {
81
- throw new Error(`instanceType must be ${MACOS_EC2_JIT.instanceType}`);
82
- }
83
- return {
84
- region,
85
- availabilityZone: exact(
86
- values.availabilityZone,
87
- /^us-[a-z]+-\d[a-z]$/,
88
- "availabilityZone",
89
- ),
90
- instanceType,
91
- amiId: exact(values.amiId, /^ami-[0-9a-f]+$/, "amiId"),
92
- amiName: exact(values.amiName, /^[A-Za-z0-9._-]+$/, "amiName"),
93
- subnetId: exact(values.subnetId, /^subnet-[0-9a-f]+$/, "subnetId"),
94
- securityGroupId: exact(
95
- values.securityGroupId,
96
- /^sg-[0-9a-f]+$/,
97
- "securityGroupId",
98
- ),
99
- instanceProfileName: exact(
100
- values.instanceProfileName,
101
- /^[A-Za-z0-9+=,.@_-]{1,128}$/,
102
- "instanceProfileName",
103
- ),
104
- evidenceBucket: exact(
105
- values.evidenceBucket,
106
- /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/,
107
- "evidenceBucket",
108
- ),
109
- };
110
- }
111
-
112
- function ownershipTags({ id, sourceSha }) {
113
- return [
114
- tag("kungfu:owner", "buildchain"),
115
- tag("kungfu:plane", "aws-us-elastic-runner-burst"),
116
- tag("kungfu:provider", "macos-ec2-jit"),
117
- tag("kungfu:campaign-id", id),
118
- tag("kungfu:source-sha", sourceSha),
119
- ];
120
- }
121
-
122
- export function createMacosJitCampaignPlan(values = {}) {
123
- const id = campaignId(values.campaignId);
124
- const boundSource = source(values);
125
- const aws = commonAws(values);
126
- const tags = ownershipTags({ id, sourceSha: boundSource.sha });
127
- const createdAt = iso(values.createdAt, "createdAt");
128
- const plan = {
129
- schemaVersion: 1,
130
- contract: AWS_MACOS_JIT_CONTROLLER_CONTRACT,
131
- kind: "campaign-launch-plan",
132
- repository: repository(values.repository),
133
- campaign: { id, createdAt },
134
- source: boundSource,
135
- aws: {
136
- ...aws,
137
- hostTags: tags,
138
- instanceTags: tags,
139
- volumeTags: tags,
140
- hostClientToken: `kungfu-mac-host-${id}`,
141
- instanceClientToken: `kungfu-mac-instance-${id}`,
142
- rootVolume: {
143
- deviceName: "/dev/sda1",
144
- deleteOnTermination: true,
145
- encrypted: true,
146
- volumeSizeGiB: 200,
147
- volumeType: "gp3",
148
- },
149
- metadata: {
150
- httpEndpoint: "enabled",
151
- httpTokens: "required",
152
- httpPutResponseHopLimit: 1,
153
- instanceMetadataTags: "disabled",
154
- },
155
- },
156
- safety: {
157
- applyMode: values.execute === true ? "execute" : "dry-run",
158
- exactSourceRequired: true,
159
- activeHostCeiling: MACOS_EC2_JIT.maxAcceptedHosts,
160
- activeInstanceCeiling: 1,
161
- awsDryRunRequiredBeforeAllocation: true,
162
- awsDryRunRequiredBeforeLaunch: true,
163
- retainHostOnInstanceLaunchFailure: true,
164
- minimumHostAllocationHours: MACOS_EC2_JIT.minimumHostAllocationHours,
165
- maximumHostAllocationHours: MACOS_EC2_JIT.maximumHostAllocationHours,
166
- cleanupOwner: "scheduled-card-scoped-reaper",
167
- },
168
- };
169
- return { ...plan, digest: digest(plan) };
170
- }
171
-
172
- export function macosAllocateHostsArgs(plan, { dryRun = false } = {}) {
173
- if (
174
- plan?.contract !== AWS_MACOS_JIT_CONTROLLER_CONTRACT ||
175
- plan.kind !== "campaign-launch-plan"
176
- ) {
177
- throw new Error("macOS JIT campaign launch plan contract is invalid");
178
- }
179
- const args = [
180
- "ec2",
181
- "allocate-hosts",
182
- "--availability-zone",
183
- plan.aws.availabilityZone,
184
- "--instance-type",
185
- plan.aws.instanceType,
186
- "--quantity",
187
- "1",
188
- "--auto-placement",
189
- "off",
190
- "--client-token",
191
- plan.aws.hostClientToken,
192
- "--tag-specifications",
193
- JSON.stringify([
194
- { ResourceType: "dedicated-host", Tags: plan.aws.hostTags },
195
- ]),
196
- "--output",
197
- "json",
198
- ];
199
- if (dryRun) args.splice(2, 0, "--dry-run");
200
- return args;
201
- }
202
-
203
- export function macosRunInstancesArgs(plan, { hostId, dryRun = false } = {}) {
204
- if (
205
- plan?.contract !== AWS_MACOS_JIT_CONTROLLER_CONTRACT ||
206
- plan.kind !== "campaign-launch-plan"
207
- ) {
208
- throw new Error("macOS JIT campaign launch plan contract is invalid");
209
- }
210
- const resolvedHostId = exact(hostId, /^h-[0-9a-f]+$/, "hostId");
211
- const args = [
212
- "ec2",
213
- "run-instances",
214
- "--image-id",
215
- plan.aws.amiId,
216
- "--instance-type",
217
- plan.aws.instanceType,
218
- "--count",
219
- "1",
220
- "--client-token",
221
- plan.aws.instanceClientToken,
222
- "--iam-instance-profile",
223
- JSON.stringify({ Name: plan.aws.instanceProfileName }),
224
- "--subnet-id",
225
- plan.aws.subnetId,
226
- "--security-group-ids",
227
- plan.aws.securityGroupId,
228
- "--associate-public-ip-address",
229
- "--placement",
230
- JSON.stringify({ HostId: resolvedHostId, Tenancy: "host" }),
231
- "--block-device-mappings",
232
- JSON.stringify([
233
- {
234
- DeviceName: plan.aws.rootVolume.deviceName,
235
- Ebs: {
236
- DeleteOnTermination: plan.aws.rootVolume.deleteOnTermination,
237
- Encrypted: plan.aws.rootVolume.encrypted,
238
- VolumeSize: plan.aws.rootVolume.volumeSizeGiB,
239
- VolumeType: plan.aws.rootVolume.volumeType,
240
- },
241
- },
242
- ]),
243
- "--metadata-options",
244
- JSON.stringify({
245
- HttpEndpoint: plan.aws.metadata.httpEndpoint,
246
- HttpTokens: plan.aws.metadata.httpTokens,
247
- HttpPutResponseHopLimit: plan.aws.metadata.httpPutResponseHopLimit,
248
- InstanceMetadataTags: plan.aws.metadata.instanceMetadataTags,
249
- }),
250
- "--instance-initiated-shutdown-behavior",
251
- "stop",
252
- "--tag-specifications",
253
- JSON.stringify([
254
- { ResourceType: "instance", Tags: plan.aws.instanceTags },
255
- { ResourceType: "volume", Tags: plan.aws.volumeTags },
256
- ]),
257
- "--output",
258
- "json",
259
- ];
260
- if (dryRun) args.splice(2, 0, "--dry-run");
261
- return args;
262
- }
263
-
264
- export function createMacosJitJobPlan(values = {}) {
265
- const id = campaignId(values.campaignId);
266
- const boundSource = source(values);
267
- const qualification = qualificationId(values.qualificationId);
268
- const runnerLabel = macosJitRunnerLabel(values.runnerLabel);
269
- const expectedLabel = `${MACOS_EC2_JIT.labelPrefix}${qualification}`;
270
- if (runnerLabel !== expectedLabel) {
271
- throw new Error(`runnerLabel must be ${expectedLabel}`);
272
- }
273
- const runId = exact(values.runId, /^\d+$/, "runId");
274
- const runAttempt = exact(
275
- values.runAttempt || "1",
276
- /^[1-9]\d*$/,
277
- "runAttempt",
278
- );
279
- const hostAllocatedAt = iso(values.hostAllocatedAt, "hostAllocatedAt");
280
- const jitParameterName = `${MACOS_EC2_JIT.jitParameterPrefix}${id}/${runId}/${runAttempt}/${qualification}`;
281
- const plan = {
282
- schemaVersion: 1,
283
- contract: AWS_MACOS_JIT_CONTROLLER_CONTRACT,
284
- kind: "job-run-plan",
285
- repository: repository(values.repository),
286
- campaign: { id },
287
- source: boundSource,
288
- github: {
289
- runId,
290
- runAttempt,
291
- jobId: exact(values.jobId, /^\d+$/, "jobId"),
292
- qualificationId: qualification,
293
- event: "workflow_dispatch",
294
- },
295
- runner: {
296
- name: exact(
297
- values.runnerName,
298
- /^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/,
299
- "runnerName",
300
- ),
301
- label: runnerLabel,
302
- labels: macosJitRunnerLabels(runnerLabel),
303
- oneJobJit: true,
304
- },
305
- aws: {
306
- ...commonAws(values),
307
- hostId: exact(values.hostId, /^h-[0-9a-f]+$/, "hostId"),
308
- instanceId: exact(values.instanceId, /^i-[0-9a-f]+$/, "instanceId"),
309
- hostAllocatedAt,
310
- jitParameterName,
311
- },
312
- safety: {
313
- applyMode: values.execute === true ? "execute" : "dry-run",
314
- exactSourceRequired: true,
315
- queuedJobRequired: true,
316
- sameCampaignHostRequired: true,
317
- sameCampaignInstanceRequired: true,
318
- jitConfigTransport: "0600-temporary-file-to-ssm-secure-string",
319
- bootstrapTransport: "0600-temporary-file-to-ssm-command",
320
- jitConfigInBootstrap: false,
321
- jitConfigInArgv: false,
322
- cleanupOnCommandFailure: ["ssm-parameter", "github-runner-registration"],
323
- },
324
- };
325
- return { ...plan, digest: digest(plan) };
326
- }
327
-
328
- export function createMacosJitClosePlan(values = {}) {
329
- const id = campaignId(values.campaignId);
330
- const boundSource = source(values);
331
- const hostAllocatedAt = iso(values.hostAllocatedAt, "hostAllocatedAt");
332
- const observedAt = iso(values.observedAt, "observedAt");
333
- const allocationHours =
334
- (new Date(observedAt).getTime() - new Date(hostAllocatedAt).getTime()) /
335
- 3_600_000;
336
- if (allocationHours < MACOS_EC2_JIT.minimumHostAllocationHours) {
337
- throw new Error(
338
- `Dedicated Host cannot be closed before ${MACOS_EC2_JIT.minimumHostAllocationHours} hours`,
339
- );
340
- }
341
- const plan = {
342
- schemaVersion: 1,
343
- contract: AWS_MACOS_JIT_CONTROLLER_CONTRACT,
344
- kind: "campaign-close-plan",
345
- repository: repository(values.repository),
346
- campaign: { id },
347
- source: boundSource,
348
- aws: {
349
- ...commonAws(values),
350
- hostId: exact(values.hostId, /^h-[0-9a-f]+$/, "hostId"),
351
- instanceId: exact(values.instanceId, /^i-[0-9a-f]+$/, "instanceId"),
352
- hostAllocatedAt,
353
- },
354
- lifecycle: {
355
- observedAt,
356
- allocationHours,
357
- minimumHostAllocationHours: MACOS_EC2_JIT.minimumHostAllocationHours,
358
- maximumHostAllocationHours: MACOS_EC2_JIT.maximumHostAllocationHours,
359
- },
360
- safety: {
361
- applyMode: values.execute === true ? "execute" : "dry-run",
362
- exactCampaignOwnershipRequired: true,
363
- instanceTerminationRequiredBeforeRelease: true,
364
- encryptedDeleteOnTerminationVolumeRequired: true,
365
- awsDryRunRequiredBeforeRelease: true,
366
- reaperFallback: true,
367
- },
368
- };
369
- return { ...plan, digest: digest(plan) };
370
- }
371
-
372
- export function macosReleaseHostsArgs(plan, { dryRun = false } = {}) {
373
- if (
374
- plan?.contract !== AWS_MACOS_JIT_CONTROLLER_CONTRACT ||
375
- plan.kind !== "campaign-close-plan"
376
- ) {
377
- throw new Error("macOS JIT campaign close plan contract is invalid");
378
- }
379
- const args = [
380
- "ec2",
381
- "release-hosts",
382
- "--host-ids",
383
- plan.aws.hostId,
384
- "--output",
385
- "json",
386
- ];
387
- if (dryRun) args.splice(2, 0, "--dry-run");
388
- return args;
389
- }
@@ -1,82 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
-
3
- import { spawnSync } from "node:child_process";
4
-
5
- export function commandResult(command, args, options = {}) {
6
- const result = spawnSync(command, args, {
7
- encoding: "utf8",
8
- maxBuffer: 16 * 1024 * 1024,
9
- stdio: [options.input === undefined ? "ignore" : "pipe", "pipe", "pipe"],
10
- input: options.input,
11
- env: process.env,
12
- });
13
- return {
14
- status: result.status,
15
- stdout: String(result.stdout || ""),
16
- stderr: String(result.stderr || result.error?.message || ""),
17
- };
18
- }
19
-
20
- export function requireSuccess(result, label) {
21
- if (result.status !== 0) {
22
- const detail = (result.stderr || result.stdout).trim().slice(0, 2000);
23
- throw new Error(`${label} failed${detail ? `: ${detail}` : ""}`);
24
- }
25
- return result.stdout;
26
- }
27
-
28
- export function jsonResult(result, label) {
29
- const output = requireSuccess(result, label);
30
- try {
31
- return JSON.parse(output);
32
- } catch {
33
- throw new Error(`${label} returned invalid JSON`);
34
- }
35
- }
36
-
37
- export function awsArgs(plan, profile, serviceArgs) {
38
- return [
39
- ...(profile ? ["--profile", profile] : []),
40
- "--region",
41
- plan.aws.region,
42
- ...serviceArgs,
43
- ];
44
- }
45
-
46
- export function awsJson(plan, profile, serviceArgs, label) {
47
- return jsonResult(
48
- commandResult("aws", awsArgs(plan, profile, serviceArgs)),
49
- label,
50
- );
51
- }
52
-
53
- export function ghJson(args, input, label) {
54
- return jsonResult(commandResult("gh", args, { input }), label);
55
- }
56
-
57
- export function assertOwnership(resourceTags, plan) {
58
- const observed = Object.fromEntries(
59
- (resourceTags || []).map((entry) => [entry.Key, String(entry.Value)]),
60
- );
61
- const expected = {
62
- "kungfu:owner": "buildchain",
63
- "kungfu:plane": "aws-us-elastic-runner-burst",
64
- "kungfu:provider": "macos-ec2-jit",
65
- "kungfu:campaign-id": plan.campaign.id,
66
- "kungfu:source-sha": plan.source.sha,
67
- };
68
- for (const [key, value] of Object.entries(expected)) {
69
- if (observed[key] !== value) {
70
- throw new Error(`AWS resource ownership tag ${key} mismatch`);
71
- }
72
- }
73
- }
74
-
75
- export function assertDryRun(result, label) {
76
- if (
77
- result.status === 0 ||
78
- !/DryRunOperation/.test(`${result.stdout}\n${result.stderr}`)
79
- ) {
80
- throw new Error(`${label} did not return DryRunOperation`);
81
- }
82
- }