@kungfu-tech/buildchain 4.0.0 → 4.0.1-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/AGENTS.md +13 -5
- package/actions/promote-buildchain-ref/README.md +11 -6
- package/actions/release-tail/README.md +8 -0
- package/architecture/ci-lane-change-budget.json +187 -0
- package/architecture/decisions/0002-next-development-transition.md +101 -0
- package/architecture/decisions/0003-two-phase-delivery-warrant.md +152 -0
- package/architecture/internal-capabilities.json +219 -7
- package/architecture/maintainability-policy.json +246 -29
- package/architecture/release-tail-contract-inventory.json +5 -2
- package/architecture/v3-core-mechanism-inventory.json +91 -0
- package/architecture/v4-adopter-delivery-parity.json +32 -5
- package/architecture/v4-capability-state-machine-manifest.json +73 -0
- package/architecture/v4-compatibility-facts-parity.json +77 -0
- package/architecture/v4-delivery-authority-parity.json +250 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.json +41 -9
- package/architecture/v4-floating-consumer-policy.json +81 -0
- package/architecture/v4-floating-consumer-policy.md +32 -0
- package/architecture/v4-next-development-parity.json +192 -0
- package/architecture/v4-publication-rehearsal-parity.json +84 -0
- package/architecture/v4-release-train-parity.json +1 -0
- package/architecture/v4-runtime-ref-resume-authority.json +64 -0
- package/architecture/v4-stage-capsule-qualification.json +2 -2
- package/architecture/v4-tail-reseal-parity.json +150 -0
- package/bin/buildchain.mjs +32 -3
- package/bin/internal/adopter-delivery-cli.mjs +70 -0
- package/bin/internal/command-registry.mjs +3 -0
- package/bin/internal/compatibility-facts-cli.mjs +101 -0
- package/bin/internal/trust-release-release-handlers.mjs +5 -0
- package/contracts/dev-delivery-authority-v2.schema.json +662 -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/fixtures/v4-adopter-delivery-v1/archive-template.json +19 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/bootstrap-positive.json +93 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/gate-positive.json +29 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/gate-unknown-selector.json +26 -0
- package/contracts/fixtures/v4-adopter-delivery-v1/offline-vectors.json +50 -0
- package/contracts/fixtures/v4-compatibility-facts-v1/shared.json +1129 -0
- package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +59 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/artifacts/product.bin +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/config/buildchain.toml +5 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/release-activation.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/documents/signed-channel.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/evidence/qualification.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/candidate.json +5 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/candidate/manifests/release-passport.json +1 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/capsule.json +707 -0
- package/contracts/fixtures/v4-publication-rehearsal-v1/offline-vectors.json +27 -0
- package/contracts/fixtures/v4-runtime-ref-resume-authority-v1/scenario.json +15 -0
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +229 -0
- package/contracts/next-development-request-v1.schema.json +66 -0
- package/contracts/next-development-transition-v1.schema.json +292 -0
- package/contracts/v4-adopter-delivery-v1.schema.json +81 -0
- package/contracts/v4-compatibility-facts-v1.schema.json +230 -0
- package/contracts/v4-floating-consumer-policy-receipt-v1.schema.json +107 -0
- package/contracts/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/contracts/v4-runtime-ref-resume-authority-v1.schema.json +235 -0
- package/contracts/v4-tail-reseal-v1.schema.json +276 -0
- package/dist/site/agent-index.json +5 -0
- package/dist/site/artifact-schemas.json +10 -0
- package/dist/site/buildchain-contract.json +1562 -44
- package/dist/site/buildchain-site.json +684 -42
- package/dist/site/capability-registry.json +17 -14
- package/dist/site/cli-registry.json +205 -3
- package/dist/site/controller-registry.json +62 -6
- package/dist/site/kfd-claims.json +551 -22
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +55 -9
- package/dist/site/node-api-registry.json +15247 -8690
- package/dist/site/page-registry.json +647 -29
- package/dist/site/public-surface-audit.json +876 -55
- package/dist/site/publication-authority-registry.json +44 -8
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +26 -0
- package/dist/site/schemas/dev-delivery-authority-v2.schema.json +662 -0
- package/dist/site/schemas/release-tail-capabilities-v1.schema.json +199 -0
- package/dist/site/schemas/v4-adopter-delivery-v1.schema.json +81 -0
- package/dist/site/schemas/v4-compatibility-facts-v1.schema.json +230 -0
- package/dist/site/schemas/v4-publication-rehearsal-capsule-v1.schema.json +233 -0
- package/dist/site/site-manifest.json +37 -13
- package/dist/site/workflow-registry.json +200 -22
- package/docs/MAP.md +10 -2
- package/docs/cli-reference.md +404 -13
- package/docs/cli.md +8 -0
- package/docs/dev-alpha-candidate-patrol.md +8 -0
- package/docs/dev-delivery-qualification-landing-adr.md +251 -0
- package/docs/dev-delivery-warrant.md +361 -30
- package/docs/lifecycle-protocol.md +41 -0
- package/docs/next-development-transition.md +118 -0
- package/docs/node-api-reference.md +748 -322
- package/docs/release-tail-provider-plane.md +30 -0
- package/docs/reusable-build-surface.md +41 -0
- package/docs/runtime-train-validation.md +10 -0
- package/docs/v4-adopter-delivery.md +133 -0
- package/docs/v4-compatibility-facts.md +91 -0
- package/docs/v4-publication-rehearsal.md +130 -0
- package/docs/v4-runtime-ref-resume-authority.md +69 -0
- package/docs/v4-tail-reseal.md +73 -0
- package/docs/versioning.md +4 -2
- package/package.json +36 -6
- 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-signing.js +61 -0
- package/packages/core/buildchain-agent-manuals.js +3 -0
- package/packages/core/buildchain-compatibility-authority.js +336 -0
- package/packages/core/buildchain-compatibility-fact.js +394 -0
- package/packages/core/buildchain-compatibility-facts.json +217 -0
- package/packages/core/buildchain-compatibility-proof.js +566 -0
- package/packages/core/buildchain-config.js +150 -1
- package/packages/core/buildchain-contract.js +96 -143
- 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/buildchain-publication-authority.js +1 -0
- package/packages/core/channel-candidate.js +8 -0
- package/packages/core/channel-promotion-baseline.js +55 -0
- package/packages/core/ci-lane-change-budget.js +247 -0
- package/packages/core/dev-alpha-candidate-selection.js +10 -2
- package/packages/core/dev-delivery-authority-candidate.js +270 -0
- package/packages/core/dev-delivery-authority-evidence.js +146 -0
- package/packages/core/dev-delivery-authority-landing.js +461 -0
- package/packages/core/dev-delivery-authority-observation.js +48 -0
- package/packages/core/dev-delivery-authority-qualification.js +591 -0
- package/packages/core/dev-delivery-authority-settlement.js +213 -0
- package/packages/core/dev-delivery-authority-state.js +583 -0
- package/packages/core/dev-delivery-candidate-identity.js +13 -0
- package/packages/core/dev-delivery-contract-surface.js +76 -0
- package/packages/core/dev-delivery-execution-failure.js +133 -0
- package/packages/core/dev-delivery-execution-transfer.js +592 -0
- package/packages/core/dev-delivery-landing-admission-core.js +119 -0
- package/packages/core/dev-delivery-landing-readback.js +598 -0
- package/packages/core/dev-delivery-landing-terminal-evidence.js +271 -0
- package/packages/core/dev-delivery-landing-testing-port.js +6 -0
- package/packages/core/dev-delivery-native-execution.js +110 -0
- package/packages/core/dev-delivery-native-proof.js +562 -0
- package/packages/core/dev-delivery-process-boundary.js +563 -0
- package/packages/core/dev-delivery-proof.js +37 -3
- package/packages/core/dev-delivery-provider-attempt.js +127 -0
- package/packages/core/dev-delivery-provider-heartbeat.js +382 -0
- package/packages/core/dev-delivery-warrant-cancellation.js +1 -0
- package/packages/core/dev-delivery-warrant-qualification.js +145 -0
- package/packages/core/dev-delivery-warrant-settlement.js +237 -36
- package/packages/core/dev-delivery-warrant-state.js +587 -0
- package/packages/core/dev-delivery-warrant.js +328 -370
- package/packages/core/github-governance-authority.js +1 -0
- package/packages/core/index.js +7 -0
- package/packages/core/kungfu-temporal-fact.js +557 -0
- 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 +728 -0
- package/packages/core/next-development-projection.js +287 -0
- package/packages/core/next-development-transition.js +738 -0
- package/packages/core/paper-agent-entry.js +11 -5
- package/packages/core/paper.js +16 -5
- package/packages/core/publication-authority.js +5 -5
- package/packages/core/published-delivery-authority.js +266 -0
- package/packages/core/release-candidate-recovery.js +66 -11
- package/packages/core/release-candidate.js +79 -19
- package/packages/core/release-passport.js +271 -38
- package/packages/core/v4-adopter-delivery-parity.js +3 -3
- package/packages/core/v4-adopter-delivery.js +359 -0
- package/packages/core/v4-canonical-contracts.js +5 -0
- package/packages/core/v4-delivery-warrant-read-candidate.js +10 -3
- package/packages/core/v4-floating-consumer-evidence.js +338 -0
- package/packages/core/v4-floating-consumer-policy.js +569 -0
- package/packages/core/v4-floating-consumer-release-passport.js +153 -0
- package/packages/core/v4-publication-rehearsal-capsule.js +468 -0
- package/packages/core/v4-publication-rehearsal-provider-bindings.js +212 -0
- package/packages/core/v4-publication-rehearsal.js +481 -0
- package/packages/core/v4-runtime-ref-resume-authority.js +625 -0
- package/packages/core/v4-runtime-selector-persistence.js +228 -0
- package/packages/core/v4-tail-reseal-contract.js +26 -0
- package/packages/core/v4-tail-reseal-github.js +178 -0
- package/packages/core/v4-tail-reseal-receipt.js +225 -0
- package/packages/core/v4-tail-reseal.js +594 -0
- package/packages/core/workflow-yaml-contract.js +72 -2
- package/scripts/audit-publication-control-plane.mjs +31 -31
- package/scripts/buildchain-cli-help.mjs +41 -2
- package/scripts/check-ci-lane-change-budget.mjs +100 -0
- package/scripts/check-inventory.mjs +14 -3
- package/scripts/check-v4-floating-consumer-policy-contract.mjs +217 -0
- package/scripts/check-v4-public-dogfood-contract.mjs +6 -11
- package/scripts/dev-alpha-candidate-patrol.mjs +22 -1
- package/scripts/dev-delivery-authority-command-adapters.mjs +206 -0
- package/scripts/dev-delivery-authority-provider.mjs +28 -0
- package/scripts/dev-delivery-authority.mjs +490 -0
- package/scripts/dev-delivery-native-run.mjs +177 -0
- package/scripts/dev-delivery-process-boundary.mjs +260 -0
- package/scripts/dev-delivery-proof.mjs +67 -2
- package/scripts/dev-delivery-provider-heartbeat.mjs +215 -0
- package/scripts/dev-delivery-source-proof-replay.mjs +153 -0
- package/scripts/dev-delivery-source-proof-reuse.mjs +547 -0
- package/scripts/dev-delivery-two-phase-resume.mjs +345 -0
- package/scripts/dev-delivery-two-phase.mjs +573 -0
- package/scripts/dev-delivery-warrant-options.mjs +266 -0
- package/scripts/dev-delivery-warrant-store.mjs +231 -0
- package/scripts/dev-delivery-warrant.mjs +232 -194
- package/scripts/dev-pr-auto-merge.mjs +37 -48
- package/scripts/dev-pr-delivery-warrant.mjs +216 -2
- package/scripts/dev-pr-prequeue-guard.mjs +399 -0
- package/scripts/dev-qualification-patrol.mjs +9 -9
- package/scripts/dispatch-artifact-signing-authority.mjs +1 -1
- package/scripts/ensure-github-release.mjs +3 -3
- package/scripts/generate-channel-build-workflow.mjs +3 -0
- package/scripts/generate-channel-promotion-workflow.mjs +114 -14
- package/scripts/generate-next-development-guidance.mjs +49 -0
- package/scripts/generate-release-candidate-passport.mjs +192 -32
- package/scripts/generate-site-bundle.mjs +69 -4
- package/scripts/init-repo.mjs +26 -4
- package/scripts/inspect-artifact-signing-requests.mjs +6 -0
- package/scripts/next-development-self-dogfood-harness.mjs +460 -0
- package/scripts/next-development-self-dogfood.mjs +597 -0
- package/scripts/next-development-transition.mjs +47 -0
- package/scripts/npm-publish-transaction.mjs +103 -91
- package/scripts/promotion-channel-router.mjs +4 -4
- package/scripts/release-candidate-anchor-provenance.mjs +99 -0
- package/scripts/release-tail.mjs +51 -3
- package/scripts/release-train-self-dogfood.mjs +94 -92
- package/scripts/resume-from-candidate-run.mjs +565 -41
- package/scripts/seal-artifact-signing-requests.mjs +6 -0
- package/scripts/site-capability-metadata.mjs +43 -0
- package/scripts/stable-candidate-qualification.mjs +9 -10
- package/scripts/v4-consumer-policy.mjs +231 -0
- package/scripts/v4-publication-rehearsal-fixture.mjs +144 -0
- package/scripts/v4-tail-reseal-macos-rehearsal.mjs +134 -0
- package/scripts/v4-tail-reseal.mjs +416 -0
- package/templates/native-dev-delivery.yml +143 -0
|
@@ -16,12 +16,21 @@ function flag(name, fallback = "") {
|
|
|
16
16
|
return index === -1 ? fallback : String(process.argv[index + 1] || "");
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
function commandJson(command, args, label) {
|
|
20
|
-
const
|
|
19
|
+
function commandJson(command, args, label, { publicReadFallback = false } = {}) {
|
|
20
|
+
const options = {
|
|
21
21
|
encoding: "utf8",
|
|
22
22
|
timeout: 60_000,
|
|
23
23
|
maxBuffer: GITHUB_JSON_MAX_BUFFER,
|
|
24
|
-
}
|
|
24
|
+
};
|
|
25
|
+
let result = spawnSyncCommand(command, args, options);
|
|
26
|
+
const fallbackToken = String(process.env.BUILDCHAIN_GITHUB_PUBLIC_READ_TOKEN || "");
|
|
27
|
+
const primaryToken = String(process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "");
|
|
28
|
+
if (result.status !== 0 && publicReadFallback && fallbackToken && fallbackToken !== primaryToken) {
|
|
29
|
+
result = spawnSyncCommand(command, args, {
|
|
30
|
+
...options,
|
|
31
|
+
env: { ...process.env, GH_TOKEN: fallbackToken, GITHUB_TOKEN: fallbackToken },
|
|
32
|
+
});
|
|
33
|
+
}
|
|
25
34
|
if (result.status !== 0) {
|
|
26
35
|
const category = /401|E401|unauthorized/i.test(result.stderr) ? "unauthorized" : "unavailable";
|
|
27
36
|
throw new Error(`${label} is ${category}; publication control-plane audit fails closed`);
|
|
@@ -33,18 +42,22 @@ function commandJson(command, args, label) {
|
|
|
33
42
|
}
|
|
34
43
|
}
|
|
35
44
|
|
|
36
|
-
function githubJson(apiPath, label) {
|
|
37
|
-
return commandJson("gh", ["api", apiPath, "-H", "Accept: application/vnd.github+json"], label);
|
|
45
|
+
function githubJson(apiPath, label, { publicReadFallback = false } = {}) {
|
|
46
|
+
return commandJson("gh", ["api", apiPath, "-H", "Accept: application/vnd.github+json"], label, { publicReadFallback });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function githubPublicJson(apiPath, label) {
|
|
50
|
+
return githubJson(apiPath, label, { publicReadFallback: true });
|
|
38
51
|
}
|
|
39
52
|
|
|
40
|
-
function githubJsonOptional(apiPath, label, fallback) {
|
|
53
|
+
function githubJsonOptional(apiPath, label, fallback, fallbackPattern = /404|not found/i) {
|
|
41
54
|
const result = spawnSyncCommand("gh", ["api", apiPath, "-H", "Accept: application/vnd.github+json"], {
|
|
42
55
|
encoding: "utf8",
|
|
43
56
|
timeout: 60_000,
|
|
44
57
|
maxBuffer: GITHUB_JSON_MAX_BUFFER,
|
|
45
58
|
});
|
|
46
59
|
if (result.status !== 0) {
|
|
47
|
-
if (
|
|
60
|
+
if (fallbackPattern.test(`${result.stdout}\n${result.stderr}`)) return fallback;
|
|
48
61
|
const category = /401|403|unauthorized|forbidden/i.test(`${result.stdout}\n${result.stderr}`) ? "unauthorized" : "unavailable";
|
|
49
62
|
throw new Error(`${label} is ${category}; publication control-plane audit fails closed`);
|
|
50
63
|
}
|
|
@@ -56,20 +69,7 @@ function githubJsonOptional(apiPath, label, fallback) {
|
|
|
56
69
|
}
|
|
57
70
|
|
|
58
71
|
function githubJsonReadLimited(apiPath, label, fallback) {
|
|
59
|
-
|
|
60
|
-
encoding: "utf8",
|
|
61
|
-
timeout: 60_000,
|
|
62
|
-
maxBuffer: GITHUB_JSON_MAX_BUFFER,
|
|
63
|
-
});
|
|
64
|
-
if (result.status !== 0) {
|
|
65
|
-
if (/401|403|404|unauthorized|forbidden|not found/i.test(`${result.stdout}\n${result.stderr}`)) return fallback;
|
|
66
|
-
throw new Error(`${label} is unavailable; publication control-plane audit fails closed`);
|
|
67
|
-
}
|
|
68
|
-
try {
|
|
69
|
-
return JSON.parse(result.stdout);
|
|
70
|
-
} catch {
|
|
71
|
-
throw new Error(`${label} did not return JSON; publication control-plane audit fails closed`);
|
|
72
|
-
}
|
|
72
|
+
return githubJsonOptional(apiPath, label, fallback, /401|403|404|unauthorized|forbidden|not found/i);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
function rulesetIncludesBranch(ruleset, branch, defaultBranch) {
|
|
@@ -218,7 +218,7 @@ function main() {
|
|
|
218
218
|
if (!repository || !branch) throw new Error("--repository and --branch are required");
|
|
219
219
|
|
|
220
220
|
const encodedWorkflow = workflowPath.split("/").map(encodeURIComponent).join("/");
|
|
221
|
-
const workflowFile =
|
|
221
|
+
const workflowFile = githubPublicJson(
|
|
222
222
|
`repos/${workflowRepository}/contents/${encodedWorkflow}${workflowRef ? `?ref=${encodeURIComponent(workflowRef)}` : ""}`,
|
|
223
223
|
"publication workflow source",
|
|
224
224
|
);
|
|
@@ -231,8 +231,8 @@ function main() {
|
|
|
231
231
|
!/^\s*[a-z-]+:\s*write\s*$/m.test(workflowHeader) &&
|
|
232
232
|
!/permissions\s*:\s*write-all/i.test(workflowHeader);
|
|
233
233
|
|
|
234
|
-
const repositoryState =
|
|
235
|
-
const branchState =
|
|
234
|
+
const repositoryState = githubPublicJson(`repos/${repository}`, "repository metadata");
|
|
235
|
+
const branchState = githubPublicJson(`repos/${repository}/branches/${encodeURIComponent(branch)}`, "branch summary");
|
|
236
236
|
const exactTransactionSource = /^[0-9a-f]{40}$/.test(sourceSha);
|
|
237
237
|
const protection = exactTransactionSource
|
|
238
238
|
? null
|
|
@@ -290,7 +290,7 @@ function main() {
|
|
|
290
290
|
if (!/^[0-9a-f]{40}$/.test(sourceSha)) {
|
|
291
291
|
throw new Error("--source-sha is required when detailed branch policy is not readable");
|
|
292
292
|
}
|
|
293
|
-
const sourceCommit =
|
|
293
|
+
const sourceCommit = githubPublicJson(`repos/${repository}/commits/${sourceSha}`, "source commit");
|
|
294
294
|
const sourceParents = Array.isArray(sourceCommit.parents) ? sourceCommit.parents.map((entry) => String(entry?.sha || "").toLowerCase()) : [];
|
|
295
295
|
const sourceChangedPaths = Array.isArray(sourceCommit.files) ? sourceCommit.files.map((entry) => String(entry?.filename || "")) : [];
|
|
296
296
|
const sourceMessage = String(sourceCommit.commit?.message || "").split("\n", 1)[0];
|
|
@@ -305,7 +305,7 @@ function main() {
|
|
|
305
305
|
const branchHeadSha = String(branchState.commit?.sha || "").toLowerCase();
|
|
306
306
|
const sourceComparison = sourceSha === branchHeadSha
|
|
307
307
|
? null
|
|
308
|
-
:
|
|
308
|
+
: githubPublicJson(
|
|
309
309
|
`repos/${repository}/compare/${sourceSha}...${branchHeadSha}`,
|
|
310
310
|
"source protected-branch lineage",
|
|
311
311
|
);
|
|
@@ -313,7 +313,7 @@ function main() {
|
|
|
313
313
|
sourceComparison?.status === "ahead" &&
|
|
314
314
|
String(sourceComparison?.merge_base_commit?.sha || "").toLowerCase() === sourceSha
|
|
315
315
|
);
|
|
316
|
-
let pullRequests =
|
|
316
|
+
let pullRequests = githubPublicJson(`repos/${repository}/commits/${authorizationSha}/pulls`, "source pull-request lineage");
|
|
317
317
|
let mergedPullRequest = (Array.isArray(pullRequests) ? pullRequests : []).find((entry) =>
|
|
318
318
|
entry?.merged_at &&
|
|
319
319
|
(
|
|
@@ -324,7 +324,7 @@ function main() {
|
|
|
324
324
|
entry.head?.repo?.full_name === repository
|
|
325
325
|
);
|
|
326
326
|
if (!mergedPullRequest && allowReleaseReconciliation) {
|
|
327
|
-
const packageFile =
|
|
327
|
+
const packageFile = githubPublicJson(
|
|
328
328
|
`repos/${repository}/contents/package.json?ref=${encodeURIComponent(sourceSha)}`,
|
|
329
329
|
"release reconciliation package metadata",
|
|
330
330
|
);
|
|
@@ -340,7 +340,7 @@ function main() {
|
|
|
340
340
|
});
|
|
341
341
|
if (releaseReconciliation.qualifying) {
|
|
342
342
|
authorizationSha = parentSha;
|
|
343
|
-
pullRequests =
|
|
343
|
+
pullRequests = githubPublicJson(`repos/${repository}/commits/${authorizationSha}/pulls`, "release parent pull-request lineage");
|
|
344
344
|
mergedPullRequest = (Array.isArray(pullRequests) ? pullRequests : []).find((entry) =>
|
|
345
345
|
entry?.merged_at &&
|
|
346
346
|
entry.merge_commit_sha === authorizationSha &&
|
|
@@ -350,7 +350,7 @@ function main() {
|
|
|
350
350
|
}
|
|
351
351
|
}
|
|
352
352
|
const reviews = mergedPullRequest
|
|
353
|
-
?
|
|
353
|
+
? githubPublicJson(`repos/${repository}/pulls/${mergedPullRequest.number}/reviews?per_page=100`, "source pull-request reviews")
|
|
354
354
|
: [];
|
|
355
355
|
const latestReviews = new Map();
|
|
356
356
|
for (const review of Array.isArray(reviews) ? reviews : []) {
|
|
@@ -378,7 +378,7 @@ function main() {
|
|
|
378
378
|
(prefixedRequiredStatusChecks.length === 1 ? prefixedRequiredStatusChecks[0] : requiredStatusCheck);
|
|
379
379
|
const requiredStatusCheckMatchCount = exactRequiredStatusCheck ? 1 : prefixedRequiredStatusChecks.length;
|
|
380
380
|
const checkRuns = /^[0-9a-f]{40}$/.test(pullRequestHeadSha)
|
|
381
|
-
?
|
|
381
|
+
? githubPublicJson(`repos/${repository}/commits/${pullRequestHeadSha}/check-runs?check_name=${encodeURIComponent(resolvedRequiredStatusCheck)}&filter=latest&per_page=100`, "merged pull-request required check runs")
|
|
382
382
|
: { check_runs: [] };
|
|
383
383
|
const requiredCheckSource = (requiredStatusCheckPolicy.checks || []).find((entry) =>
|
|
384
384
|
entry.context === resolvedRequiredStatusCheck
|
|
@@ -8,6 +8,11 @@ export const BUILDCHAIN_USAGE = `Usage:
|
|
|
8
8
|
buildchain architecture qualify --authority-revision <git-revision>
|
|
9
9
|
[--candidate-revision <git-revision>]
|
|
10
10
|
[--cwd <dir>] [--json]
|
|
11
|
+
buildchain adopter-delivery run --input <json-or-path> [--output <path>]
|
|
12
|
+
buildchain adopter-delivery verify --input <json-or-path>
|
|
13
|
+
--readback <json-or-path> [--output <path>]
|
|
14
|
+
buildchain adopter-delivery bootstrap --input <json-or-path> [--output <path>]
|
|
15
|
+
buildchain adopter-delivery archive --input <json-or-path> [--output <path>]
|
|
11
16
|
buildchain portable-cache plan --manifest <file-or-json> [--output <file>]
|
|
12
17
|
[--github-output <file>] [--json]
|
|
13
18
|
buildchain portable-cache receipt --plan <file-or-json> [--matched-key <key>]
|
|
@@ -44,6 +49,23 @@ export const BUILDCHAIN_USAGE = `Usage:
|
|
|
44
49
|
buildchain release-tail init --declaration <json-or-path> [--state <path>]
|
|
45
50
|
buildchain release-tail <status|verify> [--state <path>] [--output <path>]
|
|
46
51
|
buildchain release-tail compat --hooks-json <json-or-path> [--output <path>]
|
|
52
|
+
buildchain release-tail rehearse --capsule <absolute-path>
|
|
53
|
+
--candidate-root <absolute-path>
|
|
54
|
+
--mode <simulate|replay>
|
|
55
|
+
--state <absolute-path>
|
|
56
|
+
--evidence <absolute-path>
|
|
57
|
+
buildchain tail-reseal plan --request <request.json> [--output <plan.json>]
|
|
58
|
+
buildchain tail-reseal admit --request <request.json> [--output <admission.json>]
|
|
59
|
+
buildchain tail-reseal verify-platform --request <request.json> --platform <id>
|
|
60
|
+
[--artifact-root <dir>] [--mode retained|resealed]
|
|
61
|
+
[--provider-readback-root <sha256:...>]
|
|
62
|
+
[--output <readback.json>]
|
|
63
|
+
buildchain tail-reseal seal --request <request.json> --plan <plan.json>
|
|
64
|
+
--readbacks <dir> --passport <passport.json>
|
|
65
|
+
--protected-readback-root <sha256:...>
|
|
66
|
+
[--output <receipt.json>]
|
|
67
|
+
buildchain next-development materialize --input <request.json> [--cwd <dir>]
|
|
68
|
+
[--write] [--output <path>] [--json]
|
|
47
69
|
buildchain github-governance <plan|apply|rollback|protection-policy-plan|ruleset-policy-plan> ...
|
|
48
70
|
buildchain release <inspect|recover|finalize|abort> ...
|
|
49
71
|
buildchain transaction inspect ...
|
|
@@ -127,11 +149,20 @@ export const BUILDCHAIN_USAGE = `Usage:
|
|
|
127
149
|
[--from-config | --workflow <required-workflow.yml>...] [--cwd <dir>]
|
|
128
150
|
[--check-response-timeout-minutes <n>]
|
|
129
151
|
[--max-entries-to-build <n>] [--apply]
|
|
130
|
-
buildchain dev warrant <submit|select|heartbeat|recover|close|cancel-queued|observe>
|
|
152
|
+
buildchain dev warrant <submit|select|heartbeat|qualify|recover|close|settle|cancel-queued|observe>
|
|
131
153
|
--repository <owner/repo> --branch <dev/vN/vN.M>
|
|
132
154
|
[--execute] [--output <file>] [--json]
|
|
133
|
-
buildchain dev
|
|
155
|
+
buildchain dev authority <migrate|submit|lease-qualification|heartbeat-qualification|complete-qualification|lease-landing|heartbeat-landing|recover|admit-merge-group|settle|observe>
|
|
156
|
+
--repository <owner/repo> --branch <dev/vN/vN.M>
|
|
157
|
+
[--environment-root <root>] [--qualification-domains <json>]
|
|
158
|
+
[--provider-attempt <admitted-attempt.json>]
|
|
159
|
+
[--execute] [--output <file>] [--json]
|
|
160
|
+
buildchain dev proof <source|verify-source|classify|native|verify-native|classify-native|verify-native-reuse|replay|integration|verify-integration>
|
|
134
161
|
[--output <file>] [--json]
|
|
162
|
+
buildchain dev two-phase --repository <owner/repo> --branch <dev/vN/vN.M>
|
|
163
|
+
--pull-request <n> --expected-head <sha>
|
|
164
|
+
--warrant-result <file> --native-command <command>
|
|
165
|
+
[--native-proof <file>] [--output <file>] [--json]
|
|
135
166
|
buildchain log <info|warn|error> --event <name> [--phase <phase>]
|
|
136
167
|
[--component <name>] [--source <name>] [--attribute key=value]...
|
|
137
168
|
[--path <jsonl>] [--json]
|
|
@@ -145,6 +176,14 @@ export const BUILDCHAIN_USAGE = `Usage:
|
|
|
145
176
|
[--module-fact <file>]... [--artifact <path>]...
|
|
146
177
|
[--output <file>] [--json]
|
|
147
178
|
buildchain facts verify [--cwd <dir>] --fact <file> [--json]
|
|
179
|
+
buildchain facts compatibility project [--cwd <dir>] [--registry <json-or-path>]
|
|
180
|
+
[--output <file>] [--json]
|
|
181
|
+
buildchain facts compatibility verify [--cwd <dir>] [--registry <json-or-path>] [--json]
|
|
182
|
+
buildchain facts compatibility query --query <json-or-path>
|
|
183
|
+
[--registry <json-or-path>] [--cwd <dir>] [--json]
|
|
184
|
+
buildchain facts compatibility query-template --fact-root <sha256:...>
|
|
185
|
+
[--query-id <id>] [--registry <json-or-path>]
|
|
186
|
+
[--cwd <dir>] [--json]
|
|
148
187
|
buildchain kfd ...
|
|
149
188
|
buildchain kfd hub <init|inspect|test|explain> [--cwd <dir>]
|
|
150
189
|
[--declaration <path>] [--output-dir <path>]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
|
+
import { evaluateCiLaneChangeBudget } from "../packages/core/ci-lane-change-budget.js";
|
|
8
|
+
|
|
9
|
+
const root = process.cwd();
|
|
10
|
+
const workflowRoot = path.join(root, ".github", "workflows");
|
|
11
|
+
const policyPath = path.join(
|
|
12
|
+
root,
|
|
13
|
+
"architecture",
|
|
14
|
+
"ci-lane-change-budget.json",
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const workflows = fs
|
|
18
|
+
.readdirSync(workflowRoot, { withFileTypes: true })
|
|
19
|
+
.filter((entry) => entry.isFile() && /\.(?:yaml|yml)$/u.test(entry.name))
|
|
20
|
+
.map((entry) => {
|
|
21
|
+
const workflowPath = path.posix.join(".github/workflows", entry.name);
|
|
22
|
+
return {
|
|
23
|
+
path: workflowPath,
|
|
24
|
+
text: fs.readFileSync(path.join(workflowRoot, entry.name), "utf8"),
|
|
25
|
+
};
|
|
26
|
+
})
|
|
27
|
+
.sort((left, right) => left.path.localeCompare(right.path));
|
|
28
|
+
|
|
29
|
+
const policy = JSON.parse(fs.readFileSync(policyPath, "utf8"));
|
|
30
|
+
const baselineRevision = String(policy.baseline?.gitRevision || "").trim();
|
|
31
|
+
if (!/^[0-9a-f]{40}$/u.test(baselineRevision)) {
|
|
32
|
+
throw new Error("baseline.gitRevision must be an exact Git commit");
|
|
33
|
+
}
|
|
34
|
+
const embeddedBaselineLanes = policy.baseline?.lanes;
|
|
35
|
+
if (
|
|
36
|
+
!Array.isArray(embeddedBaselineLanes) ||
|
|
37
|
+
embeddedBaselineLanes.length === 0
|
|
38
|
+
) {
|
|
39
|
+
throw new Error("baseline.lanes must contain the exact offline lane set");
|
|
40
|
+
}
|
|
41
|
+
const embeddedLaneSetRoot = `sha256:${crypto
|
|
42
|
+
.createHash("sha256")
|
|
43
|
+
.update(`${JSON.stringify(embeddedBaselineLanes)}\n`)
|
|
44
|
+
.digest("hex")}`;
|
|
45
|
+
if (embeddedLaneSetRoot !== policy.baseline?.laneSetRoot) {
|
|
46
|
+
throw new Error("baseline laneSetRoot does not match baseline.lanes");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let baselineWorkflows = null;
|
|
50
|
+
try {
|
|
51
|
+
execFileSync("git", ["cat-file", "-e", `${baselineRevision}^{commit}`], {
|
|
52
|
+
cwd: root,
|
|
53
|
+
stdio: "ignore",
|
|
54
|
+
});
|
|
55
|
+
const baselinePaths = execFileSync(
|
|
56
|
+
"git",
|
|
57
|
+
[
|
|
58
|
+
"ls-tree",
|
|
59
|
+
"-r",
|
|
60
|
+
"--name-only",
|
|
61
|
+
baselineRevision,
|
|
62
|
+
"--",
|
|
63
|
+
".github/workflows",
|
|
64
|
+
],
|
|
65
|
+
{ cwd: root, encoding: "utf8" },
|
|
66
|
+
)
|
|
67
|
+
.split(/\r?\n/u)
|
|
68
|
+
.filter((value) => /\.(?:yaml|yml)$/u.test(value))
|
|
69
|
+
.sort();
|
|
70
|
+
baselineWorkflows = baselinePaths.map((workflowPath) => ({
|
|
71
|
+
path: workflowPath,
|
|
72
|
+
text: execFileSync("git", ["show", `${baselineRevision}:${workflowPath}`], {
|
|
73
|
+
cwd: root,
|
|
74
|
+
encoding: "utf8",
|
|
75
|
+
}),
|
|
76
|
+
}));
|
|
77
|
+
} catch {
|
|
78
|
+
// Shallow and exported source cuts may not contain the pinned commit object.
|
|
79
|
+
// The content-rooted embedded lane set remains the exact offline authority.
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const embeddedEvaluation = evaluateCiLaneChangeBudget({
|
|
83
|
+
policy,
|
|
84
|
+
workflows,
|
|
85
|
+
});
|
|
86
|
+
const evaluation = baselineWorkflows
|
|
87
|
+
? evaluateCiLaneChangeBudget({ policy, workflows, baselineWorkflows })
|
|
88
|
+
: embeddedEvaluation;
|
|
89
|
+
if (
|
|
90
|
+
baselineWorkflows &&
|
|
91
|
+
(evaluation.baselineLaneCount !== embeddedEvaluation.baselineLaneCount ||
|
|
92
|
+
JSON.stringify(evaluation.newLanes) !==
|
|
93
|
+
JSON.stringify(embeddedEvaluation.newLanes))
|
|
94
|
+
) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
"embedded baseline lanes do not match the pinned baseline revision",
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
process.stdout.write(`${JSON.stringify(evaluation, null, 2)}\n`);
|
|
100
|
+
if (!evaluation.ok) process.exitCode = 1;
|
|
@@ -54,6 +54,10 @@ const requiredPaths = [
|
|
|
54
54
|
"packages/core/homebrew.js",
|
|
55
55
|
"packages/core/artifact-verification-envelope.js",
|
|
56
56
|
"packages/core/anchored-version-material.js",
|
|
57
|
+
"packages/core/next-development-transition.js",
|
|
58
|
+
"packages/core/next-development-controller.js",
|
|
59
|
+
"packages/core/next-development-candidate-reservation.js",
|
|
60
|
+
"packages/core/next-development-projection.js",
|
|
57
61
|
"packages/core/build-facts.js",
|
|
58
62
|
"packages/core/publication-package.js",
|
|
59
63
|
"packages/core/publication-reproducibility.js",
|
|
@@ -76,6 +80,8 @@ const requiredPaths = [
|
|
|
76
80
|
"docs/auditable-demo.md",
|
|
77
81
|
"contracts/auditable-demo-scenario-v1.schema.json",
|
|
78
82
|
"contracts/release-candidate-recovery-v1.schema.json",
|
|
83
|
+
"contracts/next-development-request-v1.schema.json",
|
|
84
|
+
"contracts/next-development-transition-v1.schema.json",
|
|
79
85
|
"contracts/auditable-demo-media-profiles-v1.json",
|
|
80
86
|
"contracts/evidence/auditable-demo-web-delivery-v1.json",
|
|
81
87
|
"contracts/evidence/auditable-demo-responsive-web-delivery-v1.json",
|
|
@@ -87,10 +93,15 @@ const requiredPaths = [
|
|
|
87
93
|
"contracts/fixtures/auditable-demo-responsive-web-delivery-v1/public-projection.json",
|
|
88
94
|
"contracts/fixtures/auditable-demo-responsive-web-delivery-v1/scene.json",
|
|
89
95
|
"docs/release-propagation.md",
|
|
96
|
+
"docs/next-development-transition.md",
|
|
90
97
|
"docs/site-bundle-contract.md",
|
|
91
98
|
"docs/toolkit-observability.md",
|
|
92
99
|
"docs/versioning.md",
|
|
93
100
|
"scripts/release-line-dry-run.mjs",
|
|
101
|
+
"scripts/next-development-transition.mjs",
|
|
102
|
+
"scripts/next-development-self-dogfood.mjs",
|
|
103
|
+
"scripts/next-development-self-dogfood-harness.mjs",
|
|
104
|
+
"scripts/generate-next-development-guidance.mjs",
|
|
94
105
|
"scripts/reconcile-release-governance.mjs",
|
|
95
106
|
"scripts/buildchain-channel-router.mjs",
|
|
96
107
|
"scripts/generate-channel-build-workflow.mjs",
|
|
@@ -1010,16 +1021,16 @@ for (const forbiddenSnippet of [
|
|
|
1010
1021
|
for (const requiredSnippet of [
|
|
1011
1022
|
"id-token: write",
|
|
1012
1023
|
"actions: write",
|
|
1013
|
-
"uses:
|
|
1024
|
+
"uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v4-alpha",
|
|
1014
1025
|
"github.event.workflow_run.event == 'push'",
|
|
1015
1026
|
"!startsWith(github.event.workflow_run.display_title, 'chore(release): prepare v')",
|
|
1016
1027
|
"!startsWith(github.event.workflow_run.display_title, 'chore(release): release v')",
|
|
1017
1028
|
"resume-candidate-run-id:",
|
|
1018
1029
|
"resume-expected-source-tree:",
|
|
1019
1030
|
"resume-buildchain-runtime-sha:",
|
|
1020
|
-
"github-
|
|
1031
|
+
"uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v4",
|
|
1021
1032
|
"release-passport-buildchain-self-kfd: true",
|
|
1022
|
-
|
|
1033
|
+
"buildchain-channel: alpha",
|
|
1023
1034
|
"release-passport-impact-json: .buildchain/release-impact.json",
|
|
1024
1035
|
]) {
|
|
1025
1036
|
if (!buildchainRefPromotionWorkflow.includes(requiredSnippet)) {
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { parseYamlUses } from "../packages/core/workflow-yaml-contract.js";
|
|
6
|
+
|
|
7
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
8
|
+
const read = (relative) => fs.readFileSync(path.join(root, relative), "utf8");
|
|
9
|
+
function fail(message) {
|
|
10
|
+
throw new Error(`v4-floating-consumer-policy-contract: ${message}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function workflowJobBlock(source, job) {
|
|
14
|
+
return (
|
|
15
|
+
source.match(
|
|
16
|
+
new RegExp(
|
|
17
|
+
`^ ${job}:\\n([\\s\\S]*?)(?=^ [a-z0-9_-]+:|(?![\\s\\S]))`,
|
|
18
|
+
"mu",
|
|
19
|
+
),
|
|
20
|
+
)?.[1] || ""
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function assertTrustGatedJobs(source, jobs) {
|
|
25
|
+
for (const job of jobs) {
|
|
26
|
+
const block = workflowJobBlock(source, job);
|
|
27
|
+
if (!block.includes("- trust-gate")) {
|
|
28
|
+
fail(`.build.yml job ${job} is not directly gated by trust-gate`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function jobDependsOn(source, job, dependency) {
|
|
34
|
+
const block = workflowJobBlock(source, job);
|
|
35
|
+
return new RegExp(
|
|
36
|
+
`^ needs:\\s*(?:${dependency}|\\[[^\\]]*\\b${dependency}\\b[^\\]]*\\])\\s*$`,
|
|
37
|
+
"mu",
|
|
38
|
+
).test(block);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function assertOrdered(relative, markers) {
|
|
42
|
+
const source = read(relative);
|
|
43
|
+
let cursor = -1;
|
|
44
|
+
for (const marker of markers) {
|
|
45
|
+
const next = source.indexOf(marker, cursor + 1);
|
|
46
|
+
if (next < 0) fail(`${relative} is missing ${marker}`);
|
|
47
|
+
if (next <= cursor)
|
|
48
|
+
fail(`${relative} does not enforce ${markers.join(" -> ")}`);
|
|
49
|
+
cursor = next;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function assertPromotionCertificationWiring(source) {
|
|
54
|
+
for (const marker of [
|
|
55
|
+
"path: .buildchain/runtime/candidate-policy-runtime",
|
|
56
|
+
"BUILDCHAIN_EXPECTED_RUNTIME_SHA: ${{ steps.v4-policy-caller.outputs.runtime-sha }}",
|
|
57
|
+
"policy_runtime=.buildchain/runtime",
|
|
58
|
+
'node "${policy_runtime}/scripts/v4-consumer-policy.mjs" certify',
|
|
59
|
+
"passport.consumerPolicy?.receipt?.caller?.sourceSha",
|
|
60
|
+
"path: .buildchain/runtime/policy-caller",
|
|
61
|
+
'--caller-root "${{ github.workspace }}/.buildchain/runtime/policy-caller"',
|
|
62
|
+
'--stable-lock "${{ inputs.buildchain-stable-contract-lock-path }}"',
|
|
63
|
+
'--alpha-lock "${{ inputs.buildchain-alpha-contract-lock-path }}"',
|
|
64
|
+
"release-passport-v4-consumer-policy-certification-root: ${{ steps.v4-policy-certification.outputs.v4-consumer-policy-certification-root }}",
|
|
65
|
+
]) {
|
|
66
|
+
if (!source.includes(marker)) {
|
|
67
|
+
fail(`promotion certification is missing ${marker}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function assertPersistedSelectors() {
|
|
73
|
+
const offenders = [];
|
|
74
|
+
const workflows = fs
|
|
75
|
+
.readdirSync(path.join(root, ".github/workflows"))
|
|
76
|
+
.filter((name) => /\.ya?ml$/u.test(name))
|
|
77
|
+
.sort();
|
|
78
|
+
for (const name of workflows) {
|
|
79
|
+
const relative = `.github/workflows/${name}`;
|
|
80
|
+
for (const node of parseYamlUses(read(relative))) {
|
|
81
|
+
const match = node.value.match(
|
|
82
|
+
/^kungfu-systems\/buildchain\/(.+)@(.+)$/u,
|
|
83
|
+
);
|
|
84
|
+
if (!match) continue;
|
|
85
|
+
const selector = match[2];
|
|
86
|
+
const protectedBootstrap =
|
|
87
|
+
relative ===
|
|
88
|
+
".github/workflows/buildchain-ref-promotion-recovery.yml" &&
|
|
89
|
+
match[1] === ".github/workflows/.release-candidate-promote.yml" &&
|
|
90
|
+
selector === "alpha/v4/v4.0";
|
|
91
|
+
const isV4 =
|
|
92
|
+
selector === "v4" ||
|
|
93
|
+
selector === "v4-alpha" ||
|
|
94
|
+
/^v4(?:[./-]|$)/u.test(selector) ||
|
|
95
|
+
/^[0-9a-f]{40}$/iu.test(selector) ||
|
|
96
|
+
selector.includes("${{");
|
|
97
|
+
if (
|
|
98
|
+
(isV4 || protectedBootstrap) &&
|
|
99
|
+
!["v4", "v4-alpha"].includes(selector) &&
|
|
100
|
+
!protectedBootstrap
|
|
101
|
+
) {
|
|
102
|
+
offenders.push(`${relative}:${node.line} @${selector}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (offenders.length)
|
|
107
|
+
fail(
|
|
108
|
+
`persisted v4 selectors must be v4 or v4-alpha: ${offenders.join(", ")}`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function checkV4FloatingConsumerPolicyContract() {
|
|
113
|
+
const policy = JSON.parse(
|
|
114
|
+
read("architecture/v4-floating-consumer-policy.json"),
|
|
115
|
+
);
|
|
116
|
+
if (policy.contract !== "kungfu-buildchain-v4-floating-consumer-policy/v1") {
|
|
117
|
+
fail("architecture policy contract is missing");
|
|
118
|
+
}
|
|
119
|
+
if (
|
|
120
|
+
policy.contractLocks?.selectedLockMustBindResolvedWorkflowShell !== true
|
|
121
|
+
) {
|
|
122
|
+
fail("contract lock must bind the visible workflow shell");
|
|
123
|
+
}
|
|
124
|
+
assertPersistedSelectors();
|
|
125
|
+
assertOrdered(".github/workflows/.build.yml", [
|
|
126
|
+
"Enforce v4 floating consumer policy",
|
|
127
|
+
"Validate consumer package manager contract",
|
|
128
|
+
]);
|
|
129
|
+
if (
|
|
130
|
+
!read(".github/workflows/.build.yml").includes(
|
|
131
|
+
"BUILDCHAIN_EXPECTED_INVOCATION_CHANNEL: ${{ inputs.buildchain-expected-channel }}",
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
fail("channel builds must disambiguate dual-channel caller invocations");
|
|
135
|
+
assertTrustGatedJobs(read(".github/workflows/.build.yml"), [
|
|
136
|
+
"resolve-source",
|
|
137
|
+
"resolve-contract",
|
|
138
|
+
"controller-plan",
|
|
139
|
+
"artifact-transfer",
|
|
140
|
+
"build-native",
|
|
141
|
+
"build-linux-container",
|
|
142
|
+
"summarize",
|
|
143
|
+
]);
|
|
144
|
+
assertOrdered(".github/workflows/publication-artifact.yml", [
|
|
145
|
+
"Enforce v4 floating consumer policy",
|
|
146
|
+
"Resolve controller identities",
|
|
147
|
+
"Install Buildchain runtime dependencies",
|
|
148
|
+
]);
|
|
149
|
+
const stageCanary = read(".github/workflows/v4-stage-capsule-canary.yml");
|
|
150
|
+
if (
|
|
151
|
+
!stageCanary.includes("consumer-admission:") ||
|
|
152
|
+
!stageCanary.includes("needs: consumer-admission")
|
|
153
|
+
) {
|
|
154
|
+
fail(
|
|
155
|
+
"Stage Capsule qualification is not transitively gated by consumer admission",
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
const promotion = read(".github/workflows/release-candidate-promote.yml");
|
|
159
|
+
if (
|
|
160
|
+
!promotion.includes("consumer-admission:") ||
|
|
161
|
+
!jobDependsOn(promotion, "alpha", "consumer-admission") ||
|
|
162
|
+
!jobDependsOn(promotion, "stable", "consumer-admission")
|
|
163
|
+
) {
|
|
164
|
+
fail(
|
|
165
|
+
"release candidate promotion is not transitively gated by consumer admission",
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
assertPromotionCertificationWiring(
|
|
169
|
+
read(".github/workflows/.release-candidate-promote.yml"),
|
|
170
|
+
);
|
|
171
|
+
for (const [relative, marker] of [
|
|
172
|
+
["packages/core/release-candidate.js", "consumerPolicy"],
|
|
173
|
+
["packages/core/release-passport.js", "v4ConsumerPolicy"],
|
|
174
|
+
[
|
|
175
|
+
"actions/promote-buildchain-ref/action.yml",
|
|
176
|
+
"release-passport-v4-consumer-policy-certification-json",
|
|
177
|
+
],
|
|
178
|
+
[
|
|
179
|
+
"actions/promote-buildchain-ref/action.yml",
|
|
180
|
+
"release-passport-v4-consumer-policy-certification-root",
|
|
181
|
+
],
|
|
182
|
+
[
|
|
183
|
+
".github/workflows/.release-candidate-promote.yml",
|
|
184
|
+
"v4-policy-certification",
|
|
185
|
+
],
|
|
186
|
+
]) {
|
|
187
|
+
if (!read(relative).includes(marker)) {
|
|
188
|
+
fail(
|
|
189
|
+
`${relative} does not bind v4 consumer policy evidence marker ${marker}`,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const agents = read("AGENTS.md");
|
|
194
|
+
for (const invariant of [
|
|
195
|
+
"source-persisted exact commit SHA",
|
|
196
|
+
"matching stable and alpha contract locks",
|
|
197
|
+
"trusted non-persistent runtime input",
|
|
198
|
+
]) {
|
|
199
|
+
if (!agents.includes(invariant))
|
|
200
|
+
fail(`AGENTS.md is missing invariant: ${invariant}`);
|
|
201
|
+
}
|
|
202
|
+
return { ok: true, entrypoints: policy.scope.publicWorkflowEntrypoints };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (
|
|
206
|
+
process.argv[1] &&
|
|
207
|
+
path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
208
|
+
) {
|
|
209
|
+
try {
|
|
210
|
+
process.stdout.write(
|
|
211
|
+
`${JSON.stringify(checkV4FloatingConsumerPolicyContract(), null, 2)}\n`,
|
|
212
|
+
);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.error(error.message);
|
|
215
|
+
process.exitCode = 1;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -10,9 +10,7 @@ const DEFAULT_ROOT = path.resolve(
|
|
|
10
10
|
);
|
|
11
11
|
const CALLER_PATH = ".github/workflows/v4-public-consumer-dogfood.yml";
|
|
12
12
|
const REUSABLE_PATH = ".github/workflows/v4-stage-capsule-canary.yml";
|
|
13
|
-
export const
|
|
14
|
-
"train/v4/v4.0/public-consumer-self-dogfood";
|
|
15
|
-
const EXACT_SHA = /^[0-9a-f]{40}$/u;
|
|
13
|
+
export const V4_PUBLIC_DOGFOOD_ALPHA_REF = "v4-alpha";
|
|
16
14
|
const PRIVATE_CONSUMER = ["buildchain", "self", "dogfood"].join("-");
|
|
17
15
|
const PRIVATE_SHADOW = ["kungfu", "shadow"].join("-");
|
|
18
16
|
|
|
@@ -139,13 +137,8 @@ function assertArchitecture(root) {
|
|
|
139
137
|
);
|
|
140
138
|
const dogfood = architecture.publicConsumerDogfood;
|
|
141
139
|
const validationRef = dogfood?.validationRef;
|
|
142
|
-
if (
|
|
143
|
-
validationRef
|
|
144
|
-
!EXACT_SHA.test(String(validationRef))
|
|
145
|
-
)
|
|
146
|
-
fail(
|
|
147
|
-
"architecture validationRef must be the exact public train or a protected commit SHA",
|
|
148
|
-
);
|
|
140
|
+
if (validationRef !== V4_PUBLIC_DOGFOOD_ALPHA_REF)
|
|
141
|
+
fail("architecture validationRef must use the floating v4-alpha channel");
|
|
149
142
|
const caller = read(root, CALLER_PATH);
|
|
150
143
|
if (caller !== expectedV4PublicDogfoodWorkflow(validationRef))
|
|
151
144
|
fail(`${CALLER_PATH} must remain the exact thin public consumer caller`);
|
|
@@ -168,7 +161,7 @@ function assertArchitecture(root) {
|
|
|
168
161
|
dogfood.relativeOrSelfInvocationAllowed !== false ||
|
|
169
162
|
dogfood.directQualificationInvocationAllowed !== false ||
|
|
170
163
|
dogfood.candidateBranchOverrideAllowed !== false ||
|
|
171
|
-
dogfood.recursionRecovery !== "
|
|
164
|
+
dogfood.recursionRecovery !== "floating-selector-with-trusted-runtime-input"
|
|
172
165
|
)
|
|
173
166
|
fail(
|
|
174
167
|
"architecture publicConsumerDogfood contract is incomplete or widened",
|
|
@@ -226,6 +219,8 @@ function assertPolicySources(root) {
|
|
|
226
219
|
"No agent may add or restore a relative/self reusable-workflow call",
|
|
227
220
|
"never solve recursion with an internal exception",
|
|
228
221
|
"scripts/check-v4-public-dogfood-contract.mjs",
|
|
222
|
+
"source-persisted exact commit SHA",
|
|
223
|
+
"v4-alpha",
|
|
229
224
|
])
|
|
230
225
|
if (!agents.includes(invariant))
|
|
231
226
|
fail(`AGENTS.md is missing invariant: ${invariant}`);
|