@kungfu-tech/buildchain 3.0.9-alpha.2 → 3.0.9-alpha.3
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/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/buildchain-contract.json +4 -4
- package/dist/site/buildchain-site.json +89 -18
- package/dist/site/capability-registry.json +4 -3
- package/dist/site/kfd-claims.json +65 -5
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +19 -3
- package/dist/site/node-api-registry.json +1016 -36
- package/dist/site/page-registry.json +74 -11
- package/dist/site/public-surface-audit.json +7 -3
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +3 -0
- package/dist/site/site-manifest.json +15 -7
- package/dist/site/workflow-registry.json +4 -4
- package/docs/MAP.md +2 -1
- package/docs/aws-us-elastic-runner-burst-plane.md +11 -3
- package/docs/dev-alpha-candidate-patrol.md +8 -0
- package/docs/next-development-transition.md +119 -0
- package/docs/node-api-reference.md +126 -73
- package/docs/versioning.md +1 -1
- package/package.json +8 -3
- package/packages/core/buildchain-agent-manuals.js +1 -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/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/scripts/aws-macos-jit-controller-core.mjs +83 -2
- package/scripts/aws-macos-jit-controller.mjs +32 -1
- package/scripts/aws-macos-jit-instance-rehydrate.mjs +260 -0
- package/scripts/check-inventory.mjs +11 -0
- package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
- package/scripts/generate-next-development-guidance.mjs +49 -0
- package/scripts/generate-site-bundle.mjs +2 -0
- package/scripts/init-repo.mjs +119 -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/site-capability-metadata.mjs +3 -0
- package/scripts/stable-candidate-qualification.mjs +7 -7
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
workCheck,
|
|
14
14
|
} from "./paper-repository.js";
|
|
15
15
|
import { mergePublicationRehearsalAgentInstructions } from "./publication-rehearsal-projection.js";
|
|
16
|
+
import { mergeNextDevelopmentAgentInstructions } from "./next-development-projection.js";
|
|
16
17
|
|
|
17
18
|
export const PAPER_AGENT_ENTRY_CONTRACT = "kungfu-buildchain-paper-agent-entry";
|
|
18
19
|
export const PAPER_AGENT_ENTRY_SCHEMA_VERSION = 1;
|
|
@@ -168,10 +169,12 @@ export function paperAgentEntryFiles({
|
|
|
168
169
|
[PAPER_PATHS.agentEntry, jsonText(entry)],
|
|
169
170
|
[
|
|
170
171
|
PAPER_PATHS.agentInstructions,
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
172
|
+
mergeNextDevelopmentAgentInstructions(
|
|
173
|
+
mergePublicationRehearsalAgentInstructions(
|
|
174
|
+
mergePaperAgentEntryInstructions(currentAgents, {
|
|
175
|
+
developmentRef: resolvedDevelopmentRef,
|
|
176
|
+
}),
|
|
177
|
+
),
|
|
175
178
|
),
|
|
176
179
|
],
|
|
177
180
|
]);
|
package/packages/core/paper.js
CHANGED
|
@@ -51,6 +51,11 @@ import {
|
|
|
51
51
|
projectPublicationRehearsalToml,
|
|
52
52
|
publicationRehearsalWorkflow,
|
|
53
53
|
} from "./publication-rehearsal-projection.js";
|
|
54
|
+
import {
|
|
55
|
+
appendNextDevelopmentToml,
|
|
56
|
+
nextDevelopmentWorkflowHeader,
|
|
57
|
+
projectNextDevelopmentToml,
|
|
58
|
+
} from "./next-development-projection.js";
|
|
54
59
|
|
|
55
60
|
export { PAPER_PATHS, resolvePaperRepository } from "./paper-repository.js";
|
|
56
61
|
export {
|
|
@@ -356,7 +361,7 @@ immutable_base_url = ${tomlString(joinUrl(siteBaseUrl, "archive").replace(/\/$/,
|
|
|
356
361
|
registry_path = ".buildchain/publication/publication-registry.json"
|
|
357
362
|
`
|
|
358
363
|
: "";
|
|
359
|
-
return appendPublicationRehearsalToml(`schema = 1
|
|
364
|
+
return appendNextDevelopmentToml(appendPublicationRehearsalToml(`schema = 1
|
|
360
365
|
|
|
361
366
|
[project]
|
|
362
367
|
type = "publication-artifact"
|
|
@@ -390,14 +395,14 @@ command = "make pdf"
|
|
|
390
395
|
|
|
391
396
|
[lifecycle.verify]
|
|
392
397
|
command = "make check"
|
|
393
|
-
`);
|
|
398
|
+
`));
|
|
394
399
|
}
|
|
395
400
|
|
|
396
401
|
function scaffoldBuildWorkflow(
|
|
397
402
|
buildchainSha,
|
|
398
403
|
{ artifactName = "paper-publication" } = {},
|
|
399
404
|
) {
|
|
400
|
-
return
|
|
405
|
+
return `${nextDevelopmentWorkflowHeader()}name: Build
|
|
401
406
|
|
|
402
407
|
on:
|
|
403
408
|
workflow_dispatch:
|
|
@@ -425,7 +430,7 @@ jobs:
|
|
|
425
430
|
}
|
|
426
431
|
|
|
427
432
|
function scaffoldVerifyWorkflow(buildchainSha) {
|
|
428
|
-
return
|
|
433
|
+
return `${nextDevelopmentWorkflowHeader()}name: Verify
|
|
429
434
|
|
|
430
435
|
on:
|
|
431
436
|
pull_request:
|
|
@@ -456,7 +461,7 @@ function scaffoldReleaseWorkflow(
|
|
|
456
461
|
const passportInput = releasePassportProductName
|
|
457
462
|
? ` release-passport-product-name: ${JSON.stringify(releasePassportProductName)}\n`
|
|
458
463
|
: "";
|
|
459
|
-
return
|
|
464
|
+
return `${nextDevelopmentWorkflowHeader()}name: Paper Release
|
|
460
465
|
|
|
461
466
|
on:
|
|
462
467
|
workflow_dispatch:
|
|
@@ -1017,8 +1022,10 @@ function migrationFiles({
|
|
|
1017
1022
|
const files = new Map([
|
|
1018
1023
|
[
|
|
1019
1024
|
PAPER_PATHS.config,
|
|
1020
|
-
|
|
1021
|
-
|
|
1025
|
+
projectNextDevelopmentToml(
|
|
1026
|
+
projectPublicationRehearsalToml(
|
|
1027
|
+
fs.readFileSync(path.resolve(cwd, PAPER_PATHS.config), "utf8"),
|
|
1028
|
+
),
|
|
1022
1029
|
),
|
|
1023
1030
|
],
|
|
1024
1031
|
[PAPER_PATHS.contractLock, contractLockText],
|
|
@@ -203,6 +203,87 @@ export function createMacosJitCampaignPlan(values = {}) {
|
|
|
203
203
|
return { ...plan, digest: digest(plan) };
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
+
export function createMacosJitInstanceRehydratePlan(values = {}) {
|
|
207
|
+
const id = campaignId(values.campaignId);
|
|
208
|
+
const boundSource = source(values);
|
|
209
|
+
const aws = commonAws(values);
|
|
210
|
+
const regionConfig = macosJitRegionConfig(aws.region);
|
|
211
|
+
const tags = ownershipTags({ id, sourceSha: boundSource.sha });
|
|
212
|
+
const rehydrationId = exact(
|
|
213
|
+
values.rehydrationId,
|
|
214
|
+
/^r[1-9]\d{0,2}$/,
|
|
215
|
+
"rehydrationId",
|
|
216
|
+
);
|
|
217
|
+
const instanceClientToken = `kungfu-mac-rehydrate-${id}-${rehydrationId}`;
|
|
218
|
+
if (instanceClientToken.length > 64) {
|
|
219
|
+
throw new Error("rehydration client token exceeds the EC2 limit");
|
|
220
|
+
}
|
|
221
|
+
const plan = {
|
|
222
|
+
schemaVersion: 1,
|
|
223
|
+
contract: AWS_MACOS_JIT_CONTROLLER_CONTRACT,
|
|
224
|
+
kind: "instance-rehydrate-plan",
|
|
225
|
+
repository: repository(values.repository),
|
|
226
|
+
account: {
|
|
227
|
+
id: exact(values.accountId, /^\d{12}$/, "accountId"),
|
|
228
|
+
},
|
|
229
|
+
github: {
|
|
230
|
+
workflowId: MACOS_EC2_JIT.workflowId,
|
|
231
|
+
requiredState: "disabled_manually",
|
|
232
|
+
},
|
|
233
|
+
campaign: { id },
|
|
234
|
+
source: boundSource,
|
|
235
|
+
aws: {
|
|
236
|
+
...aws,
|
|
237
|
+
hostId: exact(values.hostId, /^h-[0-9a-f]+$/, "hostId"),
|
|
238
|
+
hostAllocatedAt: iso(values.hostAllocatedAt, "hostAllocatedAt"),
|
|
239
|
+
replacesInstanceId: exact(
|
|
240
|
+
values.replacesInstanceId,
|
|
241
|
+
/^i-[0-9a-f]+$/,
|
|
242
|
+
"replacesInstanceId",
|
|
243
|
+
),
|
|
244
|
+
rehydrationId,
|
|
245
|
+
instanceTags: tags,
|
|
246
|
+
volumeTags: tags,
|
|
247
|
+
instanceClientToken,
|
|
248
|
+
rootVolume: {
|
|
249
|
+
deviceName: "/dev/sda1",
|
|
250
|
+
deleteOnTermination: true,
|
|
251
|
+
encrypted: true,
|
|
252
|
+
volumeSizeGiB: 200,
|
|
253
|
+
volumeType: "gp3",
|
|
254
|
+
},
|
|
255
|
+
metadata: {
|
|
256
|
+
httpEndpoint: "enabled",
|
|
257
|
+
httpTokens: "required",
|
|
258
|
+
httpPutResponseHopLimit: 1,
|
|
259
|
+
instanceMetadataTags: "disabled",
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
safety: {
|
|
263
|
+
applyMode: values.execute === true ? "execute" : "dry-run",
|
|
264
|
+
exactSourceRequired: true,
|
|
265
|
+
existingCampaignHostRequired: true,
|
|
266
|
+
noHostAllocation: true,
|
|
267
|
+
zeroActiveInstanceRequired: true,
|
|
268
|
+
zeroRunnerResidueRequired: true,
|
|
269
|
+
awsDryRunRequiredBeforeLaunch: true,
|
|
270
|
+
cleanupOwner: "scheduled-card-scoped-reaper",
|
|
271
|
+
budget: {
|
|
272
|
+
name: regionConfig.budgetName,
|
|
273
|
+
limitUsd: MACOS_EC2_JIT.budgetLimitUsd,
|
|
274
|
+
metrics: ["UnblendedCost"],
|
|
275
|
+
dimensionFilter: {
|
|
276
|
+
usageTypes: regionConfig.budgetUsageTypes,
|
|
277
|
+
operation: MACOS_EC2_JIT.budgetOperation,
|
|
278
|
+
regions: regionConfig.budgetRegions,
|
|
279
|
+
},
|
|
280
|
+
requiredActualThresholds: [80, 95],
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
return { ...plan, digest: digest(plan) };
|
|
285
|
+
}
|
|
286
|
+
|
|
206
287
|
export function createMacosJitSourceRebindPlan(values = {}) {
|
|
207
288
|
const id = campaignId(values.campaignId);
|
|
208
289
|
const boundSource = source(values);
|
|
@@ -301,9 +382,9 @@ export function macosAllocateHostsArgs(plan) {
|
|
|
301
382
|
export function macosRunInstancesArgs(plan, { hostId, dryRun = false } = {}) {
|
|
302
383
|
if (
|
|
303
384
|
plan?.contract !== AWS_MACOS_JIT_CONTROLLER_CONTRACT ||
|
|
304
|
-
|
|
385
|
+
!["campaign-launch-plan", "instance-rehydrate-plan"].includes(plan.kind)
|
|
305
386
|
) {
|
|
306
|
-
throw new Error("macOS JIT
|
|
387
|
+
throw new Error("macOS JIT instance launch plan contract is invalid");
|
|
307
388
|
}
|
|
308
389
|
const resolvedHostId = exact(hostId, /^h-[0-9a-f]+$/, "hostId");
|
|
309
390
|
const args = [
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
AWS_MACOS_JIT_CONTROLLER_CONTRACT,
|
|
7
7
|
createMacosJitCampaignPlan,
|
|
8
8
|
createMacosJitClosePlan,
|
|
9
|
+
createMacosJitInstanceRehydratePlan,
|
|
9
10
|
createMacosJitJobPlan,
|
|
10
11
|
createMacosJitSourceRebindPlan,
|
|
11
12
|
macosAllocateHostsArgs,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
macosRunInstancesArgs,
|
|
14
15
|
} from "./aws-macos-jit-controller-core.mjs";
|
|
15
16
|
import { MACOS_EC2_JIT_REGIONS } from "./aws-macos-jit-core.mjs";
|
|
17
|
+
import { executeMacosJitInstanceRehydrate } from "./aws-macos-jit-instance-rehydrate.mjs";
|
|
16
18
|
import { executeMacosJitJob } from "./aws-macos-jit-job-controller.mjs";
|
|
17
19
|
import { executeMacosJitSourceRebind } from "./aws-macos-jit-source-rebind.mjs";
|
|
18
20
|
import {
|
|
@@ -516,6 +518,19 @@ function confirm(plan) {
|
|
|
516
518
|
throw new Error("--confirm-zero-allocation is required");
|
|
517
519
|
}
|
|
518
520
|
}
|
|
521
|
+
if (plan.kind === "instance-rehydrate-plan") {
|
|
522
|
+
if (arg("confirm-host-id") !== plan.aws.hostId) {
|
|
523
|
+
throw new Error("--confirm-host-id must equal the exact host id");
|
|
524
|
+
}
|
|
525
|
+
if (arg("confirm-replaces-instance-id") !== plan.aws.replacesInstanceId) {
|
|
526
|
+
throw new Error(
|
|
527
|
+
"--confirm-replaces-instance-id must equal replacesInstanceId",
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
if (!flag("confirm-no-host-allocation")) {
|
|
531
|
+
throw new Error("--confirm-no-host-allocation is required");
|
|
532
|
+
}
|
|
533
|
+
}
|
|
519
534
|
}
|
|
520
535
|
|
|
521
536
|
function emit(plan, mode, execute, operation) {
|
|
@@ -541,6 +556,20 @@ export function main() {
|
|
|
541
556
|
executeMacosJitCampaignLaunch(plan, { profile: arg("aws-profile") }),
|
|
542
557
|
);
|
|
543
558
|
}
|
|
559
|
+
if (["plan-rehydrate", "rehydrate-instance"].includes(mode)) {
|
|
560
|
+
const plan = createMacosJitInstanceRehydratePlan({
|
|
561
|
+
...commonValues(execute),
|
|
562
|
+
hostId: arg("host-id"),
|
|
563
|
+
hostAllocatedAt: arg("host-allocated-at"),
|
|
564
|
+
replacesInstanceId: arg("replaces-instance-id"),
|
|
565
|
+
rehydrationId: arg("rehydration-id"),
|
|
566
|
+
});
|
|
567
|
+
return emit(plan, mode, execute, () =>
|
|
568
|
+
executeMacosJitInstanceRehydrate(plan, {
|
|
569
|
+
profile: arg("aws-profile"),
|
|
570
|
+
}),
|
|
571
|
+
);
|
|
572
|
+
}
|
|
544
573
|
if (["plan-rebind", "rebind-campaign"].includes(mode)) {
|
|
545
574
|
const plan = createMacosJitSourceRebindPlan({
|
|
546
575
|
...commonValues(execute),
|
|
@@ -550,7 +579,9 @@ export function main() {
|
|
|
550
579
|
hostId: arg("host-id"),
|
|
551
580
|
instanceId: arg("instance-id"),
|
|
552
581
|
});
|
|
553
|
-
return emit(plan, mode, execute, () =>
|
|
582
|
+
return emit(plan, mode, execute, () =>
|
|
583
|
+
executeMacosJitSourceRebind(plan, { profile: arg("aws-profile") }),
|
|
584
|
+
);
|
|
554
585
|
}
|
|
555
586
|
if (["plan-job", "run-job"].includes(mode)) {
|
|
556
587
|
const plan = createMacosJitJobPlan({
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
AWS_MACOS_JIT_CONTROLLER_CONTRACT,
|
|
5
|
+
macosRunInstancesArgs,
|
|
6
|
+
} from "./aws-macos-jit-controller-core.mjs";
|
|
7
|
+
import { MACOS_EC2_JIT_REGIONS } from "./aws-macos-jit-core.mjs";
|
|
8
|
+
import {
|
|
9
|
+
assertDryRun,
|
|
10
|
+
assertMacosBudgetLaunchGate,
|
|
11
|
+
assertOwnership,
|
|
12
|
+
awsArgs,
|
|
13
|
+
awsJson,
|
|
14
|
+
commandResult,
|
|
15
|
+
ghJson,
|
|
16
|
+
requireSuccess,
|
|
17
|
+
} from "./aws-macos-jit-controller-runtime.mjs";
|
|
18
|
+
|
|
19
|
+
function assertInstanceRehydratePreflight(plan, profile) {
|
|
20
|
+
const launchGate = assertMacosBudgetLaunchGate(plan, profile);
|
|
21
|
+
const commit = ghJson(
|
|
22
|
+
["api", `repos/${plan.repository}/commits/${plan.source.sha}`],
|
|
23
|
+
undefined,
|
|
24
|
+
"GitHub exact-source preflight",
|
|
25
|
+
);
|
|
26
|
+
if (String(commit.sha || "").toLowerCase() !== plan.source.sha) {
|
|
27
|
+
throw new Error("GitHub did not resolve the exact campaign source SHA");
|
|
28
|
+
}
|
|
29
|
+
const host = awsJson(
|
|
30
|
+
plan,
|
|
31
|
+
profile,
|
|
32
|
+
[
|
|
33
|
+
"ec2",
|
|
34
|
+
"describe-hosts",
|
|
35
|
+
"--host-ids",
|
|
36
|
+
plan.aws.hostId,
|
|
37
|
+
"--output",
|
|
38
|
+
"json",
|
|
39
|
+
],
|
|
40
|
+
"macOS rehydration host preflight",
|
|
41
|
+
).Hosts?.[0];
|
|
42
|
+
if (
|
|
43
|
+
!host ||
|
|
44
|
+
host.State !== "available" ||
|
|
45
|
+
host.AvailabilityZone !== plan.aws.availabilityZone ||
|
|
46
|
+
host.HostProperties?.InstanceType !== plan.aws.instanceType ||
|
|
47
|
+
(host.Instances || []).length !== 0 ||
|
|
48
|
+
new Date(host.AllocationTime).toISOString() !== plan.aws.hostAllocatedAt
|
|
49
|
+
) {
|
|
50
|
+
throw new Error("macOS rehydration host identity or capacity mismatch");
|
|
51
|
+
}
|
|
52
|
+
assertOwnership(host.Tags, plan);
|
|
53
|
+
|
|
54
|
+
const regionalPlans = Object.keys(MACOS_EC2_JIT_REGIONS).map((region) => ({
|
|
55
|
+
...plan,
|
|
56
|
+
aws: { ...plan.aws, region },
|
|
57
|
+
}));
|
|
58
|
+
const otherHosts = regionalPlans.flatMap((regionalPlan) =>
|
|
59
|
+
(
|
|
60
|
+
awsJson(
|
|
61
|
+
regionalPlan,
|
|
62
|
+
profile,
|
|
63
|
+
[
|
|
64
|
+
"ec2",
|
|
65
|
+
"describe-hosts",
|
|
66
|
+
"--filter",
|
|
67
|
+
"Name=tag:kungfu:plane,Values=aws-us-elastic-runner-burst",
|
|
68
|
+
"Name=tag:kungfu:provider,Values=macos-ec2-jit",
|
|
69
|
+
"Name=state,Values=available,pending,under-assessment",
|
|
70
|
+
"--output",
|
|
71
|
+
"json",
|
|
72
|
+
],
|
|
73
|
+
`macOS rehydration host ceiling in ${regionalPlan.aws.region}`,
|
|
74
|
+
).Hosts || []
|
|
75
|
+
).filter((candidate) => candidate.HostId !== plan.aws.hostId),
|
|
76
|
+
);
|
|
77
|
+
if (otherHosts.length !== 0) {
|
|
78
|
+
throw new Error("macOS rehydration found another active Dedicated Host");
|
|
79
|
+
}
|
|
80
|
+
const activeInstances = regionalPlans.flatMap((regionalPlan) => {
|
|
81
|
+
const active = awsJson(
|
|
82
|
+
regionalPlan,
|
|
83
|
+
profile,
|
|
84
|
+
[
|
|
85
|
+
"ec2",
|
|
86
|
+
"describe-instances",
|
|
87
|
+
"--filters",
|
|
88
|
+
"Name=tag:kungfu:plane,Values=aws-us-elastic-runner-burst",
|
|
89
|
+
"Name=tag:kungfu:provider,Values=macos-ec2-jit",
|
|
90
|
+
"Name=instance-state-name,Values=pending,running,stopping,stopped,shutting-down",
|
|
91
|
+
"--output",
|
|
92
|
+
"json",
|
|
93
|
+
],
|
|
94
|
+
`active macOS JIT instance preflight in ${regionalPlan.aws.region}`,
|
|
95
|
+
);
|
|
96
|
+
return (active.Reservations || []).flatMap(
|
|
97
|
+
(reservation) => reservation.Instances || [],
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
if (activeInstances.length !== 0) {
|
|
101
|
+
throw new Error("macOS rehydration requires zero active JIT instances");
|
|
102
|
+
}
|
|
103
|
+
const runners = ghJson(
|
|
104
|
+
["api", `repos/${plan.repository}/actions/runners?per_page=100`],
|
|
105
|
+
undefined,
|
|
106
|
+
"macOS rehydration runner preflight",
|
|
107
|
+
).runners;
|
|
108
|
+
if (
|
|
109
|
+
(runners || []).some((runner) =>
|
|
110
|
+
(runner.labels || []).some((label) =>
|
|
111
|
+
String(label.name || label).startsWith("aws-us-ec2-macos-jit-"),
|
|
112
|
+
),
|
|
113
|
+
)
|
|
114
|
+
) {
|
|
115
|
+
throw new Error("macOS rehydration requires zero JIT runner residue");
|
|
116
|
+
}
|
|
117
|
+
const image = awsJson(
|
|
118
|
+
plan,
|
|
119
|
+
profile,
|
|
120
|
+
[
|
|
121
|
+
"ec2",
|
|
122
|
+
"describe-images",
|
|
123
|
+
"--image-ids",
|
|
124
|
+
plan.aws.amiId,
|
|
125
|
+
"--output",
|
|
126
|
+
"json",
|
|
127
|
+
],
|
|
128
|
+
"macOS rehydration AMI preflight",
|
|
129
|
+
).Images?.[0];
|
|
130
|
+
if (
|
|
131
|
+
!image ||
|
|
132
|
+
image.Name !== plan.aws.amiName ||
|
|
133
|
+
image.Architecture !== "arm64_mac" ||
|
|
134
|
+
image.State !== "available"
|
|
135
|
+
) {
|
|
136
|
+
throw new Error("macOS AMI identity or availability mismatch");
|
|
137
|
+
}
|
|
138
|
+
const subnet = awsJson(
|
|
139
|
+
plan,
|
|
140
|
+
profile,
|
|
141
|
+
[
|
|
142
|
+
"ec2",
|
|
143
|
+
"describe-subnets",
|
|
144
|
+
"--subnet-ids",
|
|
145
|
+
plan.aws.subnetId,
|
|
146
|
+
"--output",
|
|
147
|
+
"json",
|
|
148
|
+
],
|
|
149
|
+
"macOS rehydration subnet preflight",
|
|
150
|
+
).Subnets?.[0];
|
|
151
|
+
if (!subnet || subnet.AvailabilityZone !== plan.aws.availabilityZone) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
"macOS subnet and Dedicated Host availability zone mismatch",
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
...launchGate,
|
|
158
|
+
exactCommit: plan.source.sha,
|
|
159
|
+
hostId: host.HostId,
|
|
160
|
+
hostState: host.State,
|
|
161
|
+
activeInstances: 0,
|
|
162
|
+
otherActiveHosts: 0,
|
|
163
|
+
amiOwnerId: image.OwnerId,
|
|
164
|
+
subnetAvailabilityZone: subnet.AvailabilityZone,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function executeMacosJitInstanceRehydrate(plan, { profile = "" } = {}) {
|
|
169
|
+
if (
|
|
170
|
+
plan?.contract !== AWS_MACOS_JIT_CONTROLLER_CONTRACT ||
|
|
171
|
+
plan.kind !== "instance-rehydrate-plan"
|
|
172
|
+
) {
|
|
173
|
+
throw new Error("macOS JIT instance rehydrate plan contract is invalid");
|
|
174
|
+
}
|
|
175
|
+
const preflight = assertInstanceRehydratePreflight(plan, profile);
|
|
176
|
+
assertDryRun(
|
|
177
|
+
commandResult(
|
|
178
|
+
"aws",
|
|
179
|
+
awsArgs(
|
|
180
|
+
plan,
|
|
181
|
+
profile,
|
|
182
|
+
macosRunInstancesArgs(plan, { hostId: plan.aws.hostId, dryRun: true }),
|
|
183
|
+
),
|
|
184
|
+
),
|
|
185
|
+
"EC2 same-host RunInstances DryRun",
|
|
186
|
+
);
|
|
187
|
+
const launched = awsJson(
|
|
188
|
+
plan,
|
|
189
|
+
profile,
|
|
190
|
+
macosRunInstancesArgs(plan, { hostId: plan.aws.hostId }),
|
|
191
|
+
"EC2 same-host RunInstances",
|
|
192
|
+
);
|
|
193
|
+
const instanceId = launched.Instances?.[0]?.InstanceId;
|
|
194
|
+
if (!/^i-[0-9a-f]+$/.test(String(instanceId || ""))) {
|
|
195
|
+
throw new Error("EC2 same-host RunInstances returned no instance identity");
|
|
196
|
+
}
|
|
197
|
+
requireSuccess(
|
|
198
|
+
commandResult(
|
|
199
|
+
"aws",
|
|
200
|
+
awsArgs(plan, profile, [
|
|
201
|
+
"ec2",
|
|
202
|
+
"wait",
|
|
203
|
+
"instance-running",
|
|
204
|
+
"--instance-ids",
|
|
205
|
+
instanceId,
|
|
206
|
+
]),
|
|
207
|
+
),
|
|
208
|
+
"macOS rehydrated instance-running waiter",
|
|
209
|
+
);
|
|
210
|
+
const instance = awsJson(
|
|
211
|
+
plan,
|
|
212
|
+
profile,
|
|
213
|
+
[
|
|
214
|
+
"ec2",
|
|
215
|
+
"describe-instances",
|
|
216
|
+
"--instance-ids",
|
|
217
|
+
instanceId,
|
|
218
|
+
"--output",
|
|
219
|
+
"json",
|
|
220
|
+
],
|
|
221
|
+
"macOS rehydrated instance readback",
|
|
222
|
+
).Reservations?.[0]?.Instances?.[0];
|
|
223
|
+
if (
|
|
224
|
+
!instance ||
|
|
225
|
+
instance.State?.Name !== "running" ||
|
|
226
|
+
instance.Placement?.HostId !== plan.aws.hostId ||
|
|
227
|
+
instance.ImageId !== plan.aws.amiId ||
|
|
228
|
+
instance.InstanceType !== plan.aws.instanceType
|
|
229
|
+
) {
|
|
230
|
+
throw new Error("macOS rehydrated instance identity or placement mismatch");
|
|
231
|
+
}
|
|
232
|
+
assertOwnership(instance.Tags, plan);
|
|
233
|
+
return {
|
|
234
|
+
schemaVersion: 1,
|
|
235
|
+
contract: AWS_MACOS_JIT_CONTROLLER_CONTRACT,
|
|
236
|
+
kind: "instance-rehydrate-result",
|
|
237
|
+
status: "rehydrated",
|
|
238
|
+
repository: plan.repository,
|
|
239
|
+
campaign: plan.campaign,
|
|
240
|
+
source: plan.source,
|
|
241
|
+
replacement: {
|
|
242
|
+
rehydrationId: plan.aws.rehydrationId,
|
|
243
|
+
replacesInstanceId: plan.aws.replacesInstanceId,
|
|
244
|
+
},
|
|
245
|
+
aws: {
|
|
246
|
+
region: plan.aws.region,
|
|
247
|
+
availabilityZone: plan.aws.availabilityZone,
|
|
248
|
+
hostId: plan.aws.hostId,
|
|
249
|
+
hostAllocatedAt: plan.aws.hostAllocatedAt,
|
|
250
|
+
instanceId,
|
|
251
|
+
instanceType: instance.InstanceType,
|
|
252
|
+
imageId: instance.ImageId,
|
|
253
|
+
launchTime: new Date(instance.LaunchTime).toISOString(),
|
|
254
|
+
},
|
|
255
|
+
preflight,
|
|
256
|
+
dryRun: "RunInstances:DryRunOperation",
|
|
257
|
+
hostAllocated: false,
|
|
258
|
+
planDigest: plan.digest,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
@@ -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",
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
candidateFromDecision,
|
|
21
21
|
selectLatestQualifiedSource,
|
|
22
22
|
} from "../packages/core/dev-alpha-candidate-selection.js";
|
|
23
|
+
import { readGitHubNextDevelopmentVersionReservation } from "../packages/core/next-development-candidate-reservation.js";
|
|
23
24
|
|
|
24
25
|
export { reconcileActiveReleaseTrain, selectLatestQualifiedSource };
|
|
25
26
|
|
|
@@ -617,6 +618,7 @@ export async function runDevAlphaCandidatePatrol(
|
|
|
617
618
|
comparison,
|
|
618
619
|
workflowEvidence,
|
|
619
620
|
skippedNewerCommitCount,
|
|
621
|
+
versionReservation,
|
|
620
622
|
qualificationError,
|
|
621
623
|
} = await resolvePatrolSourceInputs({
|
|
622
624
|
client,
|
|
@@ -646,6 +648,7 @@ export async function runDevAlphaCandidatePatrol(
|
|
|
646
648
|
observedSourceHeadSha,
|
|
647
649
|
skippedNewerCommitCount,
|
|
648
650
|
...(targetBaseline ? { targetBaseline } : {}),
|
|
651
|
+
...(versionReservation ? { versionReservation } : {}),
|
|
649
652
|
},
|
|
650
653
|
workflowEvidence,
|
|
651
654
|
requiredWorkflowPaths,
|
|
@@ -992,6 +995,20 @@ export function createGitHubChannelCandidateClient({
|
|
|
992
995
|
targetBranch,
|
|
993
996
|
parseCandidate: managedCandidateFromPullRequest,
|
|
994
997
|
}),
|
|
998
|
+
async readNextDevelopmentVersionReservation({
|
|
999
|
+
reservationSha,
|
|
1000
|
+
candidateSha,
|
|
1001
|
+
targetVersion,
|
|
1002
|
+
}) {
|
|
1003
|
+
return readGitHubNextDevelopmentVersionReservation({
|
|
1004
|
+
api,
|
|
1005
|
+
owner,
|
|
1006
|
+
repo,
|
|
1007
|
+
reservationSha,
|
|
1008
|
+
candidateSha,
|
|
1009
|
+
targetVersion,
|
|
1010
|
+
});
|
|
1011
|
+
},
|
|
995
1012
|
async listCompletedWorkflowRuns(workflowPathValue, sourceBranch) {
|
|
996
1013
|
const runs = [];
|
|
997
1014
|
for (let page = 1; page <= 10; page += 1) {
|
|
@@ -1015,7 +1032,11 @@ export function createGitHubChannelCandidateClient({
|
|
|
1015
1032
|
for (const commit of rows) {
|
|
1016
1033
|
const commitSha = text(commit.sha);
|
|
1017
1034
|
if (commitSha === targetSha) return commits;
|
|
1018
|
-
commits.push(
|
|
1035
|
+
commits.push({
|
|
1036
|
+
sha: commitSha,
|
|
1037
|
+
message: text(commit.commit?.message),
|
|
1038
|
+
parents: (commit.parents || []).map((parent) => text(parent.sha)),
|
|
1039
|
+
});
|
|
1019
1040
|
}
|
|
1020
1041
|
if (rows.length < 100) return commits;
|
|
1021
1042
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
|
|
7
|
+
import { nextDevelopmentManual } from "../packages/core/next-development-projection.js";
|
|
8
|
+
|
|
9
|
+
export const NEXT_DEVELOPMENT_MANUAL_PATH =
|
|
10
|
+
"docs/next-development-transition.md";
|
|
11
|
+
|
|
12
|
+
export function generateNextDevelopmentGuidance({
|
|
13
|
+
cwd = process.cwd(),
|
|
14
|
+
check = false,
|
|
15
|
+
} = {}) {
|
|
16
|
+
const target = path.resolve(cwd, NEXT_DEVELOPMENT_MANUAL_PATH);
|
|
17
|
+
const expected = nextDevelopmentManual();
|
|
18
|
+
const current = fs.existsSync(target) ? fs.readFileSync(target, "utf8") : "";
|
|
19
|
+
if (check) {
|
|
20
|
+
if (current !== expected) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`${NEXT_DEVELOPMENT_MANUAL_PATH} drifted from its contract source`,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
return { ok: true, changed: false, path: NEXT_DEVELOPMENT_MANUAL_PATH };
|
|
26
|
+
}
|
|
27
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
28
|
+
if (current !== expected) fs.writeFileSync(target, expected);
|
|
29
|
+
return {
|
|
30
|
+
ok: true,
|
|
31
|
+
changed: current !== expected,
|
|
32
|
+
path: NEXT_DEVELOPMENT_MANUAL_PATH,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (
|
|
37
|
+
process.argv[1] &&
|
|
38
|
+
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
39
|
+
) {
|
|
40
|
+
try {
|
|
41
|
+
const result = generateNextDevelopmentGuidance({
|
|
42
|
+
check: process.argv.includes("--check"),
|
|
43
|
+
});
|
|
44
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
process.stderr.write(`next-development guidance: ${error.message}\n`);
|
|
47
|
+
process.exitCode = 1;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -368,6 +368,7 @@ const manualMetaById = new Map(Object.entries({
|
|
|
368
368
|
"release-activation-transaction": { capabilityGroup: "release-passport-trust", audience: ["release-operator", "agent"], maturity: "preview", order: 125 },
|
|
369
369
|
"release-candidate": { capabilityGroup: "reusable-build", audience: ["release-operator", "consumer"], maturity: "stable", order: 130 },
|
|
370
370
|
"release-train": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 132 },
|
|
371
|
+
"next-development-transition": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 133 },
|
|
371
372
|
"stable-candidate-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer"], maturity: "preview", order: 135 },
|
|
372
373
|
"dev-qualification-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 136 },
|
|
373
374
|
"dev-alpha-candidate-patrol": { capabilityGroup: "governance-versioning", audience: ["release-operator", "consumer", "agent"], maturity: "preview", order: 137 },
|
|
@@ -733,6 +734,7 @@ function buildSiteBundle() {
|
|
|
733
734
|
"docs/reusable-build-surface.md",
|
|
734
735
|
"docs/release-candidate.md",
|
|
735
736
|
"docs/release-train.md",
|
|
737
|
+
"docs/next-development-transition.md",
|
|
736
738
|
"docs/stable-candidate-patrol.md",
|
|
737
739
|
"docs/dev-qualification-patrol.md",
|
|
738
740
|
"docs/dev-alpha-candidate-patrol.md",
|