@kungfu-tech/buildchain 3.0.7-alpha.0 → 3.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/actions/promote-buildchain-ref/README.md +10 -0
- package/contracts/auditable-demo-scenario-v1.schema.json +1 -1
- package/contracts/engineering-housekeeper-v1.schema.json +143 -0
- package/contracts/fixtures/engineering-housekeeper-v1/cases.json +68 -0
- package/dist/site/buildchain-contract.json +24 -24
- package/dist/site/buildchain-site.json +91 -30
- package/dist/site/capability-registry.json +3 -3
- package/dist/site/controller-registry.json +6 -2
- package/dist/site/kfd-claims.json +122 -11
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +8 -7
- package/dist/site/node-api-registry.json +683 -105
- package/dist/site/page-registry.json +80 -19
- package/dist/site/public-surface-audit.json +98 -7
- package/dist/site/publication-authority-registry.json +81 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +2 -0
- package/dist/site/site-manifest.json +11 -11
- package/dist/site/workflow-registry.json +119 -2
- package/docs/MAP.md +1 -0
- package/docs/auditable-demo.md +2 -2
- package/docs/dev-delivery-warrant.md +49 -4
- package/docs/engineering-housekeeper.md +138 -0
- package/docs/lifecycle-protocol.md +4 -2
- package/docs/node-api-reference.md +277 -212
- package/docs/release-governance.md +17 -2
- package/docs/release-tail-provider-plane.md +1 -1
- package/docs/reusable-build-surface.md +11 -0
- package/package.json +4 -1
- package/packages/core/artifact-signing.js +61 -0
- package/packages/core/buildchain-config.js +66 -6
- package/packages/core/buildchain-publication-authority.js +4 -0
- package/packages/core/controller-evidence.js +2 -1
- package/packages/core/dev-delivery-warrant-cancellation.js +1 -0
- package/packages/core/dev-delivery-warrant-shadow.js +502 -0
- package/packages/core/dev-delivery-warrant.js +15 -6
- package/packages/core/diagnostics.js +8 -3
- package/packages/core/engineering-housekeeper-github-client.js +222 -0
- package/packages/core/engineering-housekeeper-github.js +501 -0
- package/packages/core/engineering-housekeeper.js +259 -0
- package/packages/core/index.js +3 -0
- package/packages/core/kfd-gate.js +45 -15
- package/packages/core/publication-rehearsal-runtime.js +13 -1
- package/packages/core/release-passport.js +130 -20
- package/scripts/assemble-self-publication-admission.mjs +1 -1
- package/scripts/audit-publication-control-plane.mjs +1 -1
- package/scripts/auditable-demo-bundle-verification.mjs +2 -3
- 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/build-contract-core.mjs +8 -3
- package/scripts/build-standalone-binary.mjs +14 -3
- package/scripts/check-inventory.mjs +3 -1
- package/scripts/dev-delivery-warrant.mjs +31 -4
- package/scripts/dev-pr-auto-merge.mjs +30 -4
- package/scripts/dev-pr-delivery-warrant.mjs +50 -0
- package/scripts/engineering-housekeeper-workflow.mjs +394 -0
- package/scripts/generate-site-bundle.mjs +23 -4
- package/scripts/inspect-artifact-signing-requests.mjs +6 -0
- package/scripts/materialize-self-release-candidate-version.mjs +6 -0
- package/scripts/publication-commit-evidence.mjs +69 -23
- package/scripts/release-candidate-resolver.mjs +16 -10
- package/scripts/resume-from-candidate-run.mjs +123 -9
- package/scripts/seal-artifact-signing-requests.mjs +6 -0
- package/scripts/site-capability-metadata.mjs +2 -0
- package/scripts/web-surface-core.mjs +8 -2
- package/scripts/workflow-call-contract.mjs +1 -1
|
@@ -610,6 +610,12 @@ The reusable caller supports `off`, read-only `shadow`, and fail-closed
|
|
|
610
610
|
`required` rollout modes. GitHub Merge Queue remains the final protected-ref
|
|
611
611
|
authority in every mode.
|
|
612
612
|
|
|
613
|
+
Bounded-concurrency experiments use the separate effect-disabled Warrant
|
|
614
|
+
shadow planner. It may evaluate at most two fully bound lanes from one exact
|
|
615
|
+
observation, but it cannot mint a second production Warrant or mutate GitHub.
|
|
616
|
+
Its aggregate threshold decision is qualification evidence for a later rollout
|
|
617
|
+
change, not authority to change the live single-flight policy.
|
|
618
|
+
|
|
613
619
|
The canonical consumer required check context is `check / check`, matching the
|
|
614
620
|
reusable workflow call plus its `check` job. Buildchain's own `Verify` workflow
|
|
615
621
|
emits the repository-local context `check`, so Buildchain self-promotion,
|
|
@@ -785,6 +791,12 @@ active semver dev line, so consumers do not pin patrol to a stale minor branch.
|
|
|
785
791
|
The separate workflow names keep consumer schedules readable and stable while
|
|
786
792
|
Buildchain adds new checks behind the cadence wrappers.
|
|
787
793
|
|
|
794
|
+
Branch and pull-request residue uses the separate
|
|
795
|
+
[`Engineering Housekeeper`](engineering-housekeeper.md) contract. Its scheduled
|
|
796
|
+
callers are report-only and read-only by default; apply requires a manual,
|
|
797
|
+
explicit two-part gate, exact provider-state revalidation, scoped job
|
|
798
|
+
permissions, and rooted plan/report/receipt evidence.
|
|
799
|
+
|
|
788
800
|
## Package-Manager Adapters
|
|
789
801
|
|
|
790
802
|
Old ABV assumed JavaScript repositories with root version state and often
|
|
@@ -816,8 +828,11 @@ declare version-state files and lifecycle commands without pretending every
|
|
|
816
828
|
project is a Node workspace. Supported version files include JSON, TOML, and
|
|
817
829
|
regex-based files such as `CMakeLists.txt` or `conanfile.py`.
|
|
818
830
|
|
|
819
|
-
The promotion action consumes `version.files`, optional
|
|
820
|
-
`version.derived_files`, and `lifecycle.verify`.
|
|
831
|
+
The promotion action consumes `version.files`, optional
|
|
832
|
+
`version.derived_files`, and `lifecycle.verify`. Semver and anchored/manual
|
|
833
|
+
repositories may both declare lifecycle-regenerated tracked outputs as derived
|
|
834
|
+
files; anchored/manual repositories additionally bind them into their committed
|
|
835
|
+
version witnesses.
|
|
821
836
|
The verify stage runs after generated version-state changes are applied locally
|
|
822
837
|
and before any release refs move. If `verification-command` is passed directly
|
|
823
838
|
to the action, that explicit command overrides `lifecycle.verify`.
|
|
@@ -95,7 +95,7 @@ Buildchain self-release calls the same public reusable workflow coordinate as a
|
|
|
95
95
|
consumer:
|
|
96
96
|
|
|
97
97
|
```text
|
|
98
|
-
kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@
|
|
98
|
+
kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@769b221bad7a6b9104afad4c2628d9dca396ab0f
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
The caller pins the public router and runtime to the same exact implementation
|
|
@@ -683,6 +683,17 @@ For a declared macOS `archive`, the authority safely extracts the sealed
|
|
|
683
683
|
container, signs and verifies every Mach-O payload, signs Mach-O payloads inside
|
|
684
684
|
embedded Python wheels, rebuilds each affected wheel's PEP 427 `RECORD`, and
|
|
685
685
|
recreates the original zip or tar.gz before returning the exact final bytes.
|
|
686
|
+
Archives whose executable hosts a JIT runtime can additionally request the
|
|
687
|
+
Buildchain-owned `entitlements_profile = "jit-executable-v1"` and exact paths,
|
|
688
|
+
for example `entitlements_paths = ["product/runtime/python/bin/python3"]`.
|
|
689
|
+
Paths are relative to the extracted archive root. The authority
|
|
690
|
+
then attaches only `com.apple.security.cs.allow-jit` to the exact executable
|
|
691
|
+
Mach-O files sealed in `entitlements_paths`, leaves every other executable and
|
|
692
|
+
library without exception entitlements, and records the profile, paths, and
|
|
693
|
+
entitled executable count in provider evidence. Consumer-provided entitlement
|
|
694
|
+
files and wildcard target paths are unsupported. The profile is valid only for
|
|
695
|
+
Apple `archive` requests and fails closed on an unsafe, missing,
|
|
696
|
+
non-executable, duplicate, or unsealed target path.
|
|
686
697
|
Windows `pe` and `binary` artifacts
|
|
687
698
|
resolve to timestamped native `windows-authenticode`; Windows PE never falls
|
|
688
699
|
back to a detached signature. Linux and other non-native binary files,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "3.0.7
|
|
3
|
+
"version": "3.0.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Buildchain Release Passport, release governance, CLI toolkit, and site facts.",
|
|
6
6
|
"repository": "https://github.com/kungfu-systems/buildchain",
|
|
@@ -49,6 +49,8 @@
|
|
|
49
49
|
"./publication-control-plane-audit": "./packages/core/publication-control-plane-audit.js",
|
|
50
50
|
"./buildchain-publication-authority": "./packages/core/buildchain-publication-authority.js",
|
|
51
51
|
"./github-governance-authority": "./packages/core/github-governance-authority.js",
|
|
52
|
+
"./engineering-housekeeper": "./packages/core/engineering-housekeeper.js",
|
|
53
|
+
"./engineering-housekeeper-github": "./packages/core/engineering-housekeeper-github.js",
|
|
52
54
|
"./kfd-gate": "./packages/core/kfd-gate.js",
|
|
53
55
|
"./release-candidate": "./packages/core/release-candidate.js",
|
|
54
56
|
"./release-candidate-recovery": "./packages/core/release-candidate-recovery.js",
|
|
@@ -137,6 +139,7 @@
|
|
|
137
139
|
},
|
|
138
140
|
"dependencies": {
|
|
139
141
|
"@kungfu-tech/kfd": "1.0.0-alpha.53",
|
|
142
|
+
"ajv": "8.20.0",
|
|
140
143
|
"smol-toml": "1.7.0"
|
|
141
144
|
}
|
|
142
145
|
}
|
|
@@ -9,6 +9,7 @@ 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"]);
|
|
12
13
|
const FORBIDDEN_CREDENTIAL_KEYS =
|
|
13
14
|
/(?:certificate|password|private.?key|secret|token|notary|issuer|team.?id|environment)/iu;
|
|
14
15
|
|
|
@@ -88,6 +89,64 @@ function exactSourceSha(value, label) {
|
|
|
88
89
|
return normalized;
|
|
89
90
|
}
|
|
90
91
|
|
|
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
|
+
|
|
91
150
|
function assertNoCredentialMaterial(value, path = "request") {
|
|
92
151
|
if (!value || typeof value !== "object") return;
|
|
93
152
|
for (const [key, child] of Object.entries(value)) {
|
|
@@ -191,6 +250,7 @@ export function createArtifactSigningRequest({
|
|
|
191
250
|
platform,
|
|
192
251
|
artifactKind: kind,
|
|
193
252
|
});
|
|
253
|
+
const entitlementIntent = signingEntitlements(profile.id, kind, signature);
|
|
194
254
|
const request = {
|
|
195
255
|
schemaVersion: 1,
|
|
196
256
|
contract: ARTIFACT_SIGNING_REQUEST_CONTRACT,
|
|
@@ -253,6 +313,7 @@ export function createArtifactSigningRequest({
|
|
|
253
313
|
profile: profile.id,
|
|
254
314
|
provider: profile.provider,
|
|
255
315
|
semantics: profile.semantics,
|
|
316
|
+
...entitlementIntent,
|
|
256
317
|
},
|
|
257
318
|
delivery: {
|
|
258
319
|
mode: nonEmptyString(
|
|
@@ -61,6 +61,10 @@ 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
|
+
]);
|
|
64
68
|
const SUPPORTED_SIGNING_ARTIFACT_KINDS = new Set([
|
|
65
69
|
"auto", "binary", "archive", "blob", "directory", "mach-o", "app-bundle",
|
|
66
70
|
"framework-bundle", "plugin-bundle", "xpc-bundle", "dylib", "pkg", "dmg",
|
|
@@ -99,6 +103,27 @@ function normalizeStringArray(value, label) {
|
|
|
99
103
|
return value.map((entry, index) => assertString(entry, `${label}[${index}]`));
|
|
100
104
|
}
|
|
101
105
|
|
|
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
|
+
|
|
102
127
|
function getByDottedKey(target, key) {
|
|
103
128
|
return String(key)
|
|
104
129
|
.split(".")
|
|
@@ -362,7 +387,16 @@ function normalizeSigningSection(signing) {
|
|
|
362
387
|
const artifacts = signing.artifacts.map((artifact, index) => {
|
|
363
388
|
const label = `signing.artifacts[${index}]`;
|
|
364
389
|
assertPlainObject(artifact, label);
|
|
365
|
-
const allowedKeys = new Set([
|
|
390
|
+
const allowedKeys = new Set([
|
|
391
|
+
"id",
|
|
392
|
+
"path",
|
|
393
|
+
"profile",
|
|
394
|
+
"entitlements_profile",
|
|
395
|
+
"entitlements_paths",
|
|
396
|
+
"kind",
|
|
397
|
+
"platforms",
|
|
398
|
+
"required",
|
|
399
|
+
]);
|
|
366
400
|
for (const key of Object.keys(artifact)) {
|
|
367
401
|
if (!allowedKeys.has(key)) {
|
|
368
402
|
throw new Error(`${label}.${key} is not supported; declare desired signature state only`);
|
|
@@ -377,10 +411,41 @@ function normalizeSigningSection(signing) {
|
|
|
377
411
|
if (!SUPPORTED_SIGNING_ARTIFACT_KINDS.has(kind)) {
|
|
378
412
|
throw new Error(`${label}.kind is not a supported signing artifact kind`);
|
|
379
413
|
}
|
|
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
|
+
}
|
|
380
443
|
return {
|
|
381
444
|
id: artifact.id === undefined ? artifactPath : assertString(artifact.id, `${label}.id`),
|
|
382
445
|
path: artifactPath,
|
|
383
446
|
profile,
|
|
447
|
+
entitlementsProfile,
|
|
448
|
+
entitlementsPaths,
|
|
384
449
|
kind,
|
|
385
450
|
platforms: normalizeStringArray(artifact.platforms, `${label}.platforms`),
|
|
386
451
|
required: optionalBoolean(artifact.required, true),
|
|
@@ -1193,11 +1258,6 @@ function validateAnchoredDerivedVersionMaterialConfig(config) {
|
|
|
1193
1258
|
if (derivedFiles.length === 0) {
|
|
1194
1259
|
return;
|
|
1195
1260
|
}
|
|
1196
|
-
if (config.version.strategy !== "anchored" || config.version.next !== "manual") {
|
|
1197
|
-
throw new Error(
|
|
1198
|
-
"version.derived_files requires version.strategy = \"anchored\" and version.next = \"manual\"",
|
|
1199
|
-
);
|
|
1200
|
-
}
|
|
1201
1261
|
const versionState =
|
|
1202
1262
|
config.lifecycle?.["version-state"] ||
|
|
1203
1263
|
config.lifecycle?.version_state;
|
|
@@ -37,6 +37,10 @@ const DESCRIPTORS = Object.freeze([
|
|
|
37
37
|
[".github/workflows/dev-delivery-warrant-cancel.yml", "governance-write"],
|
|
38
38
|
[".github/workflows/dev-merge-queue-governance.yml", "governance-write"],
|
|
39
39
|
[".github/workflows/dev-pr-auto-merge.yml", "governance-write"],
|
|
40
|
+
[".github/workflows/engineering-housekeeper-daily.yml", "governance-write"],
|
|
41
|
+
[".github/workflows/engineering-housekeeper-monthly.yml", "governance-write"],
|
|
42
|
+
[".github/workflows/engineering-housekeeper-weekly.yml", "governance-write"],
|
|
43
|
+
[".github/workflows/engineering-housekeeper.yml", "governance-write"],
|
|
40
44
|
[".github/workflows/github-governance-audit.yml", "governance-write"],
|
|
41
45
|
[".github/workflows/github-artifact-attestation.yml", "evidence-publication", true, ["github-artifact-attestation"], "oidc", "consumer-defined", "caller-bound", "caller-bound"],
|
|
42
46
|
[".github/workflows/npm-publish.yml", "dry-run-only"],
|
|
@@ -34,13 +34,14 @@ const CONTROLLER_SPECS = [
|
|
|
34
34
|
"resolve-runtime",
|
|
35
35
|
"resolve-source",
|
|
36
36
|
"anchored-release-preflight",
|
|
37
|
+
"pre-build-contracts",
|
|
37
38
|
"build",
|
|
38
39
|
"signing-finalization",
|
|
39
40
|
"credential-island",
|
|
40
41
|
"verify",
|
|
41
42
|
"aggregate",
|
|
42
43
|
],
|
|
43
|
-
optionalStages: ["anchored-release-preflight", "credential-island"],
|
|
44
|
+
optionalStages: ["anchored-release-preflight", "pre-build-contracts", "credential-island"],
|
|
44
45
|
evidence: [
|
|
45
46
|
"platform-manifests",
|
|
46
47
|
"build-summary",
|