@kungfu-tech/buildchain 3.0.9-alpha.0 → 3.0.9-alpha.10
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/bin/buildchain.mjs +9 -1
- package/contracts/dev-delivery-authority-v2.schema.json +253 -0
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +16 -0
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +57 -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/contracts/release-cut-v1.schema.json +1 -0
- package/contracts/release-train-transition-v1.schema.json +1 -0
- package/dist/site/agent-index.json +1 -0
- package/dist/site/artifact-schemas.json +2 -0
- package/dist/site/buildchain-contract.json +91 -32
- package/dist/site/buildchain-site.json +353 -45
- package/dist/site/capability-registry.json +7 -6
- package/dist/site/cli-registry.json +41 -2
- package/dist/site/controller-registry.json +8 -4
- package/dist/site/kfd-claims.json +193 -9
- package/dist/site/kfd-upstream-aggregate.json +9 -9
- package/dist/site/manual-registry.json +26 -10
- package/dist/site/node-api-registry.json +8012 -4574
- package/dist/site/page-registry.json +331 -31
- package/dist/site/public-surface-audit.json +353 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-passport-check-manifest.json +1 -0
- package/dist/site/release-provenance.json +12 -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 +22 -14
- package/dist/site/workflow-registry.json +15 -7
- 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 +200 -13
- 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 +164 -27
- package/docs/kfd-support.md +7 -2
- package/docs/next-development-transition.md +119 -0
- package/docs/node-api-reference.md +464 -277
- package/docs/release-governance.md +10 -7
- package/docs/release-passport.md +15 -2
- package/docs/release-train.md +46 -4
- package/docs/reusable-build-surface.md +26 -9
- package/docs/runtime-train-validation.md +6 -0
- package/docs/versioning.md +6 -2
- package/package.json +21 -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 +118 -0
- package/packages/core/buildchain-compatibility-proof.js +54 -0
- package/packages/core/buildchain-contract.js +7 -0
- package/packages/core/buildchain-delivery-bootstrap.js +240 -0
- package/packages/core/buildchain-delivery-infrastructure.js +164 -0
- package/packages/core/channel-candidate.js +8 -0
- package/packages/core/channel-promotion-baseline.js +52 -0
- package/packages/core/controller-evidence.js +2 -1
- 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 +141 -48
- 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/release-blocker-priority.js +192 -0
- package/packages/core/release-passport-contract.js +10 -6
- package/packages/core/release-passport.js +29 -12
- package/packages/core/release-train.js +220 -0
- 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-channel-router.mjs +18 -2
- package/scripts/buildchain-cli-help.mjs +9 -2
- package/scripts/buildchain-contract-lock.mjs +11 -1
- package/scripts/check-internal-architecture.mjs +1 -0
- package/scripts/check-inventory.mjs +14 -3
- 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 +47 -5
- package/scripts/dev-pr-delivery-warrant.mjs +19 -14
- package/scripts/generate-buildchain-kfd-witnesses.mjs +88 -94
- package/scripts/generate-channel-build-workflow.mjs +82 -31
- 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/promotion-channel-router.mjs +5 -3
- package/scripts/release-candidate-tail-reseal.mjs +426 -0
- package/scripts/release-train-self-dogfood.mjs +579 -0
- package/scripts/runtime-ref-core.mjs +4 -17
- package/scripts/seal-artifact-signing-requests.mjs +7 -4
- package/scripts/site-capability-metadata.mjs +23 -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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import { defaultDevDeliveryStateRef, GitHubDevDeliveryStore } from "./dev-delivery-warrant.mjs";
|
|
2
3
|
|
|
3
4
|
const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
4
5
|
const SHA_PATTERN = /^[0-9a-f]{40}$/;
|
|
@@ -34,6 +35,11 @@ function exactActiveReadback(result) {
|
|
|
34
35
|
const warrant = result.observation.activeWarrant;
|
|
35
36
|
const candidate = result.observation.activeCandidate;
|
|
36
37
|
requireMatch(warrant?.schema === "kungfu.buildchain.dev-delivery-warrant/v1", "delivery-warrant-missing");
|
|
38
|
+
requireMatch((warrant.phase || "qualified") === "qualified", "delivery-warrant-not-qualified");
|
|
39
|
+
if (warrant.phase === "qualified") {
|
|
40
|
+
requireMatch(ROOT_PATTERN.test(String(warrant.nativeProofRoot || "")), "delivery-warrant-native-proof-missing");
|
|
41
|
+
requireMatch(ROOT_PATTERN.test(String(warrant.nativeProofReuseRoot || "")), "delivery-warrant-native-reuse-missing");
|
|
42
|
+
}
|
|
37
43
|
requireMatch(candidate?.candidateId === warrant.candidateId, "delivery-warrant-candidate-readback-missing");
|
|
38
44
|
requireMatch(!result.warrant || JSON.stringify(result.warrant) === JSON.stringify(warrant), "delivery-warrant-readback-mismatch");
|
|
39
45
|
return { warrant, candidate };
|
|
@@ -117,19 +123,18 @@ export function readDeliveryWarrantResult(options, pullRequest) {
|
|
|
117
123
|
}
|
|
118
124
|
|
|
119
125
|
export async function readCurrentDeliveryQueueState(client, repository, targetBranch) {
|
|
120
|
-
if (typeof client.getDevDeliveryQueueState === "function")
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return JSON.parse(Buffer.from(String(data.content).replace(/\s+/g, ""), "base64").toString("utf8"));
|
|
126
|
+
if (typeof client.getDevDeliveryQueueState === "function") return client.getDevDeliveryQueueState(targetBranch);
|
|
127
|
+
const store = new GitHubDevDeliveryStore({
|
|
128
|
+
repository: `${repository.owner}/${repository.repo}`,
|
|
129
|
+
token: client.token,
|
|
130
|
+
apiUrl: client.apiUrl,
|
|
131
|
+
fetchImpl: client.fetch,
|
|
132
|
+
});
|
|
133
|
+
const { queue } = await store.read({
|
|
134
|
+
stateRef: defaultDevDeliveryStateRef(targetBranch),
|
|
135
|
+
protectedBase: targetBranch,
|
|
136
|
+
});
|
|
137
|
+
return queue;
|
|
133
138
|
}
|
|
134
139
|
|
|
135
140
|
export async function verifyCurrentDeliveryWarrant(client, options, pullRequest, warrant) {
|
|
@@ -147,7 +152,7 @@ export async function verifyCurrentDeliveryWarrant(client, options, pullRequest,
|
|
|
147
152
|
requireMatch(Number(active?.generation) === Number(warrant.generation), "delivery-warrant-current-generation-mismatch");
|
|
148
153
|
requireMatch(Number(active?.pullRequestNumber) === Number(pullRequest.number), "delivery-warrant-current-pr-mismatch");
|
|
149
154
|
requireMatch(String(active?.sourceHead || "").toLowerCase() === options.expectedHeadSha, "delivery-warrant-current-head-mismatch");
|
|
150
|
-
requireMatch(["selected", "proving", "waiting", "blocked"].includes(candidate?.status), "delivery-warrant-current-candidate-not-selected");
|
|
155
|
+
requireMatch(["selected", "proving", "waiting", "blocked", "qualified"].includes(candidate?.status), "delivery-warrant-current-candidate-not-selected");
|
|
151
156
|
requireMatch(candidate?.sourceHead === active.sourceHead, "delivery-warrant-current-candidate-head-mismatch");
|
|
152
157
|
}
|
|
153
158
|
|
|
@@ -1,121 +1,115 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import crypto from "node:crypto";
|
|
2
3
|
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
4
|
import { execFileSync } from "node:child_process";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
import path from "node:path";
|
|
5
7
|
import { pathToFileURL } from "node:url";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from "../packages/core/buildchain-kfd-claims.js";
|
|
12
|
-
import {
|
|
13
|
-
BUILDCHAIN_KFD1_CONTRACT_WORLD_WITNESS_PATH,
|
|
14
|
-
BUILDCHAIN_KFD2_CLAIMS_DIR,
|
|
15
|
-
BUILDCHAIN_KFD3_ARTIFACT_WITNESS_PATH,
|
|
16
|
-
BUILDCHAIN_KFD3_PREBUILD_WITNESS_PATH,
|
|
17
|
-
} from "../packages/core/buildchain-layout.js";
|
|
8
|
+
import { addAdopterWitness, initAdopterManifest } from "@kungfu-tech/kfd/adopter-conformance/toolchain";
|
|
9
|
+
import kfdStandards from "@kungfu-tech/kfd/standards.json" with { type: "json" };
|
|
10
|
+
import { installedKfdPackageArtifactRoot } from "../packages/core/artifact-verification-envelope.js";
|
|
11
|
+
import { createKfdAdopterManifestGate, createKfdLegacySupportMatrixProjection, validateKfdAdopterManifestGate, validateKfdLegacySupportMatrixProjection } from "../packages/core/kfd-adopter-manifest.js";
|
|
12
|
+
import { KFD_PRODUCT_GATE_INPUT_CONTRACT, evaluateKfdProductGate, kfdProductGateDigest, validateKfdProductGateResult } from "../packages/core/kfd-product-gates.js";
|
|
13
|
+
import { createBuildchainKfd1Witness, createBuildchainKfd2Claims, createBuildchainKfd3ArtifactWitness, createBuildchainKfd3PrebuildWitness } from "../packages/core/buildchain-kfd-claims.js";
|
|
14
|
+
import { BUILDCHAIN_KFD1_CONTRACT_WORLD_WITNESS_PATH, BUILDCHAIN_KFD2_CLAIMS_DIR, BUILDCHAIN_KFD3_ARTIFACT_WITNESS_PATH, BUILDCHAIN_KFD3_PREBUILD_WITNESS_PATH } from "../packages/core/buildchain-layout.js";
|
|
18
15
|
import { writeGitHubOutputs } from "./build-contract-core.mjs";
|
|
19
|
-
|
|
16
|
+
const REPOSITORY = "kungfu-systems/buildchain", require = createRequire(import.meta.url), kfdRoot = path.dirname(require.resolve("@kungfu-tech/kfd/package.json"));
|
|
17
|
+
const evidenceSources = { "KFD-1": "dist/site/release-passport-check-manifest.json", "KFD-2": "dist/site/kfd-claims.json", "KFD-3": "dist/site/public-surface-audit.json", "KFD-4": "packages/core/kfd-product-gates.js", "KFD-5": "packages/core/kfd-product-gates.js", "KFD-7": "packages/core/kfd-product-gates.js", "KFD-10": "packages/core/dev-delivery-warrant.js" };
|
|
20
18
|
function parseArgs(argv = process.argv.slice(2)) {
|
|
21
|
-
const args = {
|
|
22
|
-
cwd: process.cwd(),
|
|
23
|
-
outputDir: ".buildchain/kfd",
|
|
24
|
-
sourceSha: process.env.BUILDCHAIN_SOURCE_SHA || "",
|
|
25
|
-
};
|
|
19
|
+
const args = { cwd: process.cwd(), outputDir: ".buildchain/kfd", sourceSha: process.env.BUILDCHAIN_SOURCE_SHA || "" };
|
|
26
20
|
for (let index = 0; index < argv.length; index += 1) {
|
|
27
21
|
const arg = argv[index];
|
|
28
|
-
if (arg === "--cwd")
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
args.sourceSha = argv[++index] || args.sourceSha;
|
|
34
|
-
} else if (arg === "--help") {
|
|
35
|
-
args.help = true;
|
|
36
|
-
} else {
|
|
37
|
-
throw new Error(`unknown argument: ${arg}`);
|
|
38
|
-
}
|
|
22
|
+
if (arg === "--cwd") args.cwd = argv[++index] || args.cwd;
|
|
23
|
+
else if (arg === "--output-dir") args.outputDir = argv[++index] || args.outputDir;
|
|
24
|
+
else if (arg === "--source-sha") args.sourceSha = argv[++index] || args.sourceSha;
|
|
25
|
+
else if (arg === "--help") args.help = true;
|
|
26
|
+
else throw new Error(`unknown argument: ${arg}`);
|
|
39
27
|
}
|
|
40
28
|
return args;
|
|
41
29
|
}
|
|
42
|
-
|
|
43
30
|
function gitSha(cwd) {
|
|
44
|
-
try {
|
|
45
|
-
return execFileSync("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf8" }).trim();
|
|
46
|
-
} catch {
|
|
47
|
-
return "";
|
|
48
|
-
}
|
|
31
|
+
try { return execFileSync("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf8" }).trim(); } catch { return ""; }
|
|
49
32
|
}
|
|
50
|
-
|
|
51
|
-
function
|
|
33
|
+
function digest(bytes) { return `sha256:${crypto.createHash("sha256").update(bytes).digest("hex")}`; }
|
|
34
|
+
function fileDigest(filePath) { return digest(fs.readFileSync(filePath)); }
|
|
35
|
+
function relative(root, filePath) { return path.relative(root, filePath).replace(/\\/g, "/"); }
|
|
36
|
+
function writeJson(root, relativePath, value) {
|
|
37
|
+
const filePath = path.join(root, relativePath);
|
|
52
38
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
53
39
|
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
40
|
+
return { path: relativePath, sha256: fileDigest(filePath), filePath };
|
|
54
41
|
}
|
|
55
|
-
|
|
56
|
-
function
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function generateBuildchainKfdWitnesses({
|
|
61
|
-
cwd = process.cwd(),
|
|
62
|
-
outputDir = ".buildchain/kfd",
|
|
63
|
-
sourceSha = "",
|
|
64
|
-
emitOutputs = true,
|
|
65
|
-
} = {}) {
|
|
66
|
-
const root = path.resolve(cwd);
|
|
67
|
-
const outDir = path.resolve(root, outputDir);
|
|
68
|
-
const resolvedSourceSha = sourceSha || gitSha(root);
|
|
42
|
+
function readJson(filePath, sourceSha = "") { return JSON.parse(fs.readFileSync(filePath, "utf8").replaceAll("{{SOURCE_SHA}}", sourceSha)); }
|
|
43
|
+
export function generateBuildchainKfdWitnesses({ cwd = process.cwd(), outputDir = ".buildchain/kfd", sourceSha = "", emitOutputs = true } = {}) {
|
|
44
|
+
const root = path.resolve(cwd), outDir = path.resolve(root, outputDir), resolvedSourceSha = sourceSha || gitSha(root);
|
|
69
45
|
const outputPath = (canonicalPath) => path.join(outDir, path.relative(".buildchain/kfd", canonicalPath));
|
|
70
|
-
const paths = {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
};
|
|
76
|
-
writeJson(paths.kfd1Witness, createBuildchainKfd1Witness({ root, sourceSha: resolvedSourceSha }));
|
|
77
|
-
writeJson(paths.kfd3PrebuildWitness, createBuildchainKfd3PrebuildWitness({ root, sourceSha: resolvedSourceSha }));
|
|
78
|
-
writeJson(paths.kfd3ArtifactWitness, createBuildchainKfd3ArtifactWitness({ root, sourceSha: resolvedSourceSha }));
|
|
79
|
-
const witnessFiles = {
|
|
80
|
-
"kfd-1-witness": toRepoRelative(root, paths.kfd1Witness),
|
|
81
|
-
"kfd-3-prebuild-witness": toRepoRelative(root, paths.kfd3PrebuildWitness),
|
|
82
|
-
"kfd-3-artifact-witness": toRepoRelative(root, paths.kfd3ArtifactWitness),
|
|
83
|
-
};
|
|
46
|
+
const paths = { kfd1Witness: outputPath(BUILDCHAIN_KFD1_CONTRACT_WORLD_WITNESS_PATH), kfd3PrebuildWitness: outputPath(BUILDCHAIN_KFD3_PREBUILD_WITNESS_PATH), kfd3ArtifactWitness: outputPath(BUILDCHAIN_KFD3_ARTIFACT_WITNESS_PATH), kfd2ClaimsDir: outputPath(BUILDCHAIN_KFD2_CLAIMS_DIR) };
|
|
47
|
+
writeJson(path.dirname(paths.kfd1Witness), path.basename(paths.kfd1Witness), createBuildchainKfd1Witness({ root, sourceSha: resolvedSourceSha }));
|
|
48
|
+
writeJson(path.dirname(paths.kfd3PrebuildWitness), path.basename(paths.kfd3PrebuildWitness), createBuildchainKfd3PrebuildWitness({ root, sourceSha: resolvedSourceSha }));
|
|
49
|
+
writeJson(path.dirname(paths.kfd3ArtifactWitness), path.basename(paths.kfd3ArtifactWitness), createBuildchainKfd3ArtifactWitness({ root, sourceSha: resolvedSourceSha }));
|
|
50
|
+
const witnessFiles = { "kfd-1-witness": relative(root, paths.kfd1Witness), "kfd-3-prebuild-witness": relative(root, paths.kfd3PrebuildWitness), "kfd-3-artifact-witness": relative(root, paths.kfd3ArtifactWitness) };
|
|
84
51
|
const kfd2ClaimPaths = createBuildchainKfd2Claims({ root, witnessFiles }).map((claim) => {
|
|
85
|
-
const slug = String(claim.id || "claim")
|
|
86
|
-
|
|
87
|
-
.replace(/[^0-9A-Za-z._-]+/g, "-")
|
|
88
|
-
.replace(/^-+|-+$/g, "") || "claim";
|
|
89
|
-
const claimPath = path.join(paths.kfd2ClaimsDir, `${slug}.json`);
|
|
90
|
-
writeJson(claimPath, claim);
|
|
91
|
-
return claimPath;
|
|
52
|
+
const slug = String(claim.id || "claim").replace(/^claim:/, "").replace(/[^0-9A-Za-z._-]+/g, "-").replace(/^-+|-+$/g, "") || "claim";
|
|
53
|
+
return writeJson(paths.kfd2ClaimsDir, `${slug}.json`, claim).filePath;
|
|
92
54
|
});
|
|
93
|
-
const outputs = {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
"
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
55
|
+
const outputs = { "kfd-1-witness-jsons": relative(root, paths.kfd1Witness), "kfd-2-claim-jsons": kfd2ClaimPaths.map((filePath) => relative(root, filePath)).join(","), "kfd-3-prebuild-witness-jsons": relative(root, paths.kfd3PrebuildWitness), "kfd-3-artifact-witness-jsons": relative(root, paths.kfd3ArtifactWitness), "source-sha": resolvedSourceSha, "output-dir": relative(root, outDir) };
|
|
56
|
+
if (emitOutputs) writeGitHubOutputs(outputs);
|
|
57
|
+
return { schemaVersion: 1, contract: "kungfu-buildchain-self-kfd-witness-generation", outputs };
|
|
58
|
+
}
|
|
59
|
+
function productRecords(root, standard, sourceSha) {
|
|
60
|
+
if (standard === "kfd-4") return [
|
|
61
|
+
["observer-perspective", readJson(path.join(root, "contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json"), sourceSha)],
|
|
62
|
+
["perspective-replay", readJson(path.join(root, "contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json"), sourceSha)],
|
|
63
|
+
];
|
|
64
|
+
if (standard === "kfd-5") return [["primitive-discovery", readJson(path.join(kfdRoot, "cases/live/software-work-perspective-settlement/cuts/0001-assignment.json"))]];
|
|
65
|
+
const profile = readJson(path.join(kfdRoot, "verifier/fixtures/kfd-7/valid-domain-profile.json"));
|
|
66
|
+
profile.evidenceObligations = profile.evidenceObligations.map((entry) => ({ ...entry, status: "passed", artifactRefs: ["qualification-proof"], residualRisk: "Bound to the retained Buildchain product gate cut; independent certification remains external." }));
|
|
67
|
+
profile.activation = { decision: "activate", evidenceCut: `git://${REPOSITORY}@${sourceSha}`, independentReview: "review://protected-release-review", productWitnesses: ["qualification-proof"], residualRisk: "The product gate is non-qualifying and non-self-certifying." };
|
|
68
|
+
profile.domainProfile = { ...profile.domainProfile, product: "Buildchain", implementation: `git+https://github.com/${REPOSITORY}@${sourceSha}`, qualificationStatus: "qualified" };
|
|
69
|
+
return [["domain-profile", profile]];
|
|
70
|
+
}
|
|
71
|
+
async function generateProductGate(root, outDir, standard, sourceSha, checkedAt) {
|
|
72
|
+
const records = productRecords(root, standard, sourceSha).map(([role, value], index) => ({ role, ...writeJson(outDir, `records/${standard}-${index}.json`, value) }));
|
|
73
|
+
const kinds = standard === "kfd-4" ? [["projection-fsck", "projection-fsck"], ["negative", "negative-fixture"]] : standard === "kfd-5" ? [["negative", "negative-fixture"]] : [["qualification-proof", "qualification-proof"], ["independent-review", "independent-review"], ["negative", "negative-fixture"]];
|
|
74
|
+
const evidence = kinds.map(([id, kind]) => ({ id, kind, ...writeJson(outDir, `evidence/${standard}-${id}.json`, { schemaVersion: 1, contract: "kungfu-buildchain-kfd-adopter-evidence-reference", id, kind, source: { repository: REPOSITORY, sha: sourceSha }, observedAt: checkedAt, status: "retained", nonClaim: "This product-owned reference does not independently qualify or certify KFD adoption." }) }));
|
|
75
|
+
const input = { schemaVersion: 1, contract: KFD_PRODUCT_GATE_INPUT_CONTRACT, standard, standardRevision: kfdStandards.standards[standard].revision, source: { repository: REPOSITORY, sha: sourceSha }, evidenceCut: { generatedAt: checkedAt, expiresAt: new Date(Date.parse(checkedAt) + 86400000).toISOString() }, records: records.map(({ role, path, sha256 }) => ({ role, path, sha256 })), evidence: evidence.map(({ id, kind, path, sha256 }) => ({ id, kind, path, sha256 })), responsibility: { owner: REPOSITORY, evidenceOwner: "Buildchain maintainers", proofOwner: "Buildchain release gate" }, nonClaims: ["A passed product gate does not qualify, certify, activate, or independently approve KFD adoption."] };
|
|
76
|
+
const gate = await evaluateKfdProductGate({ cwd: outDir, input, expectedSourceSha: sourceSha, checkedAt });
|
|
77
|
+
if (gate.status !== "passed" || !validateKfdProductGateResult(gate, { expectedSourceSha: sourceSha, checkedAt }).valid) throw new Error(`${standard} product gate failed: ${JSON.stringify(gate.issues)}`);
|
|
78
|
+
writeJson(outDir, `${standard}/product-gate.json`, gate);
|
|
79
|
+
return gate;
|
|
80
|
+
}
|
|
81
|
+
export async function generateBuildchainKfdAdopterRelease({ cwd = process.cwd(), outputDir = ".buildchain/kfd/adopter-release", sourceSha = "", checkedAt = new Date().toISOString(), emitOutputs = true } = {}) {
|
|
82
|
+
const root = path.resolve(cwd), outDir = path.resolve(root, outputDir);
|
|
83
|
+
if (!/^[0-9a-f]{40}$/.test(sourceSha) || !Number.isFinite(Date.parse(checkedAt))) throw new Error("source SHA and checked-at must be exact");
|
|
84
|
+
for (const sourcePath of Object.values(evidenceSources)) if (!fs.statSync(path.join(root, sourcePath)).isFile()) throw new Error(`required Buildchain evidence source is missing: ${sourcePath}`);
|
|
85
|
+
const packageArtifactRoot = installedKfdPackageArtifactRoot(), gates = [];
|
|
86
|
+
for (const standard of ["kfd-4", "kfd-5", "kfd-7"]) gates.push(await generateProductGate(root, outDir, standard, sourceSha, checkedAt));
|
|
87
|
+
const sourceRoot = kfdProductGateDigest({ repository: REPOSITORY, sourceSha, files: [...new Set(Object.values(evidenceSources))].sort().map((sourcePath) => ({ path: sourcePath, sha256: fileDigest(path.join(root, sourcePath)) })) });
|
|
88
|
+
let manifest = initAdopterManifest({ manifestId: "buildchain-v3-full-cut", adopterId: REPOSITORY, artifactKind: "git-commit", artifactCoordinate: `${REPOSITORY}@${sourceSha}`, artifactRoot: sourceRoot, scope: "Buildchain v3 release and protected delivery authority", packageArtifactRoot, verifiedAt: checkedAt, maxAgeSeconds: 86400, packageRoot: kfdRoot });
|
|
89
|
+
const gateById = new Map(gates.map((gate) => [gate.standard.toUpperCase(), gate]));
|
|
90
|
+
for (const id of ["KFD-1", "KFD-2", "KFD-3", "KFD-4", "KFD-5", "KFD-7"]) {
|
|
91
|
+
const row = manifest.decisions.find((entry) => entry.id === id);
|
|
92
|
+
const sourcePath = evidenceSources[id];
|
|
93
|
+
const sourceEvidence = (kind, rootValue = fileDigest(path.join(root, sourcePath))) => ({ kind, coordinate: `git+https://github.com/${REPOSITORY}@${sourceSha}#${sourcePath}`, root: rootValue, observedAt: checkedAt, kfdPackageRoot: packageArtifactRoot });
|
|
94
|
+
row.state = "candidate"; row.usage = "used"; row.implementationEvidence = [sourceEvidence("implementation")]; row.verificationEvidence = [sourceEvidence("verification", gateById.get(id)?.gateRoot)]; row.gaps = ["Independent decision-specific assessment and certification remain external."];
|
|
103
95
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
};
|
|
96
|
+
const kfd6 = manifest.decisions.find((entry) => entry.id === "KFD-6");
|
|
97
|
+
kfd6.state = "unsupported"; kfd6.usage = "unused"; kfd6.gaps = ["Buildchain does not claim KFD-6 support in this cut."];
|
|
98
|
+
manifest = addAdopterWitness(manifest, { decisionId: "KFD-10", profileId: "kfd-warrant-evidence", witnessCoordinate: `git+https://github.com/${REPOSITORY}@${sourceSha}#${evidenceSources["KFD-10"]}`, witnessRoot: fileDigest(path.join(root, evidenceSources["KFD-10"])), packageArtifactRoot, verifiedAt: checkedAt, maxAgeSeconds: 86400, packageRoot: kfdRoot });
|
|
99
|
+
const manifestPath = writeJson(outDir, "kfd-adopter-manifest.json", manifest).filePath, manifestGate = createKfdAdopterManifestGate({ manifest, packageArtifactRoot, gateResults: gates, authorityPath: "kfd-adopter-manifest.json", expectedSourceSha: sourceSha, checkedAt });
|
|
100
|
+
if (!validateKfdAdopterManifestGate(manifestGate, { expectedSourceSha: sourceSha, checkedAt }).valid) throw new Error(`generated adopter manifest gate failed: ${JSON.stringify(manifestGate.issues)}`);
|
|
101
|
+
const gatePath = writeJson(outDir, "kfd-adopter-manifest-gate.json", manifestGate).filePath, support = createKfdLegacySupportMatrixProjection({ manifest, manifestGate });
|
|
102
|
+
if (!validateKfdLegacySupportMatrixProjection(support, { manifest, manifestGate }).valid) throw new Error("generated KFD support projection failed");
|
|
103
|
+
const supportPath = writeJson(outDir, "kfd-support.json", support).filePath;
|
|
104
|
+
const outputs = { "kfd-adopter-manifest-json": relative(root, manifestPath), "kfd-adopter-manifest-gate-json": relative(root, gatePath), "kfd-support-matrix-json": relative(root, supportPath), "kfd-product-gate-jsons": gates.map((gate) => relative(root, path.join(outDir, gate.standard, "product-gate.json"))).join(","), "kfd-adopter-manifest-root": manifestGate.authority.manifestRoot, "kfd-adopter-gate-root": manifestGate.gateRoot, "kfd-package-artifact-root": packageArtifactRoot, "source-sha": sourceSha };
|
|
105
|
+
if (emitOutputs) writeGitHubOutputs(outputs);
|
|
106
|
+
return { schemaVersion: 1, contract: "kungfu-buildchain-kfd-adopter-release-generation", status: "passed", qualifying: false, selfCertified: false, outputs };
|
|
109
107
|
}
|
|
110
|
-
|
|
111
108
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
112
109
|
try {
|
|
113
110
|
const args = parseArgs();
|
|
114
|
-
if (args.help)
|
|
115
|
-
|
|
116
|
-
process.exit(0);
|
|
117
|
-
}
|
|
118
|
-
process.stdout.write(`${JSON.stringify(generateBuildchainKfdWitnesses(args), null, 2)}\n`);
|
|
111
|
+
if (args.help) process.stdout.write("Usage: generate-buildchain-kfd-witnesses [--cwd <repo>] [--output-dir <dir>] [--source-sha <sha>]\n");
|
|
112
|
+
else process.stdout.write(`${JSON.stringify(generateBuildchainKfdWitnesses(args), null, 2)}\n`);
|
|
119
113
|
} catch (error) {
|
|
120
114
|
console.error(`buildchain self KFD witnesses: ${error.message}`);
|
|
121
115
|
process.exitCode = 1;
|
|
@@ -46,26 +46,30 @@ function routerInputs(inputBlock) {
|
|
|
46
46
|
|
|
47
47
|
function routerOutputs(outputBlock) {
|
|
48
48
|
const forwarded = outputBlock
|
|
49
|
-
.replace(
|
|
49
|
+
.replace(
|
|
50
|
+
/^ value: \$\{\{ jobs\.[^.]+\.outputs\.([a-z0-9-]+) \|\| jobs\.[^.]+\.outputs\.\1 \}\}$/gm,
|
|
51
|
+
" value: ${{ jobs.alpha.outputs.$1 || jobs.stable.outputs.$1 }}",
|
|
52
|
+
)
|
|
53
|
+
.replace(/^ value: \$\{\{ jobs\.[^.]+\.outputs\.([^ }]+) \}\}$/gm, " value: ${{ jobs.alpha.outputs.$1 || jobs.stable.outputs.$1 }}")
|
|
50
54
|
.replaceAll("build-lifecycle", "build-channel-router")
|
|
51
|
-
.replace("value: ${{ jobs.
|
|
52
|
-
.replace("value: ${{ jobs.
|
|
53
|
-
.replace("value: ${{ jobs.
|
|
54
|
-
.replace("value: ${{ jobs.
|
|
55
|
-
.replace("value: ${{ jobs.
|
|
56
|
-
.replace("value: ${{ jobs.
|
|
57
|
-
.replace("value: ${{ jobs.
|
|
55
|
+
.replace("value: ${{ jobs.alpha.outputs.controller-plan-artifact || jobs.stable.outputs.controller-plan-artifact }}", "value: ${{ jobs.controller-plan.outputs.controller-plan-artifact }}")
|
|
56
|
+
.replace("value: ${{ jobs.alpha.outputs.controller-plan-json || jobs.stable.outputs.controller-plan-json }}", "value: ${{ jobs.controller-plan.outputs.controller-plan-json }}")
|
|
57
|
+
.replace("value: ${{ jobs.alpha.outputs.controller-plan-digest || jobs.stable.outputs.controller-plan-digest }}", "value: ${{ jobs.controller-plan.outputs.controller-plan-digest }}")
|
|
58
|
+
.replace("value: ${{ jobs.alpha.outputs.controller-receipt-artifact || jobs.stable.outputs.controller-receipt-artifact }}", "value: ${{ jobs.controller-receipt.outputs.controller-receipt-artifact }}")
|
|
59
|
+
.replace("value: ${{ jobs.alpha.outputs.controller-receipt-json || jobs.stable.outputs.controller-receipt-json }}", "value: ${{ jobs.controller-receipt.outputs.controller-receipt-json }}")
|
|
60
|
+
.replace("value: ${{ jobs.alpha.outputs.controller-receipt-digest || jobs.stable.outputs.controller-receipt-digest }}", "value: ${{ jobs.controller-receipt.outputs.controller-receipt-digest }}")
|
|
61
|
+
.replace("value: ${{ jobs.alpha.outputs.controller-receipt-status || jobs.stable.outputs.controller-receipt-status }}", "value: ${{ jobs.controller-receipt.outputs.controller-receipt-status }}")
|
|
58
62
|
.replace(
|
|
59
|
-
/( credential-island-macos-artifact:\n(?: .*\n)*? value:) \$\{\{ jobs\.
|
|
60
|
-
"$1 ${{ jobs.
|
|
63
|
+
/( credential-island-macos-artifact:\n(?: .*\n)*? value:) \$\{\{ jobs\.alpha\.outputs\.artifact-name \|\| jobs\.stable\.outputs\.artifact-name \}\}/,
|
|
64
|
+
"$1 ${{ jobs.alpha.outputs.credential-island-macos-artifact || jobs.stable.outputs.credential-island-macos-artifact }}",
|
|
61
65
|
)
|
|
62
66
|
.replace(
|
|
63
|
-
/( credential-island-macos-manifest-artifact:\n(?: .*\n)*? value:) \$\{\{ jobs\.
|
|
64
|
-
"$1 ${{ jobs.
|
|
67
|
+
/( credential-island-macos-manifest-artifact:\n(?: .*\n)*? value:) \$\{\{ jobs\.alpha\.outputs\.manifest-artifact-name \|\| jobs\.stable\.outputs\.manifest-artifact-name \}\}/,
|
|
68
|
+
"$1 ${{ jobs.alpha.outputs.credential-island-macos-manifest-artifact || jobs.stable.outputs.credential-island-macos-manifest-artifact }}",
|
|
65
69
|
);
|
|
66
70
|
return [
|
|
67
71
|
" buildchain-channel:",
|
|
68
|
-
' description: "Resolved Buildchain channel: alpha
|
|
72
|
+
' description: "Resolved Buildchain channel: alpha or stable"',
|
|
69
73
|
" value: ${{ jobs.resolve-channel.outputs.channel }}",
|
|
70
74
|
" buildchain-channel-selection-source:",
|
|
71
75
|
' description: "Evidence source used to select the Buildchain channel"',
|
|
@@ -154,7 +158,8 @@ function routerControllerReceiptJob() {
|
|
|
154
158
|
needs:
|
|
155
159
|
- resolve-channel
|
|
156
160
|
- controller-plan
|
|
157
|
-
-
|
|
161
|
+
- alpha
|
|
162
|
+
- stable
|
|
158
163
|
if: \${{ always() && needs.controller-plan.result == 'success' }}
|
|
159
164
|
runs-on: \${{ fromJSON(inputs.control-runner-json) }}
|
|
160
165
|
outputs:
|
|
@@ -183,9 +188,9 @@ function routerControllerReceiptJob() {
|
|
|
183
188
|
env:
|
|
184
189
|
BUILDCHAIN_CONTROLLER_PLAN_PATH: .buildchain/controller/plan.json
|
|
185
190
|
BUILDCHAIN_CONTROLLER_STAGES_JSON: >-
|
|
186
|
-
[{"id":"resolve-channel","status":"\${{ needs.resolve-channel.result }}"},{"id":"
|
|
187
|
-
BUILDCHAIN_CONTROLLER_EVIDENCE_JSON: \${{ needs.
|
|
188
|
-
BUILDCHAIN_CONTROLLER_REASON_CODE: \${{ needs.
|
|
191
|
+
[{"id":"resolve-channel","status":"\${{ needs.resolve-channel.result }}"},{"id":"alpha","status":"\${{ needs.alpha.result }}"},{"id":"stable","status":"\${{ needs.stable.result }}"},{"id":"aggregate","status":"\${{ needs.resolve-channel.outputs.channel == 'alpha' && needs.alpha.result || needs.stable.result }}"}]
|
|
192
|
+
BUILDCHAIN_CONTROLLER_EVIDENCE_JSON: \${{ (needs.alpha.outputs.controller-receipt-digest != '' || needs.stable.outputs.controller-receipt-digest != '') && format('[{{"kind":"nested-controller-receipt","digest":"{0}"}}]', needs.alpha.outputs.controller-receipt-digest || needs.stable.outputs.controller-receipt-digest) || '[]' }}
|
|
193
|
+
BUILDCHAIN_CONTROLLER_REASON_CODE: \${{ ((needs.resolve-channel.outputs.channel == 'alpha' && needs.alpha.result == 'success') || (needs.resolve-channel.outputs.channel == 'stable' && needs.stable.result == 'success')) && '' || 'nested-build-incomplete' }}
|
|
189
194
|
BUILDCHAIN_CONTROLLER_REASON_SUMMARY: Nested build controller did not complete successfully
|
|
190
195
|
BUILDCHAIN_CONTROLLER_RECEIPT_ARTIFACT: buildchain-channel-controller-receipt-\${{ github.sha }}
|
|
191
196
|
BUILDCHAIN_CONTROLLER_RECEIPT_PATH: .buildchain/controller/receipt.json
|
|
@@ -215,7 +220,9 @@ function routerAggregateJob() {
|
|
|
215
220
|
return ` summarize:
|
|
216
221
|
name: Summarize build contract
|
|
217
222
|
needs:
|
|
218
|
-
-
|
|
223
|
+
- resolve-channel
|
|
224
|
+
- alpha
|
|
225
|
+
- stable
|
|
219
226
|
- controller-receipt
|
|
220
227
|
if: \${{ always() }}
|
|
221
228
|
runs-on: \${{ fromJSON(inputs.control-runner-json) }}
|
|
@@ -223,12 +230,16 @@ function routerAggregateJob() {
|
|
|
223
230
|
- name: Enforce public channel router aggregate
|
|
224
231
|
shell: bash
|
|
225
232
|
env:
|
|
226
|
-
|
|
233
|
+
CHANNEL: \${{ needs.resolve-channel.outputs.channel }}
|
|
234
|
+
ALPHA_RESULT: \${{ needs.alpha.result }}
|
|
235
|
+
STABLE_RESULT: \${{ needs.stable.result }}
|
|
227
236
|
CONTROLLER_RECEIPT_RESULT: \${{ needs.controller-receipt.result }}
|
|
228
237
|
run: |
|
|
229
238
|
set -euo pipefail
|
|
230
|
-
|
|
231
|
-
|
|
239
|
+
selected_result="\${STABLE_RESULT}"
|
|
240
|
+
if [[ "\${CHANNEL}" = "alpha" ]]; then selected_result="\${ALPHA_RESULT}"; fi
|
|
241
|
+
if [[ "\${selected_result}" != "success" || "\${CONTROLLER_RECEIPT_RESULT}" != "success" ]]; then
|
|
242
|
+
echo "::error::Buildchain channel router did not qualify: channel=\${CHANNEL} selected=\${selected_result} controller-receipt=\${CONTROLLER_RECEIPT_RESULT}"
|
|
232
243
|
exit 1
|
|
233
244
|
fi
|
|
234
245
|
echo "Buildchain channel router aggregate passed."`;
|
|
@@ -245,6 +256,47 @@ function generateChannelBuildWorkflowBase(source) {
|
|
|
245
256
|
return `# Generated by scripts/generate-channel-build-workflow.mjs. Do not edit directly.\nname: Buildchain Channel Build\n\non:\n workflow_call:\n inputs:\n${routerInputs(inputs)}\n secrets:\n${secrets.trimEnd()}\n outputs:\n${routerOutputs(outputs)}\n\npermissions:\n contents: read\n issues: write\n id-token: write\n\njobs:\n resolve-channel:\n name: Resolve Buildchain channel\n runs-on: ubuntu-24.04\n outputs:\n channel: \${{ steps.channel.outputs.channel }}\n buildchain-ref: \${{ steps.channel.outputs.buildchain-ref }}\n contract-lock-path: \${{ steps.lock.outputs.path }}\n selection-source: \${{ steps.channel.outputs.selection-source }}\n reason: \${{ steps.channel.outputs.reason }}\n steps:\n - name: Resolve router source\n id: router\n shell: bash\n env:\n BUILDCHAIN_ROUTER_WORKFLOW_REF: \${{ job.workflow_ref }}\n run: |\n set -euo pipefail\n workflow_ref=\"\${BUILDCHAIN_ROUTER_WORKFLOW_REF}\"\n repository=\"\${workflow_ref%%/.github/workflows/*}\"\n ref=\"\${workflow_ref##*@}\"\n ref=\"\${ref#refs/heads/}\"\n ref=\"\${ref#refs/tags/}\"\n if [[ -z \"\${repository}\" || \"\${repository}\" = \"\${workflow_ref}\" || -z \"\${ref}\" ]]; then\n echo \"Unable to resolve Buildchain router source from job.workflow_ref=\${workflow_ref}\" >&2\n exit 1\n fi\n {\n echo \"repository=\${repository}\"\n echo \"ref=\${ref}\"\n } >> \"\${GITHUB_OUTPUT}\"\n\n - name: Checkout Buildchain router\n uses: actions/checkout@v7.0.0\n with:\n repository: \${{ steps.router.outputs.repository }}\n ref: \${{ steps.router.outputs.ref }}\n path: .buildchain/router\n persist-credentials: false\n\n - name: Resolve channel\n id: channel\n shell: bash\n env:\n BUILDCHAIN_CHANNEL: \${{ inputs.buildchain-channel }}\n BUILDCHAIN_REQUESTED_REF: \${{ inputs.buildchain-ref }}\n BUILDCHAIN_PUBLISH_CHANNEL: \${{ inputs.publish-channel }}\n BUILDCHAIN_EVENT_NAME: \${{ github.event_name }}\n BUILDCHAIN_GIT_REF: \${{ github.ref }}\n BUILDCHAIN_RELEASE_PRERELEASE: \${{ github.event.release.prerelease }}\n BUILDCHAIN_ROUTER_REF: \${{ steps.router.outputs.ref }}\n run: |\n node .buildchain/router/scripts/buildchain-channel-router.mjs \\\n --cwd .buildchain/router \\\n --channel \"\${BUILDCHAIN_CHANNEL}\" \\\n --buildchain-ref \"\${BUILDCHAIN_REQUESTED_REF}\" \\\n --publish-channel \"\${BUILDCHAIN_PUBLISH_CHANNEL}\" \\\n --event-name \"\${BUILDCHAIN_EVENT_NAME}\" \\\n --ref \"\${BUILDCHAIN_GIT_REF}\" \\\n --release-prerelease \"\${BUILDCHAIN_RELEASE_PRERELEASE}\" \\\n --router-ref \"\${BUILDCHAIN_ROUTER_REF}\"\n\n - name: Select consumer contract lock\n id: lock\n shell: bash\n env:\n EXPLICIT_PATH: \${{ inputs.buildchain-contract-lock-path }}\n ALPHA_PATH: \${{ inputs.buildchain-alpha-contract-lock-path }}\n STABLE_PATH: \${{ inputs.buildchain-stable-contract-lock-path }}\n CHANNEL: \${{ steps.channel.outputs.channel }}\n run: |\n set -euo pipefail\n path=\"\${EXPLICIT_PATH}\"\n if [[ -z \"\${path}\" ]]; then\n if [[ \"\${CHANNEL}\" = \"alpha\" ]]; then\n path=\"\${ALPHA_PATH}\"\n else\n path=\"\${STABLE_PATH}\"\n fi\n fi\n echo \"path=\${path}\" >> \"\${GITHUB_OUTPUT}\"\n\n build:\n name: Build with resolved channel\n needs: resolve-channel\n uses: ./.github/workflows/.build.yml\n permissions:\n contents: read\n issues: write\n id-token: write\n with:\n${forwardedInputs(names)}\n secrets: inherit\n`;
|
|
246
257
|
}
|
|
247
258
|
|
|
259
|
+
function splitBuildJobsByChannel(workflow, names, major) {
|
|
260
|
+
const marker = "\n build:\n";
|
|
261
|
+
const index = workflow.indexOf(marker);
|
|
262
|
+
if (index < 0) throw new Error("generated workflow is missing the build job");
|
|
263
|
+
const inputs = forwardedInputs(names);
|
|
264
|
+
const jobs = `
|
|
265
|
+
alpha:
|
|
266
|
+
name: Build alpha channel
|
|
267
|
+
needs:
|
|
268
|
+
- resolve-channel
|
|
269
|
+
- controller-plan
|
|
270
|
+
if: \${{ needs.resolve-channel.outputs.channel == 'alpha' }}
|
|
271
|
+
uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v${major}-alpha
|
|
272
|
+
permissions:
|
|
273
|
+
actions: read
|
|
274
|
+
contents: read
|
|
275
|
+
issues: write
|
|
276
|
+
id-token: write
|
|
277
|
+
with:
|
|
278
|
+
${inputs}
|
|
279
|
+
secrets: inherit
|
|
280
|
+
|
|
281
|
+
stable:
|
|
282
|
+
name: Build stable channel
|
|
283
|
+
needs:
|
|
284
|
+
- resolve-channel
|
|
285
|
+
- controller-plan
|
|
286
|
+
if: \${{ needs.resolve-channel.outputs.channel == 'stable' }}
|
|
287
|
+
uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v${major}
|
|
288
|
+
permissions:
|
|
289
|
+
actions: read
|
|
290
|
+
contents: read
|
|
291
|
+
issues: write
|
|
292
|
+
id-token: write
|
|
293
|
+
with:
|
|
294
|
+
${inputs}
|
|
295
|
+
secrets: inherit
|
|
296
|
+
`;
|
|
297
|
+
return `${workflow.slice(0, index)}${jobs}`;
|
|
298
|
+
}
|
|
299
|
+
|
|
248
300
|
function bindRouterCheckoutToWorkflowSha(workflow) {
|
|
249
301
|
return workflow
|
|
250
302
|
.replace(
|
|
@@ -294,7 +346,14 @@ function bindRouterCheckoutToWorkflowSha(workflow) {
|
|
|
294
346
|
}
|
|
295
347
|
|
|
296
348
|
export function generateChannelBuildWorkflow(source) {
|
|
297
|
-
const
|
|
349
|
+
const inputBlock = blockBetween(source, " inputs:\n", " secrets:\n");
|
|
350
|
+
const names = inputNames(inputBlock);
|
|
351
|
+
const packageVersion = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8")).version || "";
|
|
352
|
+
const major = Number(String(packageVersion).split(".")[0]);
|
|
353
|
+
if (!Number.isInteger(major) || major < 1) throw new Error("package.json version must declare a positive major");
|
|
354
|
+
const generated = bindRouterCheckoutToWorkflowSha(
|
|
355
|
+
splitBuildJobsByChannel(generateChannelBuildWorkflowBase(source), names, major),
|
|
356
|
+
)
|
|
298
357
|
.replaceAll("runs-on: ubuntu-24.04", "runs-on: ${{ fromJSON(inputs.control-runner-json) }}")
|
|
299
358
|
.replace(
|
|
300
359
|
"\npermissions:\n contents: read\n",
|
|
@@ -310,15 +369,7 @@ export function generateChannelBuildWorkflow(source) {
|
|
|
310
369
|
"",
|
|
311
370
|
].join("\n"),
|
|
312
371
|
)
|
|
313
|
-
.replace("\n
|
|
314
|
-
.replace(
|
|
315
|
-
" needs: resolve-channel\n uses: ./.github/workflows/.build.yml",
|
|
316
|
-
" needs:\n - resolve-channel\n - controller-plan\n uses: ./.github/workflows/.build.yml",
|
|
317
|
-
)
|
|
318
|
-
.replace(
|
|
319
|
-
" uses: ./.github/workflows/.build.yml\n permissions:\n contents: read\n",
|
|
320
|
-
" uses: ./.github/workflows/.build.yml\n permissions:\n actions: read\n contents: read\n",
|
|
321
|
-
);
|
|
372
|
+
.replace("\n alpha:\n", `\n${routerControllerPlanJob()}\n\n alpha:\n`);
|
|
322
373
|
return `${generated.trimEnd()}\n\n${routerControllerReceiptJob()}\n\n${routerAggregateJob()}\n`;
|
|
323
374
|
}
|
|
324
375
|
|
|
@@ -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",
|
|
@@ -929,7 +931,7 @@ function buildSiteBundle() {
|
|
|
929
931
|
"schemas/release-tail-capabilities-v1.schema.json",
|
|
930
932
|
"schemas/release-tail-provider-bindings-v1.schema.json",
|
|
931
933
|
"schemas/kfd-agent-hub-adoption.schema.json",
|
|
932
|
-
"schemas/kfd-product-gate-input-v1.schema.json",
|
|
934
|
+
"schemas/kfd-product-gate-input-v1.schema.json", "schemas/dev-delivery-authority-v2.schema.json",
|
|
933
935
|
"kfd-support.json",
|
|
934
936
|
"artifact-evidence.json",
|
|
935
937
|
"product-mechanism.json",
|
|
@@ -966,7 +968,7 @@ function buildSiteBundle() {
|
|
|
966
968
|
"schemas/release-tail-capabilities-v1.schema.json",
|
|
967
969
|
"schemas/release-tail-provider-bindings-v1.schema.json",
|
|
968
970
|
"schemas/kfd-agent-hub-adoption.schema.json",
|
|
969
|
-
"schemas/kfd-product-gate-input-v1.schema.json",
|
|
971
|
+
"schemas/kfd-product-gate-input-v1.schema.json", "schemas/dev-delivery-authority-v2.schema.json",
|
|
970
972
|
"buildchain-contract.json",
|
|
971
973
|
"kfd-claims.json",
|
|
972
974
|
"product-mechanism.json",
|
|
@@ -1099,7 +1101,7 @@ function buildSiteBundle() {
|
|
|
1099
1101
|
"schemas/release-tail-capabilities-v1.schema.json",
|
|
1100
1102
|
"schemas/release-tail-provider-bindings-v1.schema.json",
|
|
1101
1103
|
"schemas/kfd-agent-hub-adoption.schema.json",
|
|
1102
|
-
"schemas/kfd-product-gate-input-v1.schema.json",
|
|
1104
|
+
"schemas/kfd-product-gate-input-v1.schema.json", "schemas/dev-delivery-authority-v2.schema.json",
|
|
1103
1105
|
"buildchain-contract.json",
|
|
1104
1106
|
"kfd-upstream-aggregate.json",
|
|
1105
1107
|
"kfd-claims.json",
|
|
@@ -1283,6 +1285,9 @@ function buildSiteBundle() {
|
|
|
1283
1285
|
),
|
|
1284
1286
|
"schemas/kfd-agent-hub-adoption.schema.json": KFD_AGENT_HUB_ADOPTION_SCHEMA,
|
|
1285
1287
|
"schemas/kfd-product-gate-input-v1.schema.json": KFD_PRODUCT_GATE_INPUT_SCHEMA,
|
|
1288
|
+
"schemas/dev-delivery-authority-v2.schema.json": readJson(
|
|
1289
|
+
"contracts/dev-delivery-authority-v2.schema.json",
|
|
1290
|
+
),
|
|
1286
1291
|
"badge-endpoint-registry.json": badgeEndpointRegistry,
|
|
1287
1292
|
"publication-registry.json": publicationRegistry,
|
|
1288
1293
|
"buildchain-contract.json": createBuildchainContractWorld({ root, controllerRegistry }),
|