@kungfu-tech/buildchain 3.0.9-alpha.8 → 4.0.0-alpha.0
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 +39 -0
- package/README.md +17 -13
- package/actions/promote-buildchain-ref/README.md +0 -10
- package/actions/release-tail/README.md +6 -10
- package/architecture/internal-capabilities.json +502 -0
- package/architecture/maintainability-baseline.json +59 -0
- package/architecture/maintainability-policy.json +1384 -0
- package/architecture/release-tail-contract-inventory.json +587 -0
- package/architecture/v3-core-mechanism-inventory.json +1043 -0
- package/architecture/v3-core-mechanism-inventory.md +67 -0
- package/architecture/v4-adopter-delivery-parity.json +47 -0
- package/architecture/v4-architecture-constitution.md +210 -0
- package/architecture/v4-bootstrap-authority.json +56 -0
- package/architecture/v4-canonical-contract-fixtures.json +105 -0
- package/architecture/v4-capability-state-machine-manifest.json +480 -0
- package/architecture/v4-capability-state-machine-manifest.schema.json +202 -0
- package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.json +830 -0
- package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.md +223 -0
- package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.schema.json +318 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.json +227 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.schema.json +67 -0
- package/architecture/v4-exception-ledger.json +5 -0
- package/architecture/v4-exception-ledger.schema.json +56 -0
- package/architecture/v4-partial-mutation-recovery-qualification.json +65 -0
- package/architecture/v4-platform-stage-checkpoints.json +97 -0
- package/architecture/v4-provider-operation-journal-contract.json +113 -0
- package/architecture/v4-release-activation-shadow-domain.json +61 -0
- package/architecture/v4-release-train-parity.json +68 -0
- package/architecture/v4-rust-libnode-bridge-evaluation.json +107 -0
- package/architecture/v4-rust-libnode-bridge-spike.md +85 -0
- package/architecture/v4-stable-publication-fence.json +66 -0
- package/architecture/v4-stage-capsule-contract.json +90 -0
- package/architecture/v4-stage-capsule-qualification.json +99 -0
- package/architecture/v4-stage-capsule-resume-planner.json +52 -0
- package/architecture/v4-stage-capsule-store-contract.json +72 -0
- package/bin/buildchain.mjs +15 -6
- package/contracts/auditable-demo-scenario-v1.schema.json +1 -1
- package/contracts/buildchain-v2-residuals-v1.json +2 -2
- package/contracts/fixtures/v4-delivery-warrant-trace-v1/golden.json +158 -0
- package/contracts/fixtures/v4-delivery-warrant-trace-v1/property.json +380 -0
- package/contracts/fixtures/v4-delivery-warrant-trace-v1/replay.json +163 -0
- package/contracts/fixtures/v4-partial-mutation-recovery-v1/shared.json +155 -0
- package/contracts/fixtures/v4-provider-operation-journal-v1/shared.json +457 -0
- package/contracts/fixtures/v4-provider-readback-idempotency-v1/shared.json +682 -0
- package/contracts/fixtures/v4-release-activation-shadow-v1/shared.json +53 -0
- package/contracts/fixtures/v4-stable-publication-fence-v1/shared.json +56 -0
- package/contracts/fixtures/v4-stage-capsule-resume-v1/late-platform-failure.json +176 -0
- package/contracts/fixtures/v4-stage-capsule-store-v1/shared.json +132 -0
- package/contracts/fixtures/v4-stage-capsule-v1/shared.json +111 -0
- package/contracts/v4-canonical-contracts-v1.schema.json +182 -0
- package/contracts/v4-delivery-warrant-semantic-diff-report-v1.schema.json +284 -0
- package/contracts/v4-delivery-warrant-trace-v1.schema.json +215 -0
- package/contracts/v4-host-contract-v1.schema.json +135 -0
- package/contracts/v4-partial-mutation-recovery-v1.schema.json +168 -0
- package/contracts/v4-provider-operation-journal-v1.schema.json +381 -0
- package/contracts/v4-release-activation-shadow-v1.schema.json +186 -0
- package/contracts/v4-stable-publication-fence-v1.schema.json +125 -0
- package/contracts/v4-stage-capsule-resume-v1.schema.json +225 -0
- package/contracts/v4-stage-capsule-store-v1.schema.json +154 -0
- package/contracts/v4-stage-capsule-v1.schema.json +178 -0
- package/crates/buildchain-v4-bridge/Cargo.lock +213 -0
- package/crates/buildchain-v4-bridge/Cargo.toml +12 -0
- package/crates/buildchain-v4-bridge/src/main.rs +495 -0
- package/dist/site/agent-index.json +0 -3
- package/dist/site/artifact-schemas.json +0 -6
- package/dist/site/buildchain-contract.json +103 -397
- package/dist/site/buildchain-site.json +302 -270
- package/dist/site/capability-registry.json +9 -11
- package/dist/site/cli-registry.json +8 -30
- package/dist/site/controller-registry.json +48 -8
- package/dist/site/kfd-claims.json +129 -142
- package/dist/site/kfd-upstream-aggregate.json +9 -9
- package/dist/site/manual-registry.json +18 -49
- package/dist/site/node-api-registry.json +1882 -4118
- package/dist/site/page-registry.json +278 -230
- package/dist/site/public-surface-audit.json +87 -185
- package/dist/site/publication-authority-registry.json +11 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +3 -7
- package/dist/site/schemas/kfd-support-projection-v1.schema.json +106 -0
- package/dist/site/site-manifest.json +22 -38
- package/dist/site/workflow-registry.json +80 -35
- package/docs/MAP.md +8 -4
- package/docs/auditable-demo.md +2 -2
- package/docs/aws-us-elastic-runner-burst-plane.md +7 -59
- package/docs/cli-reference.md +15 -92
- package/docs/cli.md +14 -14
- package/docs/dev-alpha-candidate-patrol.md +3 -11
- package/docs/dev-delivery-warrant.md +28 -100
- package/docs/engineering-housekeeper.md +12 -4
- package/docs/getting-started.md +1 -1
- package/docs/install.md +5 -5
- package/docs/kfd-support.md +2 -7
- package/docs/lifecycle-protocol.md +2 -4
- package/docs/node-api-reference.md +414 -523
- package/docs/release-flow.md +31 -31
- package/docs/release-governance.md +16 -28
- package/docs/release-passport.md +2 -15
- package/docs/release-tail-contract.md +13 -6
- package/docs/release-tail-provider-plane.md +13 -19
- package/docs/release-train.md +4 -4
- package/docs/reusable-build-surface.md +14 -27
- package/docs/site-bundle-contract.md +1 -5
- package/docs/stable-candidate-patrol.md +7 -7
- package/docs/v4-canonical-contracts.md +92 -0
- package/docs/v4-delivery-warrant-read-candidate.md +73 -0
- package/docs/v4-delivery-warrant-semantic-diff.md +75 -0
- package/docs/v4-production-release.md +63 -0
- package/docs/v4-stage-capsule.md +193 -0
- package/docs/versioning.md +3 -3
- package/package.json +19 -17
- package/packages/core/adopter-delivery-gate.js +576 -0
- package/packages/core/adopter-delivery-json.js +66 -0
- package/packages/core/artifact-signing.js +0 -61
- package/packages/core/artifact-verification-envelope.js +5 -5
- package/packages/core/build-facts.js +12 -4
- package/packages/core/buildchain-agent-manuals.js +0 -2
- package/packages/core/buildchain-config.js +4 -69
- package/packages/core/buildchain-contract.js +232 -46
- package/packages/core/buildchain-kfd-claims.js +1 -1
- package/packages/core/channel-candidate.js +0 -8
- package/packages/core/channel-promotion-baseline.js +0 -52
- package/packages/core/controller-evidence.js +3 -4
- package/packages/core/dev-alpha-candidate-selection.js +2 -10
- package/packages/core/dev-delivery-warrant-cancellation.js +0 -1
- package/packages/core/dev-delivery-warrant-settlement.js +1 -5
- package/packages/core/dev-delivery-warrant.js +12 -52
- package/packages/core/diagnostics.js +3 -8
- package/packages/core/github-governance-authority.js +3 -1
- package/packages/core/kfd-adopter-category-driver.js +167 -0
- package/packages/core/kfd-adopter-manifest.js +47 -46
- package/packages/core/kfd-gate.js +15 -45
- package/packages/core/paper-agent-entry.js +7 -16
- package/packages/core/paper-fleet.js +2 -2
- package/packages/core/paper-repository.js +2 -3
- package/packages/core/paper-scaffold-content.js +2 -23
- package/packages/core/paper.js +11 -43
- package/packages/core/publication-reproducibility.js +4 -4
- package/packages/core/release-line-bootstrap.js +21 -8
- package/packages/core/release-passport-contract.js +5 -5
- package/packages/core/release-passport.js +20 -131
- package/packages/core/self-dogfood-version.js +48 -35
- package/packages/core/spawn-command.js +232 -8
- package/packages/core/v4-adopter-delivery-parity.js +219 -0
- package/packages/core/v4-canonical-contracts.js +284 -0
- package/packages/core/v4-delivery-warrant-fixture-runner.js +371 -0
- package/packages/core/v4-delivery-warrant-read-candidate.js +279 -0
- package/packages/core/v4-delivery-warrant-semantic-diff-gate.js +352 -0
- package/packages/core/v4-delivery-warrant-shadow-adapter.js +447 -0
- package/packages/core/v4-partial-mutation-recovery-qualification.js +395 -0
- package/packages/core/v4-platform-stage-checkpoints.js +471 -0
- package/packages/core/v4-provider-operation-journal.js +531 -0
- package/packages/core/v4-provider-readback-idempotency.js +394 -0
- package/packages/core/v4-release-activation-shadow.js +519 -0
- package/packages/core/v4-stable-publication-fence.js +357 -0
- package/packages/core/v4-stage-capsule-local-store.js +426 -0
- package/packages/core/v4-stage-capsule-qualification-campaign.js +512 -0
- package/packages/core/v4-stage-capsule-qualification.js +456 -0
- package/packages/core/v4-stage-capsule-resume-planner.js +397 -0
- package/packages/core/v4-stage-capsule-store.js +401 -0
- package/packages/core/v4-stage-capsule.js +319 -0
- package/scripts/assemble-self-publication-admission.mjs +1 -1
- package/scripts/audit-publication-control-plane.mjs +5 -5
- package/scripts/auditable-demo-bundle-verification.mjs +3 -2
- package/scripts/auditable-demo-platform.mjs +2 -2
- package/scripts/auditable-demo-renditions.mjs +1 -1
- package/scripts/auditable-demo.mjs +2 -2
- package/scripts/aws-macos-jit-controller-core.mjs +13 -346
- package/scripts/aws-macos-jit-controller-runtime.mjs +2 -210
- package/scripts/aws-macos-jit-controller.mjs +37 -275
- package/scripts/aws-macos-jit-core.mjs +3 -41
- package/scripts/aws-macos-jit.mjs +0 -12
- package/scripts/aws-windows-jit-campaign.mjs +2 -2
- package/scripts/aws-windows-jit-controller-core.mjs +1 -1
- package/scripts/aws-windows-jit-controller.mjs +3 -3
- package/scripts/build-contract-core.mjs +3 -8
- package/scripts/build-standalone-binary.mjs +3 -14
- package/scripts/buildchain-channel-router.mjs +4 -0
- package/scripts/buildchain-cli-help.mjs +7 -13
- package/scripts/buildchain-contract-lock.mjs +0 -6
- package/scripts/check-action-bundles.mjs +3 -4
- package/scripts/check-internal-architecture.mjs +7 -6
- package/scripts/check-inventory.mjs +30 -25
- package/scripts/check-v4-public-dogfood-contract.mjs +270 -0
- package/scripts/create-release-bundle.mjs +8 -4
- package/scripts/dev-alpha-candidate-patrol.mjs +1 -22
- package/scripts/dev-delivery-proof.mjs +2 -54
- package/scripts/dev-delivery-warrant.mjs +59 -77
- package/scripts/dev-pr-auto-merge.mjs +4 -30
- package/scripts/dev-pr-delivery-warrant.mjs +0 -55
- package/scripts/dispatch-artifact-signing-authority.mjs +7 -4
- package/scripts/generate-buildchain-kfd-witnesses.mjs +94 -88
- package/scripts/generate-channel-build-workflow.mjs +54 -24
- package/scripts/generate-channel-promotion-workflow.mjs +15 -6
- package/scripts/generate-site-bundle.mjs +5 -21
- package/scripts/git-fetch-process-tree.mjs +34 -1
- package/scripts/init-repo.mjs +64 -159
- package/scripts/inspect-artifact-signing-requests.mjs +0 -6
- package/scripts/locked-source-checkout.mjs +3 -4
- package/scripts/maintainability-metrics.mjs +8 -2
- package/scripts/npm-publish-dry-run.mjs +2 -2
- package/scripts/npm-publish-transaction.mjs +2 -2
- package/scripts/publication-commit-evidence.mjs +23 -69
- package/scripts/release-candidate-resolver.mjs +11 -17
- package/scripts/release-tail.mjs +3 -49
- package/scripts/resume-from-candidate-run.mjs +9 -123
- package/scripts/run-lifecycle-core.mjs +12 -13
- package/scripts/seal-artifact-signing-requests.mjs +17 -21
- package/scripts/site-capability-metadata.mjs +2 -7
- package/scripts/stable-candidate-qualification.mjs +10 -10
- package/scripts/v4-architecture.mjs +12 -2
- package/scripts/v4-bridge-bootstrap.mjs +141 -0
- package/scripts/v4-bridge-evidence.mjs +141 -0
- package/scripts/v4-host-adapter.mjs +182 -0
- package/scripts/v4-platform-stage-checkpoint-rehearsal.mjs +207 -0
- package/scripts/v4-stage-capsule-qualification.mjs +528 -0
- package/scripts/v4-stage-capsule-resume-rehearsal.mjs +57 -0
- package/scripts/v4-warrant-shadow-plan.mjs +601 -0
- package/scripts/verify-golden-path.mjs +5 -5
- package/scripts/web-surface-cloudfront-rewrite.mjs +15 -15
- package/scripts/web-surface-core.mjs +2 -8
- package/scripts/workflow-call-contract.mjs +5 -184
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +0 -16
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +0 -57
- package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +0 -47
- package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +0 -47
- package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +0 -40
- package/contracts/next-development-request-v1.schema.json +0 -66
- package/contracts/next-development-transition-v1.schema.json +0 -292
- package/contracts/publication-rehearsal-capsule-v1.schema.json +0 -173
- package/dist/site/schemas/publication-rehearsal-capsule-v1.schema.json +0 -269
- package/dist/site/schemas/release-tail-capabilities-v1.schema.json +0 -353
- package/dist/site/schemas/release-tail-provider-bindings-v1.schema.json +0 -94
- package/docs/next-development-transition.md +0 -119
- package/docs/publication-rehearsal.md +0 -94
- package/packages/core/buildchain-compatibility-proof.js +0 -631
- package/packages/core/dev-delivery-native-proof.js +0 -333
- package/packages/core/dev-delivery-warrant-shadow.js +0 -502
- package/packages/core/next-development-candidate-reservation.js +0 -186
- package/packages/core/next-development-controller.js +0 -726
- package/packages/core/next-development-projection.js +0 -288
- package/packages/core/next-development-transition.js +0 -738
- package/packages/core/publication-rehearsal-projection.js +0 -173
- package/packages/core/publication-rehearsal-runtime.js +0 -921
- package/scripts/aws-macos-jit-instance-rehydrate.mjs +0 -260
- package/scripts/aws-macos-jit-source-rebind.mjs +0 -724
- package/scripts/dev-delivery-native-run.mjs +0 -187
- package/scripts/dev-delivery-two-phase.mjs +0 -598
- package/scripts/generate-next-development-guidance.mjs +0 -49
- package/scripts/materialize-self-release-candidate-version.mjs +0 -137
- package/scripts/next-development-self-dogfood-harness.mjs +0 -409
- package/scripts/next-development-self-dogfood.mjs +0 -532
- package/scripts/next-development-transition.mjs +0 -47
- package/scripts/release-candidate-tail-reseal.mjs +0 -426
- package/templates/native-dev-delivery.yml +0 -82
|
@@ -9,7 +9,6 @@ export const ARTIFACT_SIGNING_AUTHORITY_CONTRACT =
|
|
|
9
9
|
|
|
10
10
|
const SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/u;
|
|
11
11
|
const SOURCE_SHA_PATTERN = /^[0-9a-f]{40}$/u;
|
|
12
|
-
const ENTITLEMENTS_PROFILES = new Set(["none", "jit-executable-v1"]);
|
|
13
12
|
const FORBIDDEN_CREDENTIAL_KEYS =
|
|
14
13
|
/(?:certificate|password|private.?key|secret|token|notary|issuer|team.?id|environment)/iu;
|
|
15
14
|
|
|
@@ -89,64 +88,6 @@ function exactSourceSha(value, label) {
|
|
|
89
88
|
return normalized;
|
|
90
89
|
}
|
|
91
90
|
|
|
92
|
-
function entitlementsPaths(value) {
|
|
93
|
-
if (value === undefined) return [];
|
|
94
|
-
if (!Array.isArray(value)) {
|
|
95
|
-
throw new Error("signing entitlements paths must be an array");
|
|
96
|
-
}
|
|
97
|
-
const paths = value.map((entry, index) => {
|
|
98
|
-
const normalized = nonEmptyString(
|
|
99
|
-
entry,
|
|
100
|
-
`signing entitlements paths[${index}]`,
|
|
101
|
-
);
|
|
102
|
-
const parts = normalized.split("/");
|
|
103
|
-
if (
|
|
104
|
-
normalized.startsWith("/") ||
|
|
105
|
-
normalized.includes("\\") ||
|
|
106
|
-
normalized.includes(",") ||
|
|
107
|
-
parts.some((part) => part === "" || part === "." || part === "..")
|
|
108
|
-
) {
|
|
109
|
-
throw new Error(
|
|
110
|
-
`signing entitlements paths[${index}] must be a safe archive-relative path`,
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
return normalized;
|
|
114
|
-
});
|
|
115
|
-
if (new Set(paths).size !== paths.length) {
|
|
116
|
-
throw new Error("signing entitlements paths must not contain duplicates");
|
|
117
|
-
}
|
|
118
|
-
return paths;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function signingEntitlements(profileId, kind, signature) {
|
|
122
|
-
const profile = String(signature.entitlementsProfile || "none").trim();
|
|
123
|
-
const paths = entitlementsPaths(signature.entitlementsPaths);
|
|
124
|
-
if (!ENTITLEMENTS_PROFILES.has(profile)) {
|
|
125
|
-
throw new Error(
|
|
126
|
-
`unsupported signing entitlements profile: ${profile || "<empty>"}`,
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
if (
|
|
130
|
-
profile !== "none" &&
|
|
131
|
-
(profileId !== "apple-developer-id" || kind !== "archive")
|
|
132
|
-
) {
|
|
133
|
-
throw new Error(
|
|
134
|
-
`signing entitlements profile ${profile} requires an Apple archive`,
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
if (
|
|
138
|
-
(profile === "none" && paths.length !== 0) ||
|
|
139
|
-
(profile !== "none" && paths.length === 0)
|
|
140
|
-
) {
|
|
141
|
-
throw new Error(
|
|
142
|
-
"signing entitlements paths must be non-empty exactly when an entitlements profile is enabled",
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
return profile === "none"
|
|
146
|
-
? {}
|
|
147
|
-
: { entitlementsProfile: profile, entitlementsPaths: paths };
|
|
148
|
-
}
|
|
149
|
-
|
|
150
91
|
function assertNoCredentialMaterial(value, path = "request") {
|
|
151
92
|
if (!value || typeof value !== "object") return;
|
|
152
93
|
for (const [key, child] of Object.entries(value)) {
|
|
@@ -250,7 +191,6 @@ export function createArtifactSigningRequest({
|
|
|
250
191
|
platform,
|
|
251
192
|
artifactKind: kind,
|
|
252
193
|
});
|
|
253
|
-
const entitlementIntent = signingEntitlements(profile.id, kind, signature);
|
|
254
194
|
const request = {
|
|
255
195
|
schemaVersion: 1,
|
|
256
196
|
contract: ARTIFACT_SIGNING_REQUEST_CONTRACT,
|
|
@@ -313,7 +253,6 @@ export function createArtifactSigningRequest({
|
|
|
313
253
|
profile: profile.id,
|
|
314
254
|
provider: profile.provider,
|
|
315
255
|
semantics: profile.semantics,
|
|
316
|
-
...entitlementIntent,
|
|
317
256
|
},
|
|
318
257
|
delivery: {
|
|
319
258
|
mode: nonEmptyString(
|
|
@@ -417,7 +417,7 @@ function kfdBindingBody({ manifest, manifestGate, legacyProjection, paths }) {
|
|
|
417
417
|
gatePath: paths.gate,
|
|
418
418
|
gateRoot: manifestGate.gateRoot,
|
|
419
419
|
},
|
|
420
|
-
|
|
420
|
+
source: jsonClone(manifestGate.source),
|
|
421
421
|
standardPackage: jsonClone(manifestGate.standardPackage),
|
|
422
422
|
witness: {
|
|
423
423
|
decisionRoot: manifestGate.decisionWitness.root,
|
|
@@ -445,10 +445,10 @@ export function createKfdAdopterReleaseBinding({
|
|
|
445
445
|
manifestPath = "kfd-adopter-manifest.json",
|
|
446
446
|
gatePath = "kfd-adopter-manifest-gate.json",
|
|
447
447
|
legacyProjectionPath = "kfd-support.json",
|
|
448
|
-
|
|
448
|
+
expectedSourceSha = "",
|
|
449
449
|
} = {}) {
|
|
450
450
|
const gate = validateKfdAdopterManifestGate(manifestGate, {
|
|
451
|
-
|
|
451
|
+
expectedSourceSha,
|
|
452
452
|
checkedAt: manifestGate?.checkedAt,
|
|
453
453
|
});
|
|
454
454
|
const legacy = validateKfdLegacySupportMatrixProjection(legacyProjection, { manifest, manifestGate });
|
|
@@ -468,7 +468,7 @@ export function validateKfdAdopterReleaseBinding(binding, {
|
|
|
468
468
|
manifest,
|
|
469
469
|
manifestGate,
|
|
470
470
|
legacyProjection,
|
|
471
|
-
|
|
471
|
+
expectedSourceSha = "",
|
|
472
472
|
} = {}) {
|
|
473
473
|
const issues = [];
|
|
474
474
|
if (!binding || binding.schemaVersion !== 1 || binding.contract !== KFD_ADOPTER_RELEASE_BINDING_CONTRACT) {
|
|
@@ -488,7 +488,7 @@ export function validateKfdAdopterReleaseBinding(binding, {
|
|
|
488
488
|
manifestPath: binding.authority?.manifestPath,
|
|
489
489
|
gatePath: binding.authority?.gatePath,
|
|
490
490
|
legacyProjectionPath: binding.legacyProjection?.path,
|
|
491
|
-
|
|
491
|
+
expectedSourceSha,
|
|
492
492
|
});
|
|
493
493
|
if (stableJson(binding) !== stableJson(expected)) {
|
|
494
494
|
issues.push({ code: "adopter-release-binding-drift", path: "", message: "release binding differs from the exact manifest, gate, or legacy projection closure" });
|
|
@@ -81,10 +81,18 @@ function repositorySlug(cwd) {
|
|
|
81
81
|
function readTrackedFiles(cwd, root = ".") {
|
|
82
82
|
const repoRoot = fs.realpathSync(path.resolve(cwd));
|
|
83
83
|
const requestedRoot = fs.realpathSync(path.resolve(cwd, root));
|
|
84
|
-
const relativeRoot = posixPath(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
const relativeRoot = posixPath(
|
|
85
|
+
git(requestedRoot, ["rev-parse", "--show-prefix"]),
|
|
86
|
+
).replace(/\/+$/u, "") || ".";
|
|
87
|
+
return parseZeroSeparated(git(repoRoot, ["ls-files", "-z"]))
|
|
88
|
+
.filter((entry) =>
|
|
89
|
+
entry
|
|
90
|
+
&& !entry.startsWith(".git/")
|
|
91
|
+
&& (
|
|
92
|
+
relativeRoot === "."
|
|
93
|
+
|| entry === relativeRoot
|
|
94
|
+
|| entry.startsWith(`${relativeRoot}/`)
|
|
95
|
+
))
|
|
88
96
|
.sort();
|
|
89
97
|
}
|
|
90
98
|
|
|
@@ -10,7 +10,6 @@ export const BUILDCHAIN_AGENT_MANUALS = Object.freeze([
|
|
|
10
10
|
{ id: "github-governance-authority", title: "GitHub governance authority", path: "docs/github-governance-authority.md", plane: "verify" },
|
|
11
11
|
{ id: "release-candidate", title: "Release Candidate Passport", path: "docs/release-candidate.md", plane: "verify" },
|
|
12
12
|
{ id: "release-train", title: "Release Train and Release Cut", path: "docs/release-train.md", plane: "verify" },
|
|
13
|
-
{ id: "next-development-transition", title: "Next-development transition", path: "docs/next-development-transition.md", plane: "verify" },
|
|
14
13
|
{ id: "release-propagation", title: "Release propagation", path: "docs/release-propagation.md", plane: "use" },
|
|
15
14
|
{ id: "readme-badges", title: "README badge blocks", path: "docs/readme-badges.md", plane: "use" },
|
|
16
15
|
{ id: "homebrew", title: "Homebrew distribution indexes", path: "docs/homebrew.md", plane: "use" },
|
|
@@ -33,7 +32,6 @@ export const BUILDCHAIN_AGENT_MANUALS = Object.freeze([
|
|
|
33
32
|
{ id: "publish-transaction", title: "Publish transaction", path: "docs/publish-transaction.md", plane: "verify" },
|
|
34
33
|
{ id: "release-tail-contract", title: "Declarative release-tail contract", path: "docs/release-tail-contract.md", plane: "verify" },
|
|
35
34
|
{ id: "release-tail-provider-plane", title: "Declarative release-tail provider plane", path: "docs/release-tail-provider-plane.md", plane: "use" },
|
|
36
|
-
{ id: "publication-rehearsal", title: "Publication rehearsal", path: "docs/publication-rehearsal.md", plane: "use" },
|
|
37
35
|
{ id: "release-governance", title: "Release governance", path: "docs/release-governance.md", plane: "why" },
|
|
38
36
|
{ id: "release-flow", title: "Release flow", path: "docs/release-flow.md", plane: "verify" },
|
|
39
37
|
{ id: "runtime-train-validation", title: "Runtime train validation", path: "docs/runtime-train-validation.md", plane: "verify" },
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { execSync } from "node:child_process";
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import os from "node:os";
|
|
4
3
|
import path from "node:path";
|
|
@@ -8,6 +7,7 @@ import {
|
|
|
8
7
|
BUILDCHAIN_CONFIG_PATH,
|
|
9
8
|
resolveBuildchainConfigPath,
|
|
10
9
|
} from "./buildchain-layout.js";
|
|
10
|
+
import { runShellCommandSync } from "./spawn-command.js";
|
|
11
11
|
|
|
12
12
|
const CONFIG_FILE = BUILDCHAIN_CONFIG_PATH;
|
|
13
13
|
const RESERVED_LIFECYCLE_KEYS = new Set(["env", "shell"]);
|
|
@@ -61,10 +61,6 @@ const SUPPORTED_SIGNING_PROFILES = new Set([
|
|
|
61
61
|
"windows-authenticode",
|
|
62
62
|
"detached-signature-v1",
|
|
63
63
|
]);
|
|
64
|
-
const SUPPORTED_SIGNING_ENTITLEMENTS_PROFILES = new Set([
|
|
65
|
-
"none",
|
|
66
|
-
"jit-executable-v1",
|
|
67
|
-
]);
|
|
68
64
|
const SUPPORTED_SIGNING_ARTIFACT_KINDS = new Set([
|
|
69
65
|
"auto", "binary", "archive", "blob", "directory", "mach-o", "app-bundle",
|
|
70
66
|
"framework-bundle", "plugin-bundle", "xpc-bundle", "dylib", "pkg", "dmg",
|
|
@@ -103,27 +99,6 @@ function normalizeStringArray(value, label) {
|
|
|
103
99
|
return value.map((entry, index) => assertString(entry, `${label}[${index}]`));
|
|
104
100
|
}
|
|
105
101
|
|
|
106
|
-
function normalizeSigningEntitlementsPaths(value, label) {
|
|
107
|
-
const paths = normalizeStringArray(value, label);
|
|
108
|
-
for (const [index, entry] of paths.entries()) {
|
|
109
|
-
const parts = entry.split("/");
|
|
110
|
-
if (
|
|
111
|
-
entry.startsWith("/") ||
|
|
112
|
-
entry.includes("\\") ||
|
|
113
|
-
entry.includes(",") ||
|
|
114
|
-
parts.some((part) => part === "" || part === "." || part === "..")
|
|
115
|
-
) {
|
|
116
|
-
throw new Error(
|
|
117
|
-
`${label}[${index}] must be a safe archive-relative path`,
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
if (new Set(paths).size !== paths.length) {
|
|
122
|
-
throw new Error(`${label} must not contain duplicate paths`);
|
|
123
|
-
}
|
|
124
|
-
return paths;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
102
|
function getByDottedKey(target, key) {
|
|
128
103
|
return String(key)
|
|
129
104
|
.split(".")
|
|
@@ -387,16 +362,7 @@ function normalizeSigningSection(signing) {
|
|
|
387
362
|
const artifacts = signing.artifacts.map((artifact, index) => {
|
|
388
363
|
const label = `signing.artifacts[${index}]`;
|
|
389
364
|
assertPlainObject(artifact, label);
|
|
390
|
-
const allowedKeys = new Set([
|
|
391
|
-
"id",
|
|
392
|
-
"path",
|
|
393
|
-
"profile",
|
|
394
|
-
"entitlements_profile",
|
|
395
|
-
"entitlements_paths",
|
|
396
|
-
"kind",
|
|
397
|
-
"platforms",
|
|
398
|
-
"required",
|
|
399
|
-
]);
|
|
365
|
+
const allowedKeys = new Set(["id", "path", "profile", "kind", "platforms", "required"]);
|
|
400
366
|
for (const key of Object.keys(artifact)) {
|
|
401
367
|
if (!allowedKeys.has(key)) {
|
|
402
368
|
throw new Error(`${label}.${key} is not supported; declare desired signature state only`);
|
|
@@ -411,41 +377,10 @@ function normalizeSigningSection(signing) {
|
|
|
411
377
|
if (!SUPPORTED_SIGNING_ARTIFACT_KINDS.has(kind)) {
|
|
412
378
|
throw new Error(`${label}.kind is not a supported signing artifact kind`);
|
|
413
379
|
}
|
|
414
|
-
const entitlementsProfile =
|
|
415
|
-
artifact.entitlements_profile === undefined
|
|
416
|
-
? "none"
|
|
417
|
-
: assertString(
|
|
418
|
-
artifact.entitlements_profile,
|
|
419
|
-
`${label}.entitlements_profile`,
|
|
420
|
-
);
|
|
421
|
-
if (!SUPPORTED_SIGNING_ENTITLEMENTS_PROFILES.has(entitlementsProfile)) {
|
|
422
|
-
throw new Error(
|
|
423
|
-
`${label}.entitlements_profile must be one of none or jit-executable-v1`,
|
|
424
|
-
);
|
|
425
|
-
}
|
|
426
|
-
if (entitlementsProfile !== "none" && kind !== "archive") {
|
|
427
|
-
throw new Error(
|
|
428
|
-
`${label}.entitlements_profile ${entitlementsProfile} requires kind = "archive"`,
|
|
429
|
-
);
|
|
430
|
-
}
|
|
431
|
-
const entitlementsPaths = normalizeSigningEntitlementsPaths(
|
|
432
|
-
artifact.entitlements_paths,
|
|
433
|
-
`${label}.entitlements_paths`,
|
|
434
|
-
);
|
|
435
|
-
if (
|
|
436
|
-
(entitlementsProfile === "none" && entitlementsPaths.length !== 0) ||
|
|
437
|
-
(entitlementsProfile !== "none" && entitlementsPaths.length === 0)
|
|
438
|
-
) {
|
|
439
|
-
throw new Error(
|
|
440
|
-
`${label}.entitlements_paths must be non-empty exactly when entitlements_profile is enabled`,
|
|
441
|
-
);
|
|
442
|
-
}
|
|
443
380
|
return {
|
|
444
381
|
id: artifact.id === undefined ? artifactPath : assertString(artifact.id, `${label}.id`),
|
|
445
382
|
path: artifactPath,
|
|
446
383
|
profile,
|
|
447
|
-
entitlementsProfile,
|
|
448
|
-
entitlementsPaths,
|
|
449
384
|
kind,
|
|
450
385
|
platforms: normalizeStringArray(artifact.platforms, `${label}.platforms`),
|
|
451
386
|
required: optionalBoolean(artifact.required, true),
|
|
@@ -1463,11 +1398,11 @@ export function runLifecycleStage({ cwd = process.cwd(), loadedConfig, name, sta
|
|
|
1463
1398
|
};
|
|
1464
1399
|
const runOnce = () => {
|
|
1465
1400
|
if (selected.mode === "script") {
|
|
1466
|
-
|
|
1401
|
+
runShellCommandSync(selected.script, execOptions);
|
|
1467
1402
|
return;
|
|
1468
1403
|
}
|
|
1469
1404
|
for (const command of selected.commands) {
|
|
1470
|
-
|
|
1405
|
+
runShellCommandSync(command, execOptions);
|
|
1471
1406
|
}
|
|
1472
1407
|
};
|
|
1473
1408
|
let lastError;
|
|
@@ -2,10 +2,11 @@ import crypto from "node:crypto";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
BUILDCHAIN_CHANNELS,
|
|
6
|
+
evaluateBuildchainChannelBinding,
|
|
7
|
+
normalizeBuildchainRef,
|
|
8
|
+
parseBuildchainRefIdentity,
|
|
9
|
+
} from "./buildchain-channel-identity.js";
|
|
9
10
|
import { createControllerRegistry } from "./controller-evidence.js";
|
|
10
11
|
import { enumerateWorkflowInputs } from "./public-surface-audit.js";
|
|
11
12
|
|
|
@@ -14,28 +15,13 @@ export {
|
|
|
14
15
|
evaluateBuildchainChannelBinding,
|
|
15
16
|
normalizeBuildchainRef,
|
|
16
17
|
parseBuildchainRefIdentity,
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export {
|
|
20
|
-
BUILDCHAIN_COMPATIBILITY_PROOF_REGISTRY_SCHEMA,
|
|
21
|
-
BUILDCHAIN_COMPATIBILITY_PROOF_SCHEMA,
|
|
22
|
-
BUILDCHAIN_COMPATIBILITY_VERIFICATION_RECEIPT_SCHEMA,
|
|
23
|
-
assertBuildchainCompatibilityProjection,
|
|
24
|
-
contractSummary,
|
|
25
|
-
createBuildchainCompatibilityProof,
|
|
26
|
-
createBuildchainCompatibilityProofRegistry,
|
|
27
|
-
createBuildchainCompatibilityVerificationReceipt,
|
|
28
|
-
createHistoricalBuildchainCompatibilityProofs,
|
|
29
|
-
evaluateBuildchainContractLock,
|
|
30
|
-
renderBuildchainContractDriftIssueBody,
|
|
31
|
-
verifyBuildchainCompatibilityProof,
|
|
32
|
-
verifyBuildchainCompatibilityVerificationReceipt,
|
|
33
|
-
} from "./buildchain-compatibility-proof.js";
|
|
18
|
+
};
|
|
34
19
|
|
|
35
20
|
export const BUILDCHAIN_RUNTIME_CONTRACT_WORLD = "kungfu-buildchain-runtime-contract-world";
|
|
36
21
|
export const BUILDCHAIN_CONTRACT_LOCK = "kungfu-buildchain-contract-lock";
|
|
37
22
|
|
|
38
23
|
const DEFAULT_POLICY = "major-compatible";
|
|
24
|
+
const FLOATING_CLASSES = new Set(["stable", "alpha"]);
|
|
39
25
|
|
|
40
26
|
function optionalString(value) {
|
|
41
27
|
return value === undefined || value === null ? "" : String(value);
|
|
@@ -311,6 +297,9 @@ export function createBuildchainContractWorld({
|
|
|
311
297
|
"publish-gate source locks are created by the wrapper and enforced by promote-buildchain-ref before publish side effects",
|
|
312
298
|
"GitHub Release passport and evidence publication is delegated to promote-buildchain-ref after the semver release transaction completes",
|
|
313
299
|
],
|
|
300
|
+
compatibleBreakingDigests: [
|
|
301
|
+
"sha256:acd401cfc46450115a3763fd4b679d85f185e8757ebd52510d6262e1533df4cf",
|
|
302
|
+
],
|
|
314
303
|
}),
|
|
315
304
|
surface(root, {
|
|
316
305
|
id: "advanced-release-candidate-promote",
|
|
@@ -325,6 +314,8 @@ export function createBuildchainContractWorld({
|
|
|
325
314
|
},
|
|
326
315
|
optionalInputs: [
|
|
327
316
|
"buildchain-ref",
|
|
317
|
+
"buildchain-expected-channel",
|
|
318
|
+
"buildchain-expected-major",
|
|
328
319
|
"buildchain-contract-lock-path",
|
|
329
320
|
"promotion-router-ref",
|
|
330
321
|
"promotion-router-sha",
|
|
@@ -352,6 +343,9 @@ export function createBuildchainContractWorld({
|
|
|
352
343
|
"promotion reuses PR-stage release-candidate artifacts and does not run the heavy native build matrix",
|
|
353
344
|
"resume-from-candidate-run is fail-closed and uses a fresh caller event instead of rerunning a frozen startup graph",
|
|
354
345
|
],
|
|
346
|
+
compatibleBreakingDigests: [
|
|
347
|
+
"sha256:aa30f22e3af0a89841310bdbdc900844dd95a66974db173fa140a71bbd7e82c0",
|
|
348
|
+
],
|
|
355
349
|
}),
|
|
356
350
|
surface(root, {
|
|
357
351
|
id: "web-surface",
|
|
@@ -498,6 +492,9 @@ export function createBuildchainContractWorld({
|
|
|
498
492
|
"publish transactions can require a resolved publish-gate source lock to prevent floating-ref drift",
|
|
499
493
|
"semver GitHub Releases are created or updated only after transaction completion, with prerelease/latest metadata bound to the authoritative publication channel and tag syntax retained as the fallback for ordinary callers",
|
|
500
494
|
],
|
|
495
|
+
compatibleBreakingDigests: [
|
|
496
|
+
"sha256:a59f0910e6df842e7699139472e5dd69ac2fdd7f7213bf2cb346d1d622556874",
|
|
497
|
+
],
|
|
501
498
|
}),
|
|
502
499
|
surface(root, {
|
|
503
500
|
id: "macos-credential-island-action",
|
|
@@ -773,6 +770,14 @@ export function createBuildchainContractWorld({
|
|
|
773
770
|
registryDigest: resolvedControllerRegistry.digest,
|
|
774
771
|
inputClassifications: descriptor.inputs,
|
|
775
772
|
},
|
|
773
|
+
...(descriptor.id === "build-lifecycle"
|
|
774
|
+
? {
|
|
775
|
+
compatibleBreakingDigests: [
|
|
776
|
+
"sha256:e264a79f9f399038c2fcfd21e4168c68c2e1485ee5c651c02242a02b622ac2be",
|
|
777
|
+
"sha256:30745921541e9b0f70475bb2178c2559f6aef248f6680670ccd44d8c5a69a6b1",
|
|
778
|
+
],
|
|
779
|
+
}
|
|
780
|
+
: {}),
|
|
776
781
|
guarantees: [
|
|
777
782
|
"plans bind exact consumer source SHA, exact Buildchain runtime SHA, and the runtime contract digest",
|
|
778
783
|
"receipts bind the plan digest and preserve pass, fail, skip, and partial stage outcomes",
|
|
@@ -792,45 +797,24 @@ export function createBuildchainContractWorld({
|
|
|
792
797
|
},
|
|
793
798
|
majorLine,
|
|
794
799
|
compatibilityPolicy: DEFAULT_POLICY,
|
|
795
|
-
compatibilityProofs: createHistoricalBuildchainCompatibilityProofs(),
|
|
796
800
|
surfaces,
|
|
797
801
|
};
|
|
798
802
|
return finalizeBuildchainContractWorld(base);
|
|
799
803
|
}
|
|
800
804
|
|
|
801
805
|
export function finalizeBuildchainContractWorld(contractWorld) {
|
|
802
|
-
const sourceSurfaces = (contractWorld.surfaces || []).map((entry) => ({ ...entry }));
|
|
803
|
-
const proofRegistry = createBuildchainCompatibilityProofRegistry({
|
|
804
|
-
proofs: contractWorld.compatibilityProofs || createHistoricalBuildchainCompatibilityProofs(),
|
|
805
|
-
surfaces: sourceSurfaces,
|
|
806
|
-
majorLine: contractWorld.majorLine,
|
|
807
|
-
});
|
|
808
806
|
const world = {
|
|
809
807
|
...contractWorld,
|
|
810
|
-
|
|
811
|
-
compatibilityProofRegistryRoot: proofRegistry.registryRoot,
|
|
812
|
-
surfaces: sourceSurfaces.map((entry) => {
|
|
813
|
-
const surfaceEntry = { ...entry };
|
|
814
|
-
delete surfaceEntry.compatibleBreakingDigests;
|
|
815
|
-
delete surfaceEntry.compatibilityProofRoots;
|
|
816
|
-
const projection = proofRegistry.projections[entry.id] || [];
|
|
817
|
-
if (projection.length > 0) {
|
|
818
|
-
surfaceEntry.compatibleBreakingDigests = projection.map((item) => item.breakingDigest);
|
|
819
|
-
surfaceEntry.compatibilityProofRoots = projection.map((item) => item.proofRoot);
|
|
820
|
-
}
|
|
821
|
-
return surfaceEntry;
|
|
822
|
-
}),
|
|
808
|
+
surfaces: (contractWorld.surfaces || []).map((entry) => ({ ...entry })),
|
|
823
809
|
};
|
|
824
810
|
const compatibilityModel = {
|
|
825
811
|
schemaVersion: world.schemaVersion,
|
|
826
812
|
contract: world.contract,
|
|
827
813
|
majorLine: world.majorLine,
|
|
828
|
-
compatibilityProofRegistryRoot: world.compatibilityProofRegistryRoot,
|
|
829
814
|
surfaces: world.surfaces.map((entry) => ({
|
|
830
815
|
id: entry.id,
|
|
831
816
|
kind: entry.kind,
|
|
832
817
|
breakingDigest: entry.breakingDigest,
|
|
833
|
-
compatibilityProofRoots: entry.compatibilityProofRoots || [],
|
|
834
818
|
})),
|
|
835
819
|
};
|
|
836
820
|
const digestModel = {
|
|
@@ -862,7 +846,6 @@ export function createBuildchainContractLock({
|
|
|
862
846
|
contract: contractWorld.contract,
|
|
863
847
|
contractDigest: contractWorld.contractDigest,
|
|
864
848
|
compatibilityDigest: contractWorld.compatibilityDigest,
|
|
865
|
-
compatibilityProofRegistryRoot: contractWorld.compatibilityProofRegistryRoot,
|
|
866
849
|
majorLine: contractWorld.majorLine,
|
|
867
850
|
compatibilityPolicy,
|
|
868
851
|
acceptedAt,
|
|
@@ -870,7 +853,6 @@ export function createBuildchainContractLock({
|
|
|
870
853
|
id: entry.id,
|
|
871
854
|
kind: entry.kind,
|
|
872
855
|
breakingDigest: entry.breakingDigest,
|
|
873
|
-
compatibilityProofRoots: entry.compatibilityProofRoots || [],
|
|
874
856
|
})),
|
|
875
857
|
},
|
|
876
858
|
};
|
|
@@ -881,7 +863,6 @@ export function readBuildchainContractWorld(filePath) {
|
|
|
881
863
|
if (!value || value.contract !== BUILDCHAIN_RUNTIME_CONTRACT_WORLD) {
|
|
882
864
|
throw new Error(`Buildchain contract world is missing or invalid: ${filePath}`);
|
|
883
865
|
}
|
|
884
|
-
assertBuildchainCompatibilityProjection(value);
|
|
885
866
|
const computed = finalizeBuildchainContractWorld(value);
|
|
886
867
|
for (const digestName of ["compatibilityDigest", "contractDigest"]) {
|
|
887
868
|
const published = String(value[digestName] || "").trim();
|
|
@@ -907,3 +888,208 @@ export function readBuildchainContractLock(filePath) {
|
|
|
907
888
|
}
|
|
908
889
|
return value;
|
|
909
890
|
}
|
|
891
|
+
|
|
892
|
+
function surfaceMap(surfaces = []) {
|
|
893
|
+
return new Map(surfaces.map((entry) => [entry.id, entry]));
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
function evaluateChannelBinding({
|
|
897
|
+
lock,
|
|
898
|
+
workflowShellRef,
|
|
899
|
+
runtimeRef,
|
|
900
|
+
expectedChannel,
|
|
901
|
+
expectedMajor,
|
|
902
|
+
allowOpaqueRuntime,
|
|
903
|
+
}) {
|
|
904
|
+
if (!workflowShellRef && !expectedChannel && !expectedMajor) return null;
|
|
905
|
+
return evaluateBuildchainChannelBinding({
|
|
906
|
+
workflowShellRef,
|
|
907
|
+
runtimeRef,
|
|
908
|
+
lockRef: lock?.buildchain?.ref || "",
|
|
909
|
+
lockMajorLine: lock?.buildchain?.majorLine || "",
|
|
910
|
+
expectedChannel,
|
|
911
|
+
expectedMajor,
|
|
912
|
+
allowOpaqueRuntime,
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
export function evaluateBuildchainContractLock({
|
|
917
|
+
lock,
|
|
918
|
+
current,
|
|
919
|
+
runtimeRef = "",
|
|
920
|
+
runtimeSha = "",
|
|
921
|
+
runtimeClass = "",
|
|
922
|
+
compatibilityPolicy = "",
|
|
923
|
+
workflowShellRef = "",
|
|
924
|
+
expectedChannel = "",
|
|
925
|
+
expectedMajor = "",
|
|
926
|
+
allowOpaqueRuntime = false,
|
|
927
|
+
} = {}) {
|
|
928
|
+
if (!current || current.contract !== BUILDCHAIN_RUNTIME_CONTRACT_WORLD) {
|
|
929
|
+
throw new Error("current must be a Buildchain runtime contract world");
|
|
930
|
+
}
|
|
931
|
+
const binding = evaluateChannelBinding({
|
|
932
|
+
lock,
|
|
933
|
+
workflowShellRef,
|
|
934
|
+
runtimeRef,
|
|
935
|
+
expectedChannel,
|
|
936
|
+
expectedMajor,
|
|
937
|
+
allowOpaqueRuntime,
|
|
938
|
+
});
|
|
939
|
+
if (binding?.ok === false) {
|
|
940
|
+
return {
|
|
941
|
+
ok: false,
|
|
942
|
+
status: binding.status,
|
|
943
|
+
drift: false,
|
|
944
|
+
compatible: false,
|
|
945
|
+
issueRecommended: false,
|
|
946
|
+
reasons: binding.reasons,
|
|
947
|
+
channelBinding: binding,
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
const floatingRuntime = FLOATING_CLASSES.has(runtimeClass);
|
|
951
|
+
if (!floatingRuntime) {
|
|
952
|
+
return {
|
|
953
|
+
ok: true,
|
|
954
|
+
status: "non-floating-runtime",
|
|
955
|
+
drift: false,
|
|
956
|
+
compatible: true,
|
|
957
|
+
issueRecommended: false,
|
|
958
|
+
reason: `runtime class ${runtimeClass || "unknown"} is not a stable floating ref`,
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
if (!lock) {
|
|
962
|
+
return {
|
|
963
|
+
ok: true,
|
|
964
|
+
status: "missing-lock",
|
|
965
|
+
drift: false,
|
|
966
|
+
compatible: true,
|
|
967
|
+
issueRecommended: false,
|
|
968
|
+
reason: "consumer repository has no Buildchain contract lock",
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
const accepted = lock.buildchain || {};
|
|
972
|
+
const policy = compatibilityPolicy || accepted.compatibilityPolicy || DEFAULT_POLICY;
|
|
973
|
+
const shaDrift = !!accepted.resolvedSha && !!runtimeSha && accepted.resolvedSha !== runtimeSha;
|
|
974
|
+
const contractDrift = !!accepted.contractDigest && accepted.contractDigest !== current.contractDigest;
|
|
975
|
+
if (!shaDrift && !contractDrift) {
|
|
976
|
+
return {
|
|
977
|
+
ok: true,
|
|
978
|
+
status: "unchanged",
|
|
979
|
+
drift: false,
|
|
980
|
+
compatible: true,
|
|
981
|
+
issueRecommended: false,
|
|
982
|
+
policy,
|
|
983
|
+
accepted,
|
|
984
|
+
current: contractSummary(current, runtimeRef, runtimeSha),
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
const reasons = [];
|
|
988
|
+
if (accepted.contract !== current.contract) {
|
|
989
|
+
reasons.push(`contract changed from ${accepted.contract || "(unknown)"} to ${current.contract}`);
|
|
990
|
+
}
|
|
991
|
+
if (accepted.majorLine && accepted.majorLine !== current.majorLine) {
|
|
992
|
+
reasons.push(`major line changed from ${accepted.majorLine} to ${current.majorLine}`);
|
|
993
|
+
}
|
|
994
|
+
if (policy === "exact" && accepted.contractDigest !== current.contractDigest) {
|
|
995
|
+
reasons.push("exact policy requires the contract digest to remain unchanged");
|
|
996
|
+
}
|
|
997
|
+
if (!["major-compatible", "allow-additive", "exact"].includes(policy)) {
|
|
998
|
+
reasons.push(`unsupported compatibility policy: ${policy}`);
|
|
999
|
+
}
|
|
1000
|
+
const currentSurfaces = surfaceMap(current.surfaces);
|
|
1001
|
+
for (const oldSurface of accepted.surfaces || []) {
|
|
1002
|
+
const nextSurface = currentSurfaces.get(oldSurface.id);
|
|
1003
|
+
if (!nextSurface) {
|
|
1004
|
+
reasons.push(`surface removed: ${oldSurface.id}`);
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
const compatibleBreakingDigests = new Set(nextSurface.compatibleBreakingDigests || []);
|
|
1008
|
+
if (
|
|
1009
|
+
nextSurface.breakingDigest !== oldSurface.breakingDigest
|
|
1010
|
+
&& !compatibleBreakingDigests.has(oldSurface.breakingDigest)
|
|
1011
|
+
) {
|
|
1012
|
+
reasons.push(`surface breaking digest changed: ${oldSurface.id}`);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
const compatible = reasons.length === 0;
|
|
1016
|
+
return {
|
|
1017
|
+
ok: compatible,
|
|
1018
|
+
status: compatible ? "compatible-drift" : "breaking-drift",
|
|
1019
|
+
drift: shaDrift || contractDrift,
|
|
1020
|
+
shaDrift,
|
|
1021
|
+
contractDrift,
|
|
1022
|
+
compatible,
|
|
1023
|
+
issueRecommended: true,
|
|
1024
|
+
policy,
|
|
1025
|
+
reasons,
|
|
1026
|
+
accepted,
|
|
1027
|
+
current: contractSummary(current, runtimeRef, runtimeSha),
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
export function contractSummary(contractWorld, runtimeRef = "", runtimeSha = "") {
|
|
1032
|
+
return {
|
|
1033
|
+
ref: runtimeRef,
|
|
1034
|
+
resolvedSha: runtimeSha,
|
|
1035
|
+
contract: contractWorld.contract,
|
|
1036
|
+
contractDigest: contractWorld.contractDigest,
|
|
1037
|
+
compatibilityDigest: contractWorld.compatibilityDigest,
|
|
1038
|
+
majorLine: contractWorld.majorLine,
|
|
1039
|
+
surfaceCount: Array.isArray(contractWorld.surfaces) ? contractWorld.surfaces.length : 0,
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
export function renderBuildchainContractDriftIssueBody({
|
|
1044
|
+
repository = "",
|
|
1045
|
+
workflow = "",
|
|
1046
|
+
runUrl = "",
|
|
1047
|
+
lockPath = "",
|
|
1048
|
+
evaluation,
|
|
1049
|
+
} = {}) {
|
|
1050
|
+
const accepted = evaluation.accepted || {};
|
|
1051
|
+
const current = evaluation.current || {};
|
|
1052
|
+
const severity = evaluation.compatible ? "compatible" : "breaking";
|
|
1053
|
+
return [
|
|
1054
|
+
"# Buildchain contract drift",
|
|
1055
|
+
"",
|
|
1056
|
+
"## Summary",
|
|
1057
|
+
"",
|
|
1058
|
+
`Buildchain detected ${severity} contract drift for a floating runtime ref before expensive Buildchain work continued.`,
|
|
1059
|
+
"",
|
|
1060
|
+
"## Consumer",
|
|
1061
|
+
"",
|
|
1062
|
+
`- Repository: ${repository || "(unknown)"}`,
|
|
1063
|
+
`- Workflow: ${workflow || "(unknown)"}`,
|
|
1064
|
+
`- Run: ${runUrl || "(unknown)"}`,
|
|
1065
|
+
`- Lock path: ${lockPath || "(unknown)"}`,
|
|
1066
|
+
"",
|
|
1067
|
+
"## Accepted Buildchain contract",
|
|
1068
|
+
"",
|
|
1069
|
+
`- Ref: ${accepted.ref || "(unknown)"}`,
|
|
1070
|
+
`- SHA: ${accepted.resolvedSha || "(unknown)"}`,
|
|
1071
|
+
`- Contract digest: ${accepted.contractDigest || "(unknown)"}`,
|
|
1072
|
+
`- Compatibility digest: ${accepted.compatibilityDigest || "(unknown)"}`,
|
|
1073
|
+
`- Policy: ${evaluation.policy || accepted.compatibilityPolicy || "(unknown)"}`,
|
|
1074
|
+
"",
|
|
1075
|
+
"## Current Buildchain contract",
|
|
1076
|
+
"",
|
|
1077
|
+
`- Ref: ${current.ref || "(unknown)"}`,
|
|
1078
|
+
`- SHA: ${current.resolvedSha || "(unknown)"}`,
|
|
1079
|
+
`- Contract digest: ${current.contractDigest || "(unknown)"}`,
|
|
1080
|
+
`- Compatibility digest: ${current.compatibilityDigest || "(unknown)"}`,
|
|
1081
|
+
`- Major line: ${current.majorLine || "(unknown)"}`,
|
|
1082
|
+
"",
|
|
1083
|
+
"## Compatibility",
|
|
1084
|
+
"",
|
|
1085
|
+
`- Status: ${evaluation.status || "(unknown)"}`,
|
|
1086
|
+
`- Compatible: ${evaluation.compatible ? "yes" : "no"}`,
|
|
1087
|
+
evaluation.reasons?.length ? evaluation.reasons.map((reason) => `- ${reason}`).join("\n") : "- No breaking drift detected.",
|
|
1088
|
+
"",
|
|
1089
|
+
"## Suggested next action",
|
|
1090
|
+
"",
|
|
1091
|
+
evaluation.compatible
|
|
1092
|
+
? "Review the Buildchain release notes, then update the consumer contract lock to the current SHA and contract digest."
|
|
1093
|
+
: "Failing before heavy build is intentional. Review the Buildchain contract change, update the consumer workflow/configuration, or pin the previous Buildchain SHA.",
|
|
1094
|
+
].join("\n");
|
|
1095
|
+
}
|
|
@@ -138,7 +138,7 @@ function immediateReadmes(root, dir) {
|
|
|
138
138
|
|
|
139
139
|
function publicDocumentationFiles(root) {
|
|
140
140
|
return uniquePaths([
|
|
141
|
-
...
|
|
141
|
+
...BUILDCHAIN_AGENT_MANUALS.map((entry) => entry.path),
|
|
142
142
|
"README.md",
|
|
143
143
|
"packages/core/README.md",
|
|
144
144
|
...immediateReadmes(root, "actions"),
|