@kungfu-tech/buildchain 3.0.9-alpha.1 → 3.0.9-alpha.11
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/actions/promote-buildchain-ref/README.md +4 -0
- package/bin/buildchain.mjs +9 -1
- package/bin/internal/trust-release-release-handlers.mjs +1 -0
- 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 +92 -27
- package/dist/site/buildchain-site.json +351 -38
- package/dist/site/capability-registry.json +7 -6
- package/dist/site/cli-registry.json +43 -3
- package/dist/site/controller-registry.json +6 -2
- package/dist/site/kfd-claims.json +223 -12
- package/dist/site/kfd-upstream-aggregate.json +9 -9
- package/dist/site/manual-registry.json +23 -7
- package/dist/site/node-api-registry.json +7926 -4533
- package/dist/site/page-registry.json +332 -27
- package/dist/site/public-surface-audit.json +359 -11
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-passport-check-manifest.json +1 -0
- package/dist/site/release-provenance.json +14 -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 +21 -10
- 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 +202 -15
- 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 +402 -204
- package/docs/release-governance.md +10 -7
- package/docs/release-passport.md +18 -2
- package/docs/versioning.md +4 -1
- package/package.json +23 -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/buildchain-delivery-self-dogfood.js +395 -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 +129 -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/published-delivery-authority.js +247 -0
- package/packages/core/release-passport-contract.js +10 -6
- package/packages/core/release-passport.js +37 -13
- 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 +10 -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 +48 -6
- 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 +25 -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
|
@@ -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
|
+
}
|
|
@@ -28,7 +28,169 @@ function priorOwnershipPlan(plan) {
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
function
|
|
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) {
|
|
32
194
|
const branch = sourceRefName(plan.source.ref);
|
|
33
195
|
const response = ghJson(
|
|
34
196
|
[
|
|
@@ -46,7 +208,8 @@ function campaignRunsWithNoWork(plan) {
|
|
|
46
208
|
}
|
|
47
209
|
const receipts = [];
|
|
48
210
|
for (const run of matching) {
|
|
49
|
-
|
|
211
|
+
const classification = classifyCampaignRun(plan, run);
|
|
212
|
+
if (run.head_sha !== classification.expectedSourceSha) {
|
|
50
213
|
throw new Error(
|
|
51
214
|
`macOS campaign run ${run.id} is bound to an unexpected source`,
|
|
52
215
|
);
|
|
@@ -68,19 +231,41 @@ function campaignRunsWithNoWork(plan) {
|
|
|
68
231
|
const artifactCount = Number(
|
|
69
232
|
artifacts.total_count || (artifacts.artifacts || []).length || 0,
|
|
70
233
|
);
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
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,
|
|
74
249
|
);
|
|
75
250
|
}
|
|
76
251
|
receipts.push({
|
|
77
252
|
runId: String(run.id),
|
|
253
|
+
sourceSha: String(run.head_sha || ""),
|
|
254
|
+
classification: classification.classification,
|
|
78
255
|
status: String(run.status || "unknown"),
|
|
79
256
|
conclusion: String(run.conclusion || ""),
|
|
80
257
|
jobCount,
|
|
81
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
|
+
})),
|
|
82
266
|
});
|
|
83
267
|
}
|
|
268
|
+
assertExactReceiptInventories(plan, receipts);
|
|
84
269
|
return receipts;
|
|
85
270
|
}
|
|
86
271
|
|
|
@@ -286,8 +471,22 @@ function assertZeroCampaignResidue(plan, profile) {
|
|
|
286
471
|
if (matchingRunners.length !== 0) {
|
|
287
472
|
throw new Error("macOS campaign already has a registered JIT runner");
|
|
288
473
|
}
|
|
289
|
-
const
|
|
290
|
-
|
|
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) {
|
|
291
490
|
const evidence = awsJson(
|
|
292
491
|
plan,
|
|
293
492
|
profile,
|
|
@@ -299,27 +498,44 @@ function assertZeroCampaignResidue(plan, profile) {
|
|
|
299
498
|
"--prefix",
|
|
300
499
|
`macos/${sourceSha}/`,
|
|
301
500
|
"--max-keys",
|
|
302
|
-
"
|
|
501
|
+
"1000",
|
|
303
502
|
"--output",
|
|
304
503
|
"json",
|
|
305
504
|
],
|
|
306
505
|
`macOS campaign evidence inventory for ${sourceSha}`,
|
|
307
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
|
+
}));
|
|
308
517
|
const count = Number(
|
|
309
518
|
evidence.KeyCount || (evidence.Contents || []).length || 0,
|
|
310
519
|
);
|
|
311
|
-
|
|
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
|
+
) {
|
|
312
528
|
throw new Error(
|
|
313
|
-
`macOS campaign source ${sourceSha}
|
|
529
|
+
`macOS campaign source ${sourceSha} bootstrap evidence does not match the rebind policy`,
|
|
314
530
|
);
|
|
315
531
|
}
|
|
316
|
-
|
|
532
|
+
evidenceObjects[sourceSha] = objects;
|
|
317
533
|
}
|
|
318
534
|
return {
|
|
319
|
-
priorRuns:
|
|
535
|
+
priorRuns: campaignRunReceipts(plan),
|
|
320
536
|
jitParameterCount: parameters.length,
|
|
321
537
|
runnerCount: matchingRunners.length,
|
|
322
|
-
|
|
538
|
+
evidenceObjects,
|
|
323
539
|
};
|
|
324
540
|
}
|
|
325
541
|
|
|
@@ -480,7 +696,12 @@ export function executeMacosJitSourceRebind(plan, { profile = "" } = {}) {
|
|
|
480
696
|
schemaVersion: 1,
|
|
481
697
|
contract: AWS_MACOS_JIT_CONTROLLER_CONTRACT,
|
|
482
698
|
kind: "campaign-source-rebind-result",
|
|
483
|
-
status:
|
|
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",
|
|
484
705
|
repository: plan.repository,
|
|
485
706
|
campaign: plan.campaign,
|
|
486
707
|
previousSource: plan.previousSource,
|
|
@@ -25,6 +25,17 @@ function writeJson(file, value) {
|
|
|
25
25
|
return resolved;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
export function writeMacosJitQualificationEnvironment(
|
|
29
|
+
githubEnv = process.env.GITHUB_ENV,
|
|
30
|
+
) {
|
|
31
|
+
if (!githubEnv) return false;
|
|
32
|
+
fs.appendFileSync(
|
|
33
|
+
path.resolve(githubEnv),
|
|
34
|
+
"BUILDCHAIN_SIGNING_REQUESTS_ENABLED=false\n",
|
|
35
|
+
);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
28
39
|
export function main() {
|
|
29
40
|
const mode = process.argv[2] || "plan";
|
|
30
41
|
if (mode === "plan") {
|
|
@@ -64,6 +75,7 @@ export function main() {
|
|
|
64
75
|
".buildchain/aws-macos-jit.json",
|
|
65
76
|
result,
|
|
66
77
|
);
|
|
78
|
+
writeMacosJitQualificationEnvironment();
|
|
67
79
|
process.stdout.write(`${output}\n`);
|
|
68
80
|
return result;
|
|
69
81
|
}
|
|
@@ -67,6 +67,7 @@ export const BUILDCHAIN_USAGE = `Usage:
|
|
|
67
67
|
[--kfd-3-prebuild-witness-json <json-or-path>]...
|
|
68
68
|
[--kfd-3-artifact-witness-json <json-or-path>]...
|
|
69
69
|
[--kfd-3-artifact-verify-cmd <command>]
|
|
70
|
+
[--adopter-delivery-json <json-or-path>]
|
|
70
71
|
[--kfd-adopter-manifest-json <json-or-path>]
|
|
71
72
|
[--kfd-support-matrix-json <json-or-path>]
|
|
72
73
|
[--kfd-product-gate-json <json-or-path>]...
|
|
@@ -129,11 +130,18 @@ export const BUILDCHAIN_USAGE = `Usage:
|
|
|
129
130
|
[--from-config | --workflow <required-workflow.yml>...] [--cwd <dir>]
|
|
130
131
|
[--check-response-timeout-minutes <n>]
|
|
131
132
|
[--max-entries-to-build <n>] [--apply]
|
|
132
|
-
buildchain dev warrant <submit|select|heartbeat|recover|close|cancel-queued|observe>
|
|
133
|
+
buildchain dev warrant <submit|select|heartbeat|qualify|recover|close|cancel-queued|observe>
|
|
133
134
|
--repository <owner/repo> --branch <dev/vN/vN.M>
|
|
134
135
|
[--execute] [--output <file>] [--json]
|
|
135
|
-
buildchain dev
|
|
136
|
+
buildchain dev authority <migrate|submit|lease-qualification|heartbeat-qualification|complete-qualification|lease-landing|heartbeat-landing|recover|admit-merge-group|settle|observe>
|
|
137
|
+
--repository <owner/repo> --branch <dev/vN/vN.M>
|
|
138
|
+
[--execute] [--output <file>] [--json]
|
|
139
|
+
buildchain dev proof <source|verify-source|classify|native|verify-native|classify-native|verify-native-reuse|replay|integration|verify-integration>
|
|
136
140
|
[--output <file>] [--json]
|
|
141
|
+
buildchain dev two-phase --repository <owner/repo> --branch <dev/vN/vN.M>
|
|
142
|
+
--pull-request <n> --expected-head <sha>
|
|
143
|
+
--warrant-result <file> [--native-proof <file>]
|
|
144
|
+
[--native-command <command>]
|
|
137
145
|
buildchain log <info|warn|error> --event <name> [--phase <phase>]
|
|
138
146
|
[--component <name>] [--source <name>] [--attribute key=value]...
|
|
139
147
|
[--path <jsonl>] [--json]
|
|
@@ -131,6 +131,7 @@ function repositoryJavaScriptFiles(root) {
|
|
|
131
131
|
.filter((file) => [".js", ".mjs", ".cjs"].includes(path.extname(file)))
|
|
132
132
|
.filter((file) => !file.startsWith("tests/"))
|
|
133
133
|
.filter((file) => !/(?:^|\/)tests?\//u.test(file))
|
|
134
|
+
.filter((file) => !file.startsWith(".buildchain/"))
|
|
134
135
|
.filter((file) => !/^actions\/[^/]+\/dist\//u.test(file))
|
|
135
136
|
.sort();
|
|
136
137
|
}
|
|
@@ -53,6 +53,10 @@ const requiredPaths = [
|
|
|
53
53
|
"packages/core/homebrew.js",
|
|
54
54
|
"packages/core/artifact-verification-envelope.js",
|
|
55
55
|
"packages/core/anchored-version-material.js",
|
|
56
|
+
"packages/core/next-development-transition.js",
|
|
57
|
+
"packages/core/next-development-controller.js",
|
|
58
|
+
"packages/core/next-development-candidate-reservation.js",
|
|
59
|
+
"packages/core/next-development-projection.js",
|
|
56
60
|
"packages/core/build-facts.js",
|
|
57
61
|
"packages/core/publication-package.js",
|
|
58
62
|
"packages/core/publication-reproducibility.js",
|
|
@@ -75,6 +79,8 @@ const requiredPaths = [
|
|
|
75
79
|
"docs/auditable-demo.md",
|
|
76
80
|
"contracts/auditable-demo-scenario-v1.schema.json",
|
|
77
81
|
"contracts/release-candidate-recovery-v1.schema.json",
|
|
82
|
+
"contracts/next-development-request-v1.schema.json",
|
|
83
|
+
"contracts/next-development-transition-v1.schema.json",
|
|
78
84
|
"contracts/auditable-demo-media-profiles-v1.json",
|
|
79
85
|
"contracts/evidence/auditable-demo-web-delivery-v1.json",
|
|
80
86
|
"contracts/evidence/auditable-demo-responsive-web-delivery-v1.json",
|
|
@@ -86,10 +92,15 @@ const requiredPaths = [
|
|
|
86
92
|
"contracts/fixtures/auditable-demo-responsive-web-delivery-v1/public-projection.json",
|
|
87
93
|
"contracts/fixtures/auditable-demo-responsive-web-delivery-v1/scene.json",
|
|
88
94
|
"docs/release-propagation.md",
|
|
95
|
+
"docs/next-development-transition.md",
|
|
89
96
|
"docs/site-bundle-contract.md",
|
|
90
97
|
"docs/toolkit-observability.md",
|
|
91
98
|
"docs/versioning.md",
|
|
92
99
|
"scripts/release-line-dry-run.mjs",
|
|
100
|
+
"scripts/next-development-transition.mjs",
|
|
101
|
+
"scripts/next-development-self-dogfood.mjs",
|
|
102
|
+
"scripts/next-development-self-dogfood-harness.mjs",
|
|
103
|
+
"scripts/generate-next-development-guidance.mjs",
|
|
93
104
|
"scripts/reconcile-release-governance.mjs",
|
|
94
105
|
"scripts/buildchain-channel-router.mjs",
|
|
95
106
|
"scripts/generate-channel-build-workflow.mjs",
|
|
@@ -992,7 +1003,7 @@ for (const forbiddenSnippet of [
|
|
|
992
1003
|
for (const requiredSnippet of [
|
|
993
1004
|
"id-token: write",
|
|
994
1005
|
"actions: write",
|
|
995
|
-
"uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@
|
|
1006
|
+
"uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@15db8a9d7c39f7a649a0e3971af68490356dd9ac\n",
|
|
996
1007
|
"github.event.workflow_run.event == 'push'",
|
|
997
1008
|
"!startsWith(github.event.workflow_run.display_title, 'chore(release): prepare v')",
|
|
998
1009
|
"!startsWith(github.event.workflow_run.display_title, 'chore(release): release v')",
|