@kungfu-tech/buildchain 4.0.2-alpha.12 → 4.0.2-alpha.14
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/architecture/agent-change-map.md +4 -0
- package/architecture/maintainability-debt.json +20 -6
- package/architecture/maintainability-policy.json +12 -7
- package/architecture/v4-universal-workflow-train-admission.json +3 -3
- package/dist/site/buildchain-contract.json +2 -2
- package/dist/site/buildchain-site.json +4 -4
- package/dist/site/kfd-claims.json +2 -2
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/public-surface-audit.json +1 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +4 -4
- package/dist/site/workflow-registry.json +4 -4
- package/package.json +1 -1
- package/packages/core/dev-delivery-candidate-identity.js +18 -0
- package/packages/core/v4-universal-workflow-bootstrap.js +7 -3
- package/scripts/dev-delivery-warrant-store.mjs +30 -7
- package/scripts/dev-delivery-warrant.mjs +48 -46
- package/scripts/dev-pr-auto-merge.mjs +15 -15
- package/scripts/v4-universal-workflow-engine.mjs +56 -26
|
@@ -84,6 +84,7 @@ Safe change route: Reduce one owned responsibility behind the stable facade, run
|
|
|
84
84
|
- sources: `scripts/run-lifecycle-core.mjs`
|
|
85
85
|
- sources: `scripts/stable-candidate-patrol.mjs`
|
|
86
86
|
- sources: `scripts/stable-release-gate.mjs`
|
|
87
|
+
- sources: `scripts/v4-universal-workflow-engine.mjs`
|
|
87
88
|
- sources: `scripts/v4-architecture.mjs`
|
|
88
89
|
- sources: `scripts/v4-warrant-shadow-plan.mjs`
|
|
89
90
|
- sources: `scripts/web-surface-core.mjs`
|
|
@@ -152,12 +153,15 @@ Safe change route: Reduce one owned responsibility behind the stable facade, run
|
|
|
152
153
|
|
|
153
154
|
- `.github/workflows/bootstrap.yml`
|
|
154
155
|
- `scripts/check-v4-universal-workflow-bootstrap.mjs`
|
|
156
|
+
- `scripts/v4-universal-workflow-engine.mjs`
|
|
157
|
+
- `tests/v4-universal-workflow-bootstrap.test.mjs`
|
|
155
158
|
- `tests/v3-v4-capability-inventory.test.mjs`
|
|
156
159
|
- `tests/v4-floating-consumer-policy-contract.test.mjs`
|
|
157
160
|
- `tests/v4-publication-qualification.test.mjs`
|
|
158
161
|
- `.github/workflows/v4-adopter-delivery.yml`
|
|
159
162
|
- `.github/workflows/buildchain-ref-promotion.yml`
|
|
160
163
|
- `tests/release-candidate-recovery.test.mjs`
|
|
164
|
+
- `tests/buildchain-ref-promotion-recovery-entry.test.mjs`
|
|
161
165
|
- `.github/workflows/.release-candidate-promote.yml`
|
|
162
166
|
- `tests/build-surface-promotion.test.mjs`
|
|
163
167
|
- `.github/workflows/buildchain-ref-promotion-recovery.yml`
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
},
|
|
8
8
|
"totalExcessLedger": {
|
|
9
9
|
"metric": "calibrated-full-dimensional-excess-v1",
|
|
10
|
-
"measuredExcess":
|
|
11
|
-
"current":
|
|
10
|
+
"measuredExcess": 64361,
|
|
11
|
+
"current": 56300
|
|
12
12
|
},
|
|
13
13
|
"defaults": {
|
|
14
14
|
"owner": "Buildchain architecture maintainers",
|
|
@@ -1425,7 +1425,7 @@
|
|
|
1425
1425
|
"evaluatePullRequest@204": {
|
|
1426
1426
|
"complexity": 31
|
|
1427
1427
|
},
|
|
1428
|
-
"runDevPrAdmission@
|
|
1428
|
+
"runDevPrAdmission@767": {
|
|
1429
1429
|
"complexity": 32
|
|
1430
1430
|
},
|
|
1431
1431
|
"runDevPrAutoMerge@909": {
|
|
@@ -1440,7 +1440,7 @@
|
|
|
1440
1440
|
"evaluatePullRequest@204": {
|
|
1441
1441
|
"complexity": 31
|
|
1442
1442
|
},
|
|
1443
|
-
"runDevPrAdmission@
|
|
1443
|
+
"runDevPrAdmission@767": {
|
|
1444
1444
|
"complexity": 32
|
|
1445
1445
|
},
|
|
1446
1446
|
"runDevPrAutoMerge@909": {
|
|
@@ -1455,7 +1455,7 @@
|
|
|
1455
1455
|
"evaluatePullRequest@204": {
|
|
1456
1456
|
"complexity": 25
|
|
1457
1457
|
},
|
|
1458
|
-
"runDevPrAdmission@
|
|
1458
|
+
"runDevPrAdmission@767": {
|
|
1459
1459
|
"complexity": 25
|
|
1460
1460
|
},
|
|
1461
1461
|
"runDevPrAutoMerge@909": {
|
|
@@ -1804,6 +1804,17 @@
|
|
|
1804
1804
|
}
|
|
1805
1805
|
}
|
|
1806
1806
|
},
|
|
1807
|
+
"scripts/v4-universal-workflow-engine.mjs": {
|
|
1808
|
+
"baseline": {
|
|
1809
|
+
"lines": 620
|
|
1810
|
+
},
|
|
1811
|
+
"current": {
|
|
1812
|
+
"lines": 616
|
|
1813
|
+
},
|
|
1814
|
+
"target": {
|
|
1815
|
+
"lines": 600
|
|
1816
|
+
}
|
|
1817
|
+
},
|
|
1807
1818
|
"scripts/v4-architecture.mjs": {
|
|
1808
1819
|
"baseline": {
|
|
1809
1820
|
"lines": 610
|
|
@@ -2373,7 +2384,7 @@
|
|
|
2373
2384
|
"lines": 626
|
|
2374
2385
|
},
|
|
2375
2386
|
"current": {
|
|
2376
|
-
"lines":
|
|
2387
|
+
"lines": 624
|
|
2377
2388
|
},
|
|
2378
2389
|
"target": {
|
|
2379
2390
|
"lines": 600
|
|
@@ -3085,12 +3096,15 @@
|
|
|
3085
3096
|
"hotspots": [
|
|
3086
3097
|
".github/workflows/bootstrap.yml",
|
|
3087
3098
|
"scripts/check-v4-universal-workflow-bootstrap.mjs",
|
|
3099
|
+
"scripts/v4-universal-workflow-engine.mjs",
|
|
3100
|
+
"tests/v4-universal-workflow-bootstrap.test.mjs",
|
|
3088
3101
|
"tests/v3-v4-capability-inventory.test.mjs",
|
|
3089
3102
|
"tests/v4-floating-consumer-policy-contract.test.mjs",
|
|
3090
3103
|
"tests/v4-publication-qualification.test.mjs",
|
|
3091
3104
|
".github/workflows/v4-adopter-delivery.yml",
|
|
3092
3105
|
".github/workflows/buildchain-ref-promotion.yml",
|
|
3093
3106
|
"tests/release-candidate-recovery.test.mjs",
|
|
3107
|
+
"tests/buildchain-ref-promotion-recovery-entry.test.mjs",
|
|
3094
3108
|
".github/workflows/.release-candidate-promote.yml",
|
|
3095
3109
|
"tests/build-surface-promotion.test.mjs",
|
|
3096
3110
|
".github/workflows/buildchain-ref-promotion-recovery.yml",
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
"rationale": "Preserve the accepted 66,712 governance ledger at the exact protected Wave 4 cut while charging every subsequent source, Rust, test, and workflow line, function, job, step, and decision excess one-for-one. The fixed calibration offset cannot hide new debt because both the measured ledger and calibrated total are recomputed from the complete exact debt map."
|
|
14
14
|
},
|
|
15
15
|
"exceptionBudget": {
|
|
16
|
-
"maxTotal":
|
|
16
|
+
"maxTotal": 95,
|
|
17
17
|
"maxByCollection": {
|
|
18
18
|
"approvedExistingDebtTransitions": 19,
|
|
19
|
-
"approvedNewFileTransitions":
|
|
19
|
+
"approvedNewFileTransitions": 22,
|
|
20
20
|
"approvedExtractedDebt": 20,
|
|
21
21
|
"approvedPublicSurfaceTransitions": 19,
|
|
22
22
|
"approvedTestDebtTransitions": 9,
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"repositoryBudgets": {
|
|
28
28
|
"baselineRevision": "858dbe00b6206a8f356b4a12d2659bee7a72e71b",
|
|
29
29
|
"maxHandMaintainedSourceFiles": 462,
|
|
30
|
-
"maxHandMaintainedSourceLines":
|
|
30
|
+
"maxHandMaintainedSourceLines": 170347,
|
|
31
31
|
"maxWorkflowFiles": 80,
|
|
32
32
|
"maxWorkflowLines": 26791,
|
|
33
|
-
"latestTransition": "The
|
|
34
|
-
"latestTransitionRationale": "The
|
|
33
|
+
"latestTransition": "The proven r31 publication runtime is absorbed into protected dev without replacing later dev work.",
|
|
34
|
+
"latestTransitionRationale": "The linear Project Cut absorbs the complete reviewed Train r31 runtime delta while preserving the current protected dev tree, including later Warrant readback and protected product-finalization repairs; the immutable r31 SHA remains the evidence and publication-engine coordinate. The combined runtime keeps the single admitted universal engine, clean-consumer dependency bridge, source-bound recovery version, rooted result channel, and protected finalization ancestry without adding a second publisher, mutation authority, or release engine. The exact inventory contains 462 hand-maintained source files and 170347 lines; the ceiling binds the complete combined tree rather than hiding merge-time debt.",
|
|
35
35
|
"rationale": "Bounded modules separate candidate, evidence, native proof and execution, canonical failure verification, closed-set execution transfer, process-boundary verification, Warrant state and qualification, authority state, qualification, Landing acquisition, verified-attempt admission, Landing settlement and fence validation, provider attempt and heartbeat readback, sealed terminal evidence, hosted heartbeat, resume, store, options, command-specific adapters, and CLI orchestration responsibilities. The protected delivery workflow gives candidate and seal jobs no provider write credential, recursively rejects transfer drift, scopes every transfer artifact to the exact provider run attempt, chains every durable heartbeat state and receipt root on a distinct hosted runner domain, and permits qualification or exact failure settlement only after the finalizer verifies live jobs and the latest durable authority state. Public Landing admission and heartbeat always perform non-injectable fresh exact-attempt provider readback, tolerate only a not-yet-materialized seal job while retaining exact native and duplicate-job rejection, and heartbeat-loss coordination never invokes GitHub's run-scoped cancellation API where a successor rerun could race. The hosted process boundary anchors trust in the kernel-resolved versioned Runner.Worker path while accepting an equivalent argv-zero spelling with the same basename; its public boundary marker is allowed only at the one exact non-secret value, so a forged value under the same credential-like name still fails closed. The credentialless transfer seal validates the rooted native reuse decision against the Warrant-bound source, closure, dependency, toolchain, environment and command inputs; qualifiedBase stays in the proof and decision where it is rooted and later independently rechecked, rather than being assumed on the Warrant schema. V2 mutation cannot initialize a second empty authority beside a legacy ref: migration reads and roots the canonical live v1 ref, rechecks it, applies the explicit configured v2 policy, and atomically replaces its bytes through one expected-old non-force update, so old v1 controllers fail closed while the prior commit remains immutable history. Candidate ancestry classifies the GitHub artifact runtime token as credential authority. Every Landing settlement, including an unexpired fence, requires product-owned terminal readback. Terminal cleanup reads the immutable historical attempt, derives workflow and hosted Landing job identity from live run data, reads the exact protected ref head and compares containment against that immutable SHA, and seals merged only when the admitted merge-group head is contained by that protected base; a later different-head merge settles the old attempt as dequeued. Terminal normalization preserves transfer, finalizer boundary, native and seal job, admitted provider attempt, protected-base head, containment result, and terminal readback coordinates. The public reusable workflow truthfully retains its single-flight v1 authority without advertising v2 migration, while bounded v2 remains a separate opt-in CLI, Node API, and schema contract; the tracked self-caller stays on public v4-alpha with matching stable and alpha locks, and transient train selection is dispatch-only. Next-development parity adds one versioned transition authority, one durable controller, one bounded reservation adapter, and pure public-contract recovery evidence while retaining the existing thin floating-channel callers byte-for-byte. The floating-consumer gate reuses the existing contract-world evaluator so SHA drift passes only under the selected lock's compatibility policy, breaking surface drift still fails closed, and external certification reconstructs the receipt from the observed workflow SHA rather than the lock's historical evidence SHA. Tail reseal adds no Stage Capsule effect or general rebuild authority: it verifies the exact retained four-platform bytes, admits one known macOS tail failure through live provider readback, fences signing and finalization to explicit credentials and Warrant identity, and requires standard candidate Passport plus provider and protected readback. Compatibility Facts parity isolates canonical encoding, temporal verification, lifecycle validation, fixture projection, public authority projection, and CLI adaptation into bounded modules; legacy digest and proof arrays are derived only from exact current Fact lineage. Publication Rehearsal adds no production release authority: the content-addressed capsule contract binds source, candidate, manifest, config, canonical data-only provider bindings, provider policy, observations and core version separately from filesystem, runtime and rehearsal-only provider authority; simulate, replay and explicitly authorized provider rehearsal all reuse the release-tail planner, while same-commit local dogfood resolves the new reusable contract and exact current runtime before external callers remain on floating v4-alpha. Cross-runtime candidate recovery preserves original runtime and provider roots and creates resume lineage only after exact public ref, tag, ancestry, npm version and integrity readback. Adopter Delivery restores the reviewed public v3 gate, drivers, Passport binding, N-1 bootstrap and exact archive loader through one public v4 surface while keeping provider and release authority explicit non-claims. Floating Alpha bootstrap uses only the existing exact resolver and protected Alpha private shell to move an otherwise stale floating shell, retains public v4 stable promotion, and removes itself after protected readback. CI lane change-budget enforcement adds a bounded parser, evaluator, guard, and tests without creating another merge authority.",
|
|
36
36
|
"governance": "legacy-debt-sunset-2026q4"
|
|
37
37
|
},
|
|
@@ -144,6 +144,11 @@
|
|
|
144
144
|
"rationale": "The isolated source scanner rejects persisted exact, train, authority, variable, secret, and environment selector authority while inventorying OIDC, IAM, and external authority metadata without reading credentials; this exact ceiling cannot grow implicitly.",
|
|
145
145
|
"governance": "legacy-debt-sunset-2026q4"
|
|
146
146
|
},
|
|
147
|
+
"scripts/v4-universal-workflow-engine.mjs": {
|
|
148
|
+
"maxLines": 620,
|
|
149
|
+
"rationale": "The exact candidate engine adds only a fail-closed OIDC preflight and a fixed npm 11.13.0 trusted-publishing shim under the already admitted ephemeral runtime path. This lets a reviewed Train candidate execute through an older floating Alpha Bootstrap shell without broadening capability, permission, provider, or mutation authority; this exact live-repair ceiling cannot grow implicitly.",
|
|
150
|
+
"governance": "legacy-debt-sunset-2026q4"
|
|
151
|
+
},
|
|
147
152
|
"scripts/resume-from-candidate-run.mjs": {
|
|
148
153
|
"maxLines": 1125,
|
|
149
154
|
"rationale": "The candidate recovery adapter retains the existing artifact normalization and immutable recovery receipt boundary while adding fresh authorized cross-runtime Stage Capsule resume material, post-publication ref, tag, ancestry, and npm readback, and rooted Release Passport lineage. Its main orchestration remains below the default function length and complexity limits, and this exact file ceiling cannot grow implicitly.",
|
|
@@ -1851,8 +1856,8 @@
|
|
|
1851
1856
|
"governance": "stage1-test-split-net-reduction"
|
|
1852
1857
|
},
|
|
1853
1858
|
"tests/dev-delivery-warrant-command.test.mjs": {
|
|
1854
|
-
"maxLines":
|
|
1855
|
-
"rationale": "
|
|
1859
|
+
"maxLines": 624,
|
|
1860
|
+
"rationale": "Exact command-store assertions cover raw GitHub media reads and JSON-envelope fallback for persisted Dev Delivery state. The test surface has reduced to an exact 624-line ceiling and cannot grow implicitly.",
|
|
1856
1861
|
"governance": "legacy-debt-sunset-2026q4"
|
|
1857
1862
|
},
|
|
1858
1863
|
"tests/build-surface-reusable-build.test.mjs": {
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
"statuses": "write"
|
|
24
24
|
},
|
|
25
25
|
"contractRoots": [
|
|
26
|
-
"sha256:
|
|
26
|
+
"sha256:51792fe2928fe9851f717728f4820b70029a52d04804246d87242e9e482fbb18",
|
|
27
27
|
"sha256:60fc0802b7e46cb4460264b8b7359d06b2406d66a9d805d0d076bb692f80b35e",
|
|
28
28
|
"sha256:65a0c2fc3faecaddf5076fff086b5d6e12eb9ba27796efc10aed19f2d93b9457",
|
|
29
|
-
"sha256:
|
|
30
|
-
"sha256:
|
|
29
|
+
"sha256:7ca5dffff9eaff8b9265d35c7d8573b8cedd56a69dc26c12e4a5c26e723412b0",
|
|
30
|
+
"sha256:83d6b141d3c60ac5b861029a7c6241ee0ca8e6674dd346d27d476151dd16ea6c",
|
|
31
31
|
"sha256:b6737ed958e8f53f7770b1c214c092591573ad63af41e05797117d9b7b212ed8",
|
|
32
32
|
"sha256:d6ad9cf661c0ba3825cabdc7185fd122a60fc1929dfc799cda3352650588663f"
|
|
33
33
|
],
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"product": {
|
|
5
5
|
"name": "Buildchain",
|
|
6
6
|
"package": "@kungfu-tech/buildchain",
|
|
7
|
-
"version": "4.0.2-alpha.
|
|
7
|
+
"version": "4.0.2-alpha.14",
|
|
8
8
|
"repository": "https://github.com/kungfu-systems/buildchain"
|
|
9
9
|
},
|
|
10
10
|
"majorLine": "v4",
|
|
@@ -5444,5 +5444,5 @@
|
|
|
5444
5444
|
"compatibilityFactCutRoot": "sha256:f61b975d4a96c921cc4bd37fb3ed9a0596b47004fe34dc7219979d697e3707f9",
|
|
5445
5445
|
"compatibilityProofRegistryRoot": "sha256:b7194b811e4d3af2df4390bc4d1731e16a86215bd64ee18cb630dd5492f4fb63",
|
|
5446
5446
|
"compatibilityDigest": "sha256:20411029d83211dff426fb6535a608902129844a5ccb4788db77d3f81becb839",
|
|
5447
|
-
"contractDigest": "sha256:
|
|
5447
|
+
"contractDigest": "sha256:a7fa27e20846c32f1ce22a0c37cc32b47818b3b30d72640c0d608c2b67d323c6"
|
|
5448
5448
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"contract": "kungfu-buildchain-site-bundle",
|
|
4
|
-
"generatedAt": "2026-09-
|
|
5
|
-
"publishedAt": "2026-09-
|
|
4
|
+
"generatedAt": "2026-09-01T14:47:21.000Z",
|
|
5
|
+
"publishedAt": "2026-09-01T14:47:21.000Z",
|
|
6
6
|
"reproducible": true,
|
|
7
7
|
"timestampPolicy": "ci-injected",
|
|
8
8
|
"deterministicInputs": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"declared Buildchain surface manifest contract"
|
|
20
20
|
],
|
|
21
21
|
"sourceDateEpoch": "0",
|
|
22
|
-
"sourceRevision": "
|
|
22
|
+
"sourceRevision": "94a4c73e3bc7fa20c4a1bcf732bd00c4593fd5d3",
|
|
23
23
|
"timestampPolicyDetails": {
|
|
24
24
|
"contract": "kungfu-buildchain-surface-timestamp-policy",
|
|
25
25
|
"timestampFields": [
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"package": {
|
|
39
39
|
"name": "@kungfu-tech/buildchain",
|
|
40
|
-
"version": "4.0.2-alpha.
|
|
40
|
+
"version": "4.0.2-alpha.14",
|
|
41
41
|
"versionSource": "package.json#version"
|
|
42
42
|
},
|
|
43
43
|
"source": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"contract": "kungfu-buildchain-public-surface-reverse-audit",
|
|
22
22
|
"path": "dist/site/public-surface-audit.json",
|
|
23
23
|
"status": "passed",
|
|
24
|
-
"sha256": "
|
|
24
|
+
"sha256": "444c6427bf82c74fe401cf9fbedddd96be6e016260ea53d77b4e091d1718e812",
|
|
25
25
|
"summary": {
|
|
26
26
|
"cliCommandCount": 117,
|
|
27
27
|
"workflowCount": 80,
|
|
@@ -246,7 +246,7 @@
|
|
|
246
246
|
"contract": "kungfu-buildchain-public-surface-reverse-audit",
|
|
247
247
|
"path": "dist/site/public-surface-audit.json",
|
|
248
248
|
"status": "passed",
|
|
249
|
-
"sha256": "
|
|
249
|
+
"sha256": "444c6427bf82c74fe401cf9fbedddd96be6e016260ea53d77b4e091d1718e812",
|
|
250
250
|
"summary": {
|
|
251
251
|
"cliCommandCount": 117,
|
|
252
252
|
"workflowCount": 80,
|
|
@@ -9035,7 +9035,7 @@
|
|
|
9035
9035
|
"workflowRegistryPath": "dist/site/workflow-registry.json",
|
|
9036
9036
|
"pageRegistryPath": "dist/site/page-registry.json",
|
|
9037
9037
|
"cliRegistryDigest": "fbc23073bbae240489a2f826b2c26daadc58e704050c5f8df366509f5c5a1c3e",
|
|
9038
|
-
"workflowRegistryDigest": "
|
|
9038
|
+
"workflowRegistryDigest": "8f19cd149a2c8c6a2a4931b7c5fc13c82a7fa3b03aef1676eb28914fd9d1fd2e",
|
|
9039
9039
|
"pageRegistryDigest": "8e2a30416d969f390d1fa57468c8d29f5e89d179b6d1ea1600159e72adff8130"
|
|
9040
9040
|
},
|
|
9041
9041
|
"comparison": {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"contract": "kungfu-buildchain-publication-release-registry",
|
|
4
|
-
"generatedAt": "2026-09-
|
|
5
|
-
"publishedAt": "2026-09-
|
|
4
|
+
"generatedAt": "2026-09-01T14:47:21.000Z",
|
|
5
|
+
"publishedAt": "2026-09-01T14:47:21.000Z",
|
|
6
6
|
"reproducible": true,
|
|
7
7
|
"timestampPolicy": "ci-injected",
|
|
8
8
|
"deterministicInputs": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"declared Buildchain surface manifest contract"
|
|
20
20
|
],
|
|
21
21
|
"sourceDateEpoch": "0",
|
|
22
|
-
"sourceRevision": "
|
|
22
|
+
"sourceRevision": "94a4c73e3bc7fa20c4a1bcf732bd00c4593fd5d3",
|
|
23
23
|
"timestampPolicyDetails": {
|
|
24
24
|
"contract": "kungfu-buildchain-surface-timestamp-policy",
|
|
25
25
|
"timestampFields": [
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"package": {
|
|
34
34
|
"name": "@kungfu-tech/buildchain",
|
|
35
|
-
"version": "4.0.2-alpha.
|
|
35
|
+
"version": "4.0.2-alpha.14",
|
|
36
36
|
"versionSource": "package.json#version"
|
|
37
37
|
},
|
|
38
38
|
"sourceKind": "package-site-bundle",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"contract": "kungfu-buildchain-site-manifest",
|
|
4
|
-
"generatedAt": "2026-09-
|
|
5
|
-
"publishedAt": "2026-09-
|
|
4
|
+
"generatedAt": "2026-09-01T14:47:21.000Z",
|
|
5
|
+
"publishedAt": "2026-09-01T14:47:21.000Z",
|
|
6
6
|
"reproducible": true,
|
|
7
7
|
"timestampPolicy": "ci-injected",
|
|
8
8
|
"deterministicInputs": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"declared Buildchain surface manifest contract"
|
|
20
20
|
],
|
|
21
21
|
"sourceDateEpoch": "0",
|
|
22
|
-
"sourceRevision": "
|
|
22
|
+
"sourceRevision": "94a4c73e3bc7fa20c4a1bcf732bd00c4593fd5d3",
|
|
23
23
|
"timestampPolicyDetails": {
|
|
24
24
|
"contract": "kungfu-buildchain-surface-timestamp-policy",
|
|
25
25
|
"timestampFields": [
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"package": {
|
|
39
39
|
"name": "@kungfu-tech/buildchain",
|
|
40
|
-
"version": "4.0.2-alpha.
|
|
40
|
+
"version": "4.0.2-alpha.14",
|
|
41
41
|
"versionSource": "package.json#version"
|
|
42
42
|
},
|
|
43
43
|
"entrypoint": "buildchain-site.json",
|
|
@@ -2096,7 +2096,7 @@
|
|
|
2096
2096
|
"status": "preview",
|
|
2097
2097
|
"owner": "buildchain-workflows",
|
|
2098
2098
|
"maturity": "preview",
|
|
2099
|
-
"introducedVersion": "4.0.2-alpha.
|
|
2099
|
+
"introducedVersion": "4.0.2-alpha.14",
|
|
2100
2100
|
"compatibilityPromise": "preserved-through-the-v3-major-line",
|
|
2101
2101
|
"deprecationReplacement": "",
|
|
2102
2102
|
"sunsetCondition": "explicit-breaking-change-review-in-a-future-major-line",
|
|
@@ -2117,7 +2117,7 @@
|
|
|
2117
2117
|
"status": "preview",
|
|
2118
2118
|
"owner": "buildchain-workflows",
|
|
2119
2119
|
"maturity": "preview",
|
|
2120
|
-
"introducedVersion": "4.0.2-alpha.
|
|
2120
|
+
"introducedVersion": "4.0.2-alpha.14",
|
|
2121
2121
|
"compatibilityPromise": "preserved-through-the-v3-major-line",
|
|
2122
2122
|
"deprecationReplacement": "",
|
|
2123
2123
|
"sunsetCondition": "explicit-breaking-change-review-in-a-future-major-line",
|
|
@@ -2138,7 +2138,7 @@
|
|
|
2138
2138
|
"status": "preview",
|
|
2139
2139
|
"owner": "buildchain-workflows",
|
|
2140
2140
|
"maturity": "preview",
|
|
2141
|
-
"introducedVersion": "4.0.2-alpha.
|
|
2141
|
+
"introducedVersion": "4.0.2-alpha.14",
|
|
2142
2142
|
"compatibilityPromise": "preserved-through-the-v3-major-line",
|
|
2143
2143
|
"deprecationReplacement": "",
|
|
2144
2144
|
"sunsetCondition": "explicit-breaking-change-review-in-a-future-major-line",
|
|
@@ -2189,7 +2189,7 @@
|
|
|
2189
2189
|
"status": "preview",
|
|
2190
2190
|
"owner": "buildchain-workflows",
|
|
2191
2191
|
"maturity": "preview",
|
|
2192
|
-
"introducedVersion": "4.0.2-alpha.
|
|
2192
|
+
"introducedVersion": "4.0.2-alpha.14",
|
|
2193
2193
|
"compatibilityPromise": "preserved-through-the-v3-major-line",
|
|
2194
2194
|
"deprecationReplacement": "",
|
|
2195
2195
|
"sunsetCondition": "explicit-breaking-change-review-in-a-future-major-line",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "4.0.2-alpha.
|
|
3
|
+
"version": "4.0.2-alpha.14",
|
|
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",
|
|
@@ -38,6 +38,24 @@ export function matchesExactDevDeliveryCandidate(existing, attempted) {
|
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
export function reuseExactActiveDevDeliverySourceProof(active, input) {
|
|
42
|
+
const exact =
|
|
43
|
+
["provisional", "qualified"].includes(active?.phase) &&
|
|
44
|
+
[
|
|
45
|
+
"pullRequestNumber",
|
|
46
|
+
"assignmentRoot",
|
|
47
|
+
"initiativeRoot",
|
|
48
|
+
"sourceIdentityRoot",
|
|
49
|
+
"deliveryClass",
|
|
50
|
+
"priority",
|
|
51
|
+
].every((field) => active[field] === input[field]) &&
|
|
52
|
+
matchesExactDevDeliveryCandidate(active, {
|
|
53
|
+
...input,
|
|
54
|
+
sourceProofRoot: active.sourceProofRoot,
|
|
55
|
+
});
|
|
56
|
+
return exact ? { ...input, sourceProofRoot: active.sourceProofRoot } : input;
|
|
57
|
+
}
|
|
58
|
+
|
|
41
59
|
export function createDevDeliveryCandidateIdentity(
|
|
42
60
|
input,
|
|
43
61
|
expected,
|
|
@@ -8,7 +8,6 @@ export const V4_UNIVERSAL_WORKFLOW_ADMISSION =
|
|
|
8
8
|
"kungfu-buildchain-v4-universal-workflow-admission/v1";
|
|
9
9
|
export const V4_UNIVERSAL_WORKFLOW_TERMINAL_RECEIPT =
|
|
10
10
|
"kungfu-buildchain-v4-universal-workflow-terminal-receipt/v1";
|
|
11
|
-
|
|
12
11
|
const EXACT_SHA = /^[0-9a-f]{40}$/u;
|
|
13
12
|
const ROOT = /^sha256:[0-9a-f]{64}$/u;
|
|
14
13
|
const REPOSITORY = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u;
|
|
@@ -36,7 +35,6 @@ const LEVELS = new Map([
|
|
|
36
35
|
["read", 1],
|
|
37
36
|
["write", 2],
|
|
38
37
|
]);
|
|
39
|
-
|
|
40
38
|
function fail(code, message) {
|
|
41
39
|
const error = new Error(message);
|
|
42
40
|
error.code = code;
|
|
@@ -275,7 +273,13 @@ export function v4UniversalWorkflowRequestRoot(value) {
|
|
|
275
273
|
validateV4UniversalWorkflowRequest(value),
|
|
276
274
|
);
|
|
277
275
|
}
|
|
278
|
-
|
|
276
|
+
export function v4ProductStateVersion(value, requestedSha) { const sourceSha = exactSha(requestedSha, "requestedSha"), ref = nonEmpty(value?.ref, "productStateRef.ref"), prefix = `refs/heads/buildchain/v4-product-state/${sourceSha}-`; if (!ref.startsWith(prefix)) fail("recovery-state-mismatch", "release recovery product state does not bind the requested source"); const match = ref.slice(prefix.length).match(/^(\d+)-(\d+)-(\d+)(?:-alpha-(\d+))?$/u); if (!match) fail("invalid-recovery-version", "release recovery product state version is invalid"); return `${match[1]}.${match[2]}.${match[3]}${match[4] === undefined ? "" : `-alpha.${match[4]}`}`; }
|
|
277
|
+
export function selectV4RecoveredProductPublicationVersion(value) {
|
|
278
|
+
if (value.routeDecision !== "Resume") return ""; const version = nonEmpty(value.candidateVersion, "candidateVersion"), sourceSha = exactSha(value.requestedSha, "requestedSha"), candidate = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-alpha\.(\d+))?$/u); if (!candidate) fail("invalid-recovery-version", "release recovery candidate version is invalid"); if (value.explicitResume) return version;
|
|
279
|
+
const states = Array.isArray(value.recoveryStates) ? value.recoveryStates : []; if (states.length) { const recovered = states.map(({ stateRef, stateCommit, exactTagRef }) => { const selected = v4ProductStateVersion(stateRef, sourceSha), parsed = selected.match(/^(\d+)\.(\d+)\.(\d+)(?:-alpha\.(\d+))?$/u), parents = stateCommit?.parents || []; if (stateRef?.object?.type !== "commit" || exactSha(stateRef?.object?.sha, "productStateRef.object.sha") !== exactSha(stateCommit?.sha, "productStateCommit.sha") || parents.length !== 1 || exactSha(parents[0]?.sha, "productStateCommit.parents[0].sha") !== sourceSha) fail("recovery-state-mismatch", "release recovery product state commit does not bind the requested source"); if (exactTagRef && (exactTagRef.ref !== `refs/tags/v${selected}` || exactTagRef.object?.type !== "commit" || exactSha(exactTagRef.object?.sha, "exactTagRef.object.sha") !== sourceSha)) fail("recovery-tag-mismatch", "release recovery exact tag does not bind the requested source"); if (parsed[1] !== candidate[1] || parsed[2] !== candidate[2] || parsed[3] !== candidate[3] || (parsed[4] === undefined) !== (candidate[4] === undefined)) fail("recovery-state-mismatch", "release recovery product state is outside the candidate release line"); return selected; }); recovered.sort((left, right) => Number(left.match(/(?:-alpha\.)?(\d+)$/u)[1]) - Number(right.match(/(?:-alpha\.)?(\d+)$/u)[1])); return recovered.at(-1); }
|
|
280
|
+
if (!value.exactTagRef) fail("recovery-tag-missing", "release recovery requires an exact partial-publication tag"); if (value.exactTagRef.ref !== `refs/tags/v${version}` || value.exactTagRef.object?.type !== "commit" || exactSha(value.exactTagRef.object?.sha, "exactTagRef.object.sha") !== sourceSha) fail("recovery-tag-mismatch", "release recovery exact tag does not bind the requested source");
|
|
281
|
+
return version;
|
|
282
|
+
}
|
|
279
283
|
function validatePolicy(value) {
|
|
280
284
|
exactKeys(
|
|
281
285
|
value,
|
|
@@ -35,11 +35,10 @@ function encodeRef(ref) {
|
|
|
35
35
|
return ref.split("/").map(encodeURIComponent).join("/");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
function
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
);
|
|
38
|
+
function decodeGitHubBlobEnvelope(raw) {
|
|
39
|
+
if (!/"encoding"\s*:\s*"base64"/.test(raw)) return raw;
|
|
40
|
+
const blob = JSON.parse(raw);
|
|
41
|
+
if (blob?.encoding !== "base64") return raw;
|
|
43
42
|
return Buffer.from(
|
|
44
43
|
String(blob.content || "").replace(/\s+/g, ""),
|
|
45
44
|
"base64",
|
|
@@ -108,6 +107,30 @@ export class GitHubDevDeliveryStore {
|
|
|
108
107
|
return data;
|
|
109
108
|
}
|
|
110
109
|
|
|
110
|
+
async requestRaw(method, requestPath) {
|
|
111
|
+
const response = await this.fetch(`${this.apiUrl}${requestPath}`, {
|
|
112
|
+
method,
|
|
113
|
+
headers: {
|
|
114
|
+
accept: "application/vnd.github.raw+json",
|
|
115
|
+
authorization: `Bearer ${this.token}`,
|
|
116
|
+
"x-github-api-version": "2022-11-28",
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
const raw = await response.text();
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
let message = raw || `${method} ${requestPath} failed`;
|
|
122
|
+
try {
|
|
123
|
+
message = JSON.parse(raw)?.message || message;
|
|
124
|
+
} catch {
|
|
125
|
+
// Raw blob responses are not required to be JSON.
|
|
126
|
+
}
|
|
127
|
+
const error = new Error(message);
|
|
128
|
+
error.status = response.status;
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
return raw;
|
|
132
|
+
}
|
|
133
|
+
|
|
111
134
|
async read({ stateRef, protectedBase, now, allowLegacyV3Readback = false }) {
|
|
112
135
|
let ref;
|
|
113
136
|
try {
|
|
@@ -149,11 +172,11 @@ export class GitHubDevDeliveryStore {
|
|
|
149
172
|
);
|
|
150
173
|
if (!entry?.sha)
|
|
151
174
|
throw new Error(`${commitSha} does not contain ${STATE_PATH}`);
|
|
152
|
-
const
|
|
175
|
+
const raw = await this.requestRaw(
|
|
153
176
|
"GET",
|
|
154
177
|
`/repos/${this.repository.fullName}/git/blobs/${entry.sha}`,
|
|
155
178
|
);
|
|
156
|
-
const bytes =
|
|
179
|
+
const bytes = decodeGitHubBlobEnvelope(raw);
|
|
157
180
|
const queue = validateStoredState(JSON.parse(bytes), {
|
|
158
181
|
allowLegacyV3Readback,
|
|
159
182
|
});
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
submitDevDeliveryCandidate,
|
|
18
18
|
} from "../packages/core/dev-delivery-warrant.js";
|
|
19
19
|
import { recoverLegacyTerminalDevDeliveryQueue } from "../packages/core/dev-delivery-warrant-legacy-recovery.js";
|
|
20
|
+
import { reuseExactActiveDevDeliverySourceProof } from "../packages/core/dev-delivery-candidate-identity.js";
|
|
20
21
|
import { runV4DeliveryWarrantReadCandidate } from "../packages/core/v4-delivery-warrant-read-candidate.js";
|
|
21
22
|
|
|
22
23
|
import { GitHubDevDeliveryStore } from "./dev-delivery-warrant-store.mjs";
|
|
@@ -199,54 +200,55 @@ function transitionFor(command, queue, options) {
|
|
|
199
200
|
"native command contract root does not match native-command",
|
|
200
201
|
);
|
|
201
202
|
}
|
|
203
|
+
const input = {
|
|
204
|
+
pullRequestNumber: positiveInteger(
|
|
205
|
+
options.pullRequestNumber,
|
|
206
|
+
"pullRequestNumber",
|
|
207
|
+
),
|
|
208
|
+
sourceHead: exactSha(options.sourceHead, "sourceHead"),
|
|
209
|
+
assignmentRoot: exactRoot(options.assignmentRoot, "assignmentRoot"),
|
|
210
|
+
initiativeRoot: exactRoot(options.initiativeRoot, "initiativeRoot"),
|
|
211
|
+
sourceIdentityRoot: exactRoot(
|
|
212
|
+
options.sourceIdentityRoot,
|
|
213
|
+
"sourceIdentityRoot",
|
|
214
|
+
),
|
|
215
|
+
sourcePatchRoot: exactRoot(options.sourcePatchRoot, "sourcePatchRoot"),
|
|
216
|
+
sourceProofRoot: exactRoot(options.sourceProofRoot, "sourceProofRoot"),
|
|
217
|
+
planRoot: exactRoot(options.planRoot, "planRoot"),
|
|
218
|
+
closureRoot: exactRoot(options.closureRoot, "closureRoot"),
|
|
219
|
+
dependencyRoot: exactRoot(options.dependencyRoot, "dependencyRoot"),
|
|
220
|
+
toolchainRoot: exactRoot(options.toolchainRoot, "toolchainRoot"),
|
|
221
|
+
...(options.environmentRoot
|
|
222
|
+
? {
|
|
223
|
+
environmentRoot: exactRoot(
|
|
224
|
+
options.environmentRoot,
|
|
225
|
+
"environmentRoot",
|
|
226
|
+
),
|
|
227
|
+
}
|
|
228
|
+
: {}),
|
|
229
|
+
...(nativeCommandContract ? { nativeCommandContract } : {}),
|
|
230
|
+
affectedPaths: jsonList(options.affectedPaths, "affected paths"),
|
|
231
|
+
shardEvidenceRoots: jsonList(
|
|
232
|
+
options.shardEvidenceRoots,
|
|
233
|
+
"shard evidence roots",
|
|
234
|
+
),
|
|
235
|
+
sourceWorkflowRunId: options.sourceWorkflowRunId
|
|
236
|
+
? positiveInteger(options.sourceWorkflowRunId, "sourceWorkflowRunId")
|
|
237
|
+
: 0,
|
|
238
|
+
deliveryClass: options.deliveryClass,
|
|
239
|
+
priority: options.priority || "ordinary",
|
|
240
|
+
...(options.releaseBlockerPriority
|
|
241
|
+
? {
|
|
242
|
+
releaseBlockerPriority: jsonObject(
|
|
243
|
+
options.releaseBlockerPriority,
|
|
244
|
+
"release blocker priority",
|
|
245
|
+
),
|
|
246
|
+
}
|
|
247
|
+
: {}),
|
|
248
|
+
};
|
|
202
249
|
return submitDevDeliveryCandidate(
|
|
203
250
|
queue,
|
|
204
|
-
|
|
205
|
-
pullRequestNumber: positiveInteger(
|
|
206
|
-
options.pullRequestNumber,
|
|
207
|
-
"pullRequestNumber",
|
|
208
|
-
),
|
|
209
|
-
sourceHead: exactSha(options.sourceHead, "sourceHead"),
|
|
210
|
-
assignmentRoot: exactRoot(options.assignmentRoot, "assignmentRoot"),
|
|
211
|
-
initiativeRoot: exactRoot(options.initiativeRoot, "initiativeRoot"),
|
|
212
|
-
sourceIdentityRoot: exactRoot(
|
|
213
|
-
options.sourceIdentityRoot,
|
|
214
|
-
"sourceIdentityRoot",
|
|
215
|
-
),
|
|
216
|
-
sourcePatchRoot: exactRoot(options.sourcePatchRoot, "sourcePatchRoot"),
|
|
217
|
-
sourceProofRoot: exactRoot(options.sourceProofRoot, "sourceProofRoot"),
|
|
218
|
-
planRoot: exactRoot(options.planRoot, "planRoot"),
|
|
219
|
-
closureRoot: exactRoot(options.closureRoot, "closureRoot"),
|
|
220
|
-
dependencyRoot: exactRoot(options.dependencyRoot, "dependencyRoot"),
|
|
221
|
-
toolchainRoot: exactRoot(options.toolchainRoot, "toolchainRoot"),
|
|
222
|
-
...(options.environmentRoot
|
|
223
|
-
? {
|
|
224
|
-
environmentRoot: exactRoot(
|
|
225
|
-
options.environmentRoot,
|
|
226
|
-
"environmentRoot",
|
|
227
|
-
),
|
|
228
|
-
}
|
|
229
|
-
: {}),
|
|
230
|
-
...(nativeCommandContract ? { nativeCommandContract } : {}),
|
|
231
|
-
affectedPaths: jsonList(options.affectedPaths, "affected paths"),
|
|
232
|
-
shardEvidenceRoots: jsonList(
|
|
233
|
-
options.shardEvidenceRoots,
|
|
234
|
-
"shard evidence roots",
|
|
235
|
-
),
|
|
236
|
-
sourceWorkflowRunId: options.sourceWorkflowRunId
|
|
237
|
-
? positiveInteger(options.sourceWorkflowRunId, "sourceWorkflowRunId")
|
|
238
|
-
: 0,
|
|
239
|
-
deliveryClass: options.deliveryClass,
|
|
240
|
-
priority: options.priority || "ordinary",
|
|
241
|
-
...(options.releaseBlockerPriority
|
|
242
|
-
? {
|
|
243
|
-
releaseBlockerPriority: jsonObject(
|
|
244
|
-
options.releaseBlockerPriority,
|
|
245
|
-
"release blocker priority",
|
|
246
|
-
),
|
|
247
|
-
}
|
|
248
|
-
: {}),
|
|
249
|
-
},
|
|
251
|
+
reuseExactActiveDevDeliverySourceProof(queue.activeWarrant, input),
|
|
250
252
|
{ now: options.now },
|
|
251
253
|
);
|
|
252
254
|
}
|
|
@@ -723,7 +723,6 @@ function renderAdmissionComment(receipt, receiptRoot) {
|
|
|
723
723
|
"GitHub auto-merge state is observed evidence only; it is not Buildchain admission authority.",
|
|
724
724
|
].join("\n");
|
|
725
725
|
}
|
|
726
|
-
|
|
727
726
|
async function publishAdmissionDiagnostic(client, options, receipt, receiptRoot) {
|
|
728
727
|
const marker = `<!-- ${AGENT_ADMISSION_MARKER} pr=${receipt.pullRequestNumber} head=${receipt.expectedHeadSha} -->`;
|
|
729
728
|
const body = renderAdmissionComment(receipt, receiptRoot);
|
|
@@ -743,7 +742,6 @@ async function publishAdmissionDiagnostic(client, options, receipt, receiptRoot)
|
|
|
743
742
|
}
|
|
744
743
|
return { commentId: comment?.id || null, commentUrl: comment?.html_url || "", statusPublished: Boolean(status) };
|
|
745
744
|
}
|
|
746
|
-
|
|
747
745
|
function createAdmissionReceipt({ options, pr = {}, state, reason, readiness, decision = {}, queue = null, warrant = null }) {
|
|
748
746
|
return createDevPrAdmissionReceipt({ options, pr, state, reason, readiness, decision, queue, warrant, labels: labelsOf(pr), nextAction: nextAdmissionAction });
|
|
749
747
|
}
|
|
@@ -751,16 +749,21 @@ function createAdmissionReceipt({ options, pr = {}, state, reason, readiness, de
|
|
|
751
749
|
function targetedFailure({ options, pr, state, reason, readiness, decision, queue }) {
|
|
752
750
|
const receipt = createAdmissionReceipt({ options, pr, state, reason, readiness, decision, queue });
|
|
753
751
|
return {
|
|
754
|
-
schema: AGENT_ADMISSION_RESULT_SCHEMA,
|
|
755
|
-
|
|
756
|
-
mode: options.dryRun ? "plan" : "execute",
|
|
757
|
-
outcome: "targeted-admission-failed",
|
|
758
|
-
receipt,
|
|
759
|
-
receiptRoot: contentRoot(receipt),
|
|
760
|
-
diagnostic: null,
|
|
752
|
+
schema: AGENT_ADMISSION_RESULT_SCHEMA, ok: false, mode: options.dryRun ? "plan" : "execute",
|
|
753
|
+
outcome: "targeted-admission-failed", receipt, receiptRoot: contentRoot(receipt), diagnostic: null,
|
|
761
754
|
};
|
|
762
755
|
}
|
|
763
|
-
|
|
756
|
+
async function readTargetPullRequest(client, number, options) {
|
|
757
|
+
let { pollMergeableAttempts: attempts, pollMergeableDelayMs: delayMs } = options;
|
|
758
|
+
for (; attempts > 1; attempts -= 1) {
|
|
759
|
+
try {
|
|
760
|
+
return await client.getPullRequest(number, { attempts, delayMs });
|
|
761
|
+
} catch {
|
|
762
|
+
await delay(delayMs);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return client.getPullRequest(number, { attempts, delayMs });
|
|
766
|
+
}
|
|
764
767
|
export async function runDevPrAdmission(optionsInput = {}, clientInput) {
|
|
765
768
|
const options = normalizeOptions(optionsInput);
|
|
766
769
|
if (!options.targetBranch) throw new Error("target branch is required");
|
|
@@ -776,12 +779,9 @@ export async function runDevPrAdmission(optionsInput = {}, clientInput) {
|
|
|
776
779
|
|
|
777
780
|
let pr;
|
|
778
781
|
try {
|
|
779
|
-
pr = await client
|
|
780
|
-
attempts: options.pollMergeableAttempts,
|
|
781
|
-
delayMs: options.pollMergeableDelayMs,
|
|
782
|
-
});
|
|
782
|
+
pr = await readTargetPullRequest(client, options.targetPullRequestNumber, options);
|
|
783
783
|
} catch (error) {
|
|
784
|
-
return targetedFailure({ options, pr: {}, state: "
|
|
784
|
+
return targetedFailure({ options, pr: {}, state: "blocked", reason: "pull-request-read-failed" });
|
|
785
785
|
}
|
|
786
786
|
|
|
787
787
|
const readinessFence = createPreReadinessQueueFence({ client, options, sleep: delay });
|
|
@@ -6,6 +6,7 @@ import { execFileSync } from "node:child_process";
|
|
|
6
6
|
import {
|
|
7
7
|
admitV4UniversalWorkflow,
|
|
8
8
|
completeV4UniversalWorkflow,
|
|
9
|
+
selectV4RecoveredProductPublicationVersion, v4ProductStateVersion,
|
|
9
10
|
validateV4UniversalWorkflowRequest,
|
|
10
11
|
v4UniversalWorkflowRequestRoot,
|
|
11
12
|
} from "../packages/core/v4-universal-workflow-bootstrap.js";
|
|
@@ -24,7 +25,6 @@ import {
|
|
|
24
25
|
function fail(message) {
|
|
25
26
|
throw new Error(message);
|
|
26
27
|
}
|
|
27
|
-
|
|
28
28
|
function readJsonEnvironment(name) {
|
|
29
29
|
const source = process.env[name];
|
|
30
30
|
if (!source) fail(`${name} is required`);
|
|
@@ -34,7 +34,6 @@ function readJsonEnvironment(name) {
|
|
|
34
34
|
throw new Error(`${name} is not valid JSON`, { cause: error });
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
|
|
38
37
|
function exactRuntime(admission) {
|
|
39
38
|
const runtime = admission?.runtime;
|
|
40
39
|
if (
|
|
@@ -45,7 +44,6 @@ function exactRuntime(admission) {
|
|
|
45
44
|
fail("an exact admitted Buildchain runtime is required");
|
|
46
45
|
return runtime;
|
|
47
46
|
}
|
|
48
|
-
|
|
49
47
|
function contentRoot(domain, value) {
|
|
50
48
|
const hash = crypto.createHash("sha256");
|
|
51
49
|
hash.update(domain, "utf8");
|
|
@@ -53,11 +51,9 @@ function contentRoot(domain, value) {
|
|
|
53
51
|
hash.update(`${JSON.stringify(value)}\n`, "utf8");
|
|
54
52
|
return `sha256:${hash.digest("hex")}`;
|
|
55
53
|
}
|
|
56
|
-
|
|
57
54
|
function emit(value) {
|
|
58
55
|
process.stdout.write(`${JSON.stringify(value)}\n`);
|
|
59
56
|
}
|
|
60
|
-
|
|
61
57
|
function exactObject(value, keys, label) {
|
|
62
58
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
63
59
|
fail(`${label} must be an object`);
|
|
@@ -85,7 +81,38 @@ function validateWorkflowInputs(workflowId, inputs) {
|
|
|
85
81
|
`candidate payload has unregistered workflow inputs: ${unknown.sort().join(", ")}`,
|
|
86
82
|
);
|
|
87
83
|
}
|
|
88
|
-
|
|
84
|
+
const TRUSTED_PUBLISHING_NPM_VERSION = "11.13.0";
|
|
85
|
+
function prepareTrustedPublishingNpm() {
|
|
86
|
+
if (
|
|
87
|
+
!process.env.ACTIONS_ID_TOKEN_REQUEST_URL ||
|
|
88
|
+
!process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN
|
|
89
|
+
)
|
|
90
|
+
fail("trusted publishing requires GitHub Actions OIDC authority");
|
|
91
|
+
const binDirectory = path.resolve(".buildchain/runtime/bin");
|
|
92
|
+
const npmShim = path.join(binDirectory, "npm");
|
|
93
|
+
fs.mkdirSync(binDirectory, { recursive: true });
|
|
94
|
+
fs.writeFileSync(
|
|
95
|
+
npmShim,
|
|
96
|
+
`#!/bin/sh\nexec corepack pnpm@11.7.0 dlx npm@${TRUSTED_PUBLISHING_NPM_VERSION} "$@"\n`,
|
|
97
|
+
{ mode: 0o755 },
|
|
98
|
+
);
|
|
99
|
+
fs.chmodSync(npmShim, 0o755);
|
|
100
|
+
process.env.PATH = `${binDirectory}${path.delimiter}${process.env.PATH || ""}`;
|
|
101
|
+
}
|
|
102
|
+
function prepareReleasePromotionConsumerDependencies(repository) {
|
|
103
|
+
if (repository !== "kungfu-systems/buildchain") return;
|
|
104
|
+
const manifest = JSON.parse(fs.readFileSync("package.json", "utf8"));
|
|
105
|
+
if (manifest.packageManager !== "pnpm@11.7.0" || !fs.existsSync("pnpm-lock.yaml")) fail("Buildchain release consumer dependency lock is unavailable");
|
|
106
|
+
const consumerModules = path.resolve("node_modules"), runtimeModules = path.resolve(".buildchain/runtime/node_modules");
|
|
107
|
+
if (!fs.existsSync(consumerModules) && fs.existsSync(runtimeModules)) fs.symlinkSync(path.relative(path.dirname(consumerModules), runtimeModules), consumerModules, "dir");
|
|
108
|
+
if (!fs.existsSync("node_modules/@kungfu-tech/kfd/package.json")) execFileSync("corepack", "pnpm@11.7.0 install --frozen-lockfile --ignore-scripts".split(" "), { stdio: ["ignore", 2, 2] });
|
|
109
|
+
if (fs.existsSync(runtimeModules)) {
|
|
110
|
+
if (fs.realpathSync(consumerModules) !== fs.realpathSync(runtimeModules)) fail("Buildchain release consumer runtime bridge conflicts");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
fs.mkdirSync(path.dirname(runtimeModules), { recursive: true }); fs.renameSync(consumerModules, runtimeModules);
|
|
114
|
+
fs.symlinkSync(path.relative(path.dirname(consumerModules), runtimeModules), consumerModules, "dir");
|
|
115
|
+
}
|
|
89
116
|
function actionEnvironment(inputs, outputPath) {
|
|
90
117
|
const environment = { ...process.env, GITHUB_OUTPUT: outputPath };
|
|
91
118
|
for (const [name, value] of Object.entries(inputs))
|
|
@@ -237,7 +264,12 @@ async function observedSourceTimestamp(repository, sourceSha) {
|
|
|
237
264
|
fail("protected publication source timestamp is unavailable");
|
|
238
265
|
return new Date(observed).toISOString();
|
|
239
266
|
}
|
|
240
|
-
|
|
267
|
+
async function observeProductPublicationRecovery(repository, sourceSha, candidateVersion) {
|
|
268
|
+
const apiUrl = process.env.GITHUB_API_URL || "https://api.github.com", token = process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "", prefix = `heads/buildchain/v4-product-state/${sourceSha}-`;
|
|
269
|
+
const stateRefs = (await githubJson({ apiUrl, token, path: `/repos/${repository}/git/matching-refs/${prefix}`, allowNotFound: true })) || [];
|
|
270
|
+
const recoveryStates = await Promise.all(stateRefs.map(async (stateRef) => { const version = v4ProductStateVersion(stateRef, sourceSha); const [stateCommit, exactTagRef] = await Promise.all([githubJson({ apiUrl, token, path: `/repos/${repository}/git/commits/${stateRef.object.sha}` }), githubJson({ apiUrl, token, path: `/repos/${repository}/git/ref/tags/v${version}`, allowNotFound: true })]); return { stateRef, stateCommit, exactTagRef }; }));
|
|
271
|
+
const exactTagRef = recoveryStates.length ? undefined : await githubJson({ apiUrl, token, path: `/repos/${repository}/git/ref/tags/v${encodeURIComponent(candidateVersion)}`, allowNotFound: true }); return { recoveryStates, exactTagRef };
|
|
272
|
+
}
|
|
241
273
|
async function materializeProductPublicationIntent({
|
|
242
274
|
candidate,
|
|
243
275
|
inputs,
|
|
@@ -247,10 +279,10 @@ async function materializeProductPublicationIntent({
|
|
|
247
279
|
const intentPath = path.resolve(
|
|
248
280
|
".buildchain/release-candidate/v4-product-publication-intent.json",
|
|
249
281
|
);
|
|
250
|
-
const sourceTimestamp = await observedSourceTimestamp(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
);
|
|
282
|
+
const sourceTimestamp = await observedSourceTimestamp(repository, route.requestedSha);
|
|
283
|
+
const version = String(candidate.publicationVersion || candidate.version || "").trim(), explicitResume = String(inputs["resume-transaction-id"] || "") !== "";
|
|
284
|
+
const recovery = route.decision !== "Resume" || explicitResume ? {} : await observeProductPublicationRecovery(repository, route.requestedSha, version);
|
|
285
|
+
const recoveredVersion = selectV4RecoveredProductPublicationVersion({ routeDecision: route.decision, candidateVersion: version, requestedSha: route.requestedSha, explicitResume, ...recovery });
|
|
254
286
|
execFileSync(
|
|
255
287
|
process.execPath,
|
|
256
288
|
[
|
|
@@ -269,10 +301,7 @@ async function materializeProductPublicationIntent({
|
|
|
269
301
|
BUILDCHAIN_SOURCE_TIMESTAMP: sourceTimestamp,
|
|
270
302
|
BUILDCHAIN_CANDIDATE_VERSION:
|
|
271
303
|
candidate.publicationVersion || candidate.version,
|
|
272
|
-
BUILDCHAIN_RECOVERED_PUBLICATION_VERSION:
|
|
273
|
-
String(inputs["resume-transaction-id"] || "") !== ""
|
|
274
|
-
? candidate.publicationVersion || candidate.version
|
|
275
|
-
: "",
|
|
304
|
+
BUILDCHAIN_RECOVERED_PUBLICATION_VERSION: recoveredVersion,
|
|
276
305
|
BUILDCHAIN_SEALED_BUNDLE_MANIFEST: candidate.paths.sealedBundleManifest,
|
|
277
306
|
BUILDCHAIN_REQUIRED_ARTIFACTS_PATH:
|
|
278
307
|
candidate.paths.publishRequiredArtifacts,
|
|
@@ -285,7 +314,6 @@ async function materializeProductPublicationIntent({
|
|
|
285
314
|
);
|
|
286
315
|
return intentPath;
|
|
287
316
|
}
|
|
288
|
-
|
|
289
317
|
function verifiedReleaseDocuments() {
|
|
290
318
|
const base = path.resolve(".buildchain/release-tail");
|
|
291
319
|
const invocation = JSON.parse(
|
|
@@ -370,6 +398,9 @@ async function executeReleasePromotion(request, admission) {
|
|
|
370
398
|
fail(`release route blocked: ${route.reason}`);
|
|
371
399
|
if (route.decision === "NoOp" || payload.inputs["dry-run"] === true)
|
|
372
400
|
return { route, dryRun: payload.inputs["dry-run"] === true };
|
|
401
|
+
prepareReleasePromotionConsumerDependencies(repository);
|
|
402
|
+
if (payload.inputs["trusted-publishing"] === true)
|
|
403
|
+
prepareTrustedPublishingNpm();
|
|
373
404
|
const candidate = await resolveReleaseCandidateArtifacts({
|
|
374
405
|
repository,
|
|
375
406
|
targetRef: route.targetRef,
|
|
@@ -390,13 +421,13 @@ async function executeReleasePromotion(request, admission) {
|
|
|
390
421
|
});
|
|
391
422
|
if (!candidate.enabled)
|
|
392
423
|
fail(candidate.reason || "release candidate is unavailable");
|
|
393
|
-
const productPublicationIntentPath =
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
424
|
+
const productPublicationIntentPath = await materializeProductPublicationIntent({
|
|
425
|
+
candidate,
|
|
426
|
+
inputs: payload.inputs,
|
|
427
|
+
repository,
|
|
428
|
+
route,
|
|
429
|
+
});
|
|
430
|
+
const productPublicationIntent = JSON.parse(fs.readFileSync(productPublicationIntentPath));
|
|
400
431
|
const runtimeTree = execFileSync(
|
|
401
432
|
"git",
|
|
402
433
|
["-C", ".buildchain/candidate", "rev-parse", "HEAD^{tree}"],
|
|
@@ -408,8 +439,8 @@ async function executeReleasePromotion(request, admission) {
|
|
|
408
439
|
process.env.BUILDCHAIN_PROMOTION_TOKEN || process.env.GH_TOKEN,
|
|
409
440
|
repository,
|
|
410
441
|
"source-sha": route.requestedSha,
|
|
411
|
-
version:
|
|
412
|
-
tag:
|
|
442
|
+
version: productPublicationIntent.version,
|
|
443
|
+
tag: productPublicationIntent.exactTag,
|
|
413
444
|
channel: route.channel,
|
|
414
445
|
"target-ref": route.targetRef,
|
|
415
446
|
"target-sha": route.requestedSha,
|
|
@@ -481,7 +512,6 @@ async function capabilityResult(request, admission) {
|
|
|
481
512
|
return executeReleasePromotion(request, admission);
|
|
482
513
|
fail(`candidate capability is not implemented: ${request.capability.id}`);
|
|
483
514
|
}
|
|
484
|
-
|
|
485
515
|
async function executeCandidate(request, admission) {
|
|
486
516
|
const runtime = exactRuntime(admission);
|
|
487
517
|
if (v4UniversalWorkflowRequestRoot(request) !== admission.requestRoot)
|