@kungfu-tech/buildchain 2.12.0 → 2.12.1-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/buildchain.mjs +18 -0
- package/dist/site/buildchain-contract.json +8 -8
- package/dist/site/buildchain-site.json +92 -26
- package/dist/site/capability-registry.json +5 -5
- package/dist/site/cli-registry.json +12 -0
- package/dist/site/kfd-claims.json +182 -14
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +8 -7
- package/dist/site/node-api-registry.json +20 -7
- package/dist/site/page-registry.json +81 -15
- package/dist/site/public-surface-audit.json +95 -10
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +1 -0
- package/dist/site/site-manifest.json +11 -11
- package/dist/site/workflow-registry.json +68 -3
- package/docs/MAP.md +3 -0
- package/docs/binary-distribution.md +20 -1
- package/docs/cli.md +17 -0
- package/docs/kfd-support.md +44 -1
- package/docs/lifecycle-protocol.md +12 -0
- package/docs/release-governance.md +23 -0
- package/docs/reusable-build-surface.md +69 -0
- package/docs/stable-candidate-patrol.md +188 -0
- package/package.json +3 -1
- package/packages/core/buildchain-config.js +58 -0
- package/packages/core/buildchain-kfd-claims.js +51 -2
- package/packages/core/buildchain-layout.js +46 -0
- package/packages/core/index.js +19 -0
- package/packages/core/kfd3-surface-register.js +59 -2
- package/packages/core/stable-candidate-ledger.js +276 -0
- package/scripts/build-standalone-binary.mjs +2 -1
- package/scripts/check-inventory.mjs +4 -1
- package/scripts/generate-site-bundle.mjs +9 -1
- package/scripts/init-repo.mjs +1 -1
- package/scripts/locked-source-checkout.mjs +52 -12
- package/scripts/npm-publish-dry-run.mjs +1 -1
- package/scripts/publication-artifact.mjs +1 -1
- package/scripts/publication-package.mjs +1 -1
- package/scripts/release-propagation.mjs +1 -1
- package/scripts/stable-candidate-patrol.mjs +448 -0
- package/scripts/stable-candidate-policy.mjs +25 -0
- package/scripts/stable-candidate-qualification.mjs +239 -0
|
@@ -303,6 +303,15 @@ Compatible work should still be batched until a stable release has a concrete
|
|
|
303
303
|
consumer need. Changing the interval, canary set, attestors, product path
|
|
304
304
|
boundary, or soak time is a reviewed policy change.
|
|
305
305
|
|
|
306
|
+
Repositories that want a predictable scheduled stable window can use
|
|
307
|
+
[`Stable Candidate Patrol`](stable-candidate-patrol.md). It persists each exact
|
|
308
|
+
alpha independently, qualifies it after repository-declared checks and soak,
|
|
309
|
+
and selects the newest qualified non-revoked candidate. A newer soaking alpha
|
|
310
|
+
does not invalidate an older qualified candidate. The selected tree enters the
|
|
311
|
+
existing strict `publish-gate/release/<line>/<version> -> release/<line>` PR
|
|
312
|
+
path, so scheduled selection changes release intent timing without weakening
|
|
313
|
+
source locks, review, verification, publish transactions, or passports.
|
|
314
|
+
|
|
306
315
|
If release finalization is resumed after generated version-state bookkeeping was
|
|
307
316
|
partially applied, Buildchain applies the same recovery rule: the current
|
|
308
317
|
release head may be the generated commit, or a historical merge commit that
|
|
@@ -403,6 +412,13 @@ Consumers that want Buildchain to own the check wrapper can call
|
|
|
403
412
|
`lifecycle.install` and `lifecycle.verify` stages and fails the `check` job when
|
|
404
413
|
either declaration is missing or the command exits non-zero.
|
|
405
414
|
|
|
415
|
+
Development pull requests that need source acceptance without product build or
|
|
416
|
+
artifact verification can opt into `mode: source`. That mode runs only
|
|
417
|
+
`lifecycle.install` and `lifecycle.check` on GitHub-hosted `ubuntu-24.04` while
|
|
418
|
+
preserving the stable `check / check` required-check context. Existing callers
|
|
419
|
+
remain on `mode: verify` by default, and callers may set
|
|
420
|
+
`upload-artifacts: false` without weakening the job conclusion.
|
|
421
|
+
|
|
406
422
|
Typical consumer wrapper:
|
|
407
423
|
|
|
408
424
|
```yaml
|
|
@@ -445,6 +461,7 @@ patrol workflow family:
|
|
|
445
461
|
| `.github/workflows/patrol-daily.yml` | daily | lightweight inspection plus ready dev PR maintenance |
|
|
446
462
|
| `.github/workflows/patrol-weekly.yml` | weekly | release-state, passport, gate, and stale-state health checks as they are added |
|
|
447
463
|
| `.github/workflows/patrol-monthly.yml` | monthly | governance, permission, branch-protection, and workflow drift checks as they are added |
|
|
464
|
+
| `.github/workflows/stable-candidate-patrol.yml` | repository-selected release window | qualify immutable alpha candidates and open the exact source-lock stable PR |
|
|
448
465
|
|
|
449
466
|
The cadence names describe patrol intensity, not release cadence:
|
|
450
467
|
|
|
@@ -453,6 +470,12 @@ The cadence names describe patrol intensity, not release cadence:
|
|
|
453
470
|
- monthly patrol is for structural drift checks that should not block ordinary
|
|
454
471
|
development velocity.
|
|
455
472
|
|
|
473
|
+
Stable Candidate Patrol is separate from those maintenance cadences because its
|
|
474
|
+
caller-owned cron is a release-intent window. Its candidate ledger and selection
|
|
475
|
+
remain generic; registry-specific side effects still run through the normal
|
|
476
|
+
repository `lifecycle.publish` transaction. See
|
|
477
|
+
[`stable-candidate-patrol.md`](stable-candidate-patrol.md).
|
|
478
|
+
|
|
456
479
|
Consumers should schedule thin callers and keep their YAML declarative. For
|
|
457
480
|
example:
|
|
458
481
|
|
|
@@ -162,6 +162,43 @@ Artifact names, manifest paths, expected artifact checks, publish-source locks,
|
|
|
162
162
|
and aggregate summaries are the same in both jobs. The split is an execution
|
|
163
163
|
detail, not a different artifact contract.
|
|
164
164
|
|
|
165
|
+
## Native Rust Toolchains
|
|
166
|
+
|
|
167
|
+
Native lifecycle jobs can request an isolated Rust installation instead of
|
|
168
|
+
depending on a self-hosted runner user's PATH:
|
|
169
|
+
|
|
170
|
+
```yaml
|
|
171
|
+
with:
|
|
172
|
+
setup-rust: true
|
|
173
|
+
rust-toolchain: "1.96.0"
|
|
174
|
+
rustup-dist-server: "https://rsproxy.cn"
|
|
175
|
+
rustup-update-root: "https://rsproxy.cn/rustup"
|
|
176
|
+
cargo-registry-index: ${{ vars.BUILDCHAIN_CARGO_REGISTRY_INDEX }}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`setup-rust` defaults to `false`, so existing consumers are unchanged. When it
|
|
180
|
+
is enabled, Buildchain installs `rust-toolchain` before the install, build, and
|
|
181
|
+
verify lifecycle stages on every native matrix platform. Windows uses the
|
|
182
|
+
official rustup bootstrap through `cmd.exe` and `curl.exe` into runner-temporary
|
|
183
|
+
Cargo and rustup homes, so it works under a restrictive PowerShell execution
|
|
184
|
+
policy and the service account does not depend on another user's PATH or mutate
|
|
185
|
+
host toolchain state. Pin an exact toolchain for release builds. Linux container jobs continue
|
|
186
|
+
to obtain Rust from their digest-pinned image contract; Buildchain does not
|
|
187
|
+
mutate that container surface.
|
|
188
|
+
|
|
189
|
+
The rustup server inputs are optional and default to Rust's official servers.
|
|
190
|
+
Consumers behind a slow cross-border link may select a trusted transport mirror;
|
|
191
|
+
rustup still verifies the selected toolchain's distribution metadata and
|
|
192
|
+
component checksums.
|
|
193
|
+
|
|
194
|
+
`cargo-registry-index` is also optional. When set, Buildchain exposes it to
|
|
195
|
+
Cargo as `CARGO_REGISTRIES_CRATES_IO_INDEX` for every native lifecycle stage,
|
|
196
|
+
so a self-hosted runner can use a repository or organization variable without
|
|
197
|
+
committing private LAN topology to public workflow YAML. The endpoint must be a
|
|
198
|
+
crates.io-compatible index whose `config.json` download contract serves the
|
|
199
|
+
matching checksum-verified crate archives. An empty value preserves Cargo's
|
|
200
|
+
normal crates.io behavior.
|
|
201
|
+
|
|
165
202
|
The container image provides `fnm` but does not preinstall Node. Buildchain uses
|
|
166
203
|
`fnm` inside the container to install the requested `node-version` before it
|
|
167
204
|
runs Buildchain runtime scripts or lifecycle actions.
|
|
@@ -289,6 +326,38 @@ also pass `buildchain-ref: v2-alpha` explicitly; official floating refs are
|
|
|
289
326
|
ordinary channel selections and are allowed on pull requests and pushes. Train
|
|
290
327
|
refs and exact SHAs remain trusted manual overrides.
|
|
291
328
|
|
|
329
|
+
## Shifu Cache Profile Passthrough
|
|
330
|
+
|
|
331
|
+
Buildchain can carry one trusted Shifu cache-profile reference and its exact
|
|
332
|
+
digest into lifecycle execution. Its contract is an opaque reference and digest
|
|
333
|
+
only. This surface is deliberately opaque:
|
|
334
|
+
Buildchain does not fetch the profile, parse JSON, select cache services,
|
|
335
|
+
rewrite bindings, decide fallback, or emit Shifu resolution evidence. Those
|
|
336
|
+
semantics remain owned by the consumer's pinned Shifu implementation.
|
|
337
|
+
|
|
338
|
+
```yaml
|
|
339
|
+
jobs:
|
|
340
|
+
build:
|
|
341
|
+
uses: kungfu-systems/buildchain/.github/workflows/build.yml@v2
|
|
342
|
+
with:
|
|
343
|
+
shifu-cache-profile-ref: ${{ vars.SHIFU_CACHE_PROFILE_REF }}
|
|
344
|
+
shifu-cache-profile-digest: ${{ vars.SHIFU_CACHE_PROFILE_DIGEST }}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
The reusable workflow passes the pair as `SHIFU_CACHE_PROFILE_REF` and
|
|
348
|
+
`SHIFU_CACHE_PROFILE_DIGEST` to install, build, and verify lifecycle commands.
|
|
349
|
+
The consumer must invoke its Shifu cache-aware execution surface. An empty pair
|
|
350
|
+
preserves existing behavior; a consumer Shifu should fail closed when exactly
|
|
351
|
+
one value is present or the resolved bytes do not match the expected digest.
|
|
352
|
+
|
|
353
|
+
Use trusted repository or organization variables rather than PR-controlled
|
|
354
|
+
files for private/LAN references. The variables must remain secret-free; any
|
|
355
|
+
credentials use a separate provider-approved secret surface and must not be
|
|
356
|
+
embedded in the profile reference. This passthrough is separate from
|
|
357
|
+
Buildchain's locked source checkout cache below: Buildchain owns checkout
|
|
358
|
+
transport and source identity, while Shifu owns post-checkout execution cache
|
|
359
|
+
bindings and receipts.
|
|
360
|
+
|
|
292
361
|
## Locked Source Checkout Cache
|
|
293
362
|
|
|
294
363
|
Self-hosted runners that build large repositories can opt into a locked checkout
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
---
|
|
2
|
+
status: preview
|
|
3
|
+
period: ongoing
|
|
4
|
+
theme: stable-candidate-patrol
|
|
5
|
+
doc_type: architecture-and-usage
|
|
6
|
+
source_level: local-files
|
|
7
|
+
confidence: high
|
|
8
|
+
sensitivity: public
|
|
9
|
+
evidence_grade: A
|
|
10
|
+
review_state: self-reviewed
|
|
11
|
+
last_reviewed: 2026-07-11
|
|
12
|
+
ai_provenance:
|
|
13
|
+
model_family: GPT-5
|
|
14
|
+
product: Codex
|
|
15
|
+
generated_at: 2026-07-11
|
|
16
|
+
visible_context: Buildchain candidate passport, stable gate, Patrol, publish transaction, exact source-lock PR contracts, tests, and user consensus.
|
|
17
|
+
invisible_context_boundary: No credentials, private logs, or unpublished consumer content were used.
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Stable Candidate Patrol
|
|
21
|
+
|
|
22
|
+
Buildchain can treat every exact alpha as an independent stable candidate. A
|
|
23
|
+
new alpha creates a new candidate; it does not silently revoke an older alpha
|
|
24
|
+
that already completed its checks and soak interval.
|
|
25
|
+
|
|
26
|
+
## Candidate lifecycle
|
|
27
|
+
|
|
28
|
+
The durable ledger contract is `kungfu-buildchain-stable-candidate-ledger`:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
registered -> soaking -> qualified -> promoted
|
|
32
|
+
\-> revoked
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Each entry binds an exact alpha version to one immutable commit SHA. Registering
|
|
36
|
+
the same version at another SHA fails closed. Qualification records the required
|
|
37
|
+
checks, their completion times, the derived soak start, and elapsed time.
|
|
38
|
+
|
|
39
|
+
Scheduled selection chooses the newest `qualified` candidate that is neither
|
|
40
|
+
`revoked` nor already `promoted`. A newer alpha that is still `soaking` does not
|
|
41
|
+
hide an older qualified candidate. Once a stable version is promoted, remaining
|
|
42
|
+
alphas for that exact stable version are closed because that immutable stable
|
|
43
|
+
version has been consumed; their later product changes continue through the
|
|
44
|
+
next patch alpha prepared by the normal release transaction.
|
|
45
|
+
|
|
46
|
+
For Buildchain's own release line, successful Alpha Self-Dogfood starts an
|
|
47
|
+
idempotent qualification producer for the exact alpha SHA. It dispatches the
|
|
48
|
+
existing `Build Surface Fixture` at the immutable exact tag, runs the existing
|
|
49
|
+
`site-libkungfu-dev` no-apply canary with the exact SHA, and writes the declared
|
|
50
|
+
commit-status attestation only after that authoritative canary succeeds. The
|
|
51
|
+
producer creates evidence only: Patrol still owns qualification and selection,
|
|
52
|
+
and the normal source-lock PR, stable gate, transaction, and branch protections
|
|
53
|
+
remain mandatory.
|
|
54
|
+
|
|
55
|
+
Cross-repository dispatch and repository-local attestation use separate tokens.
|
|
56
|
+
The promotion token can start the no-apply consumer workflow, while the
|
|
57
|
+
repository-scoped Actions token writes the status as `github-actions[bot]` only
|
|
58
|
+
after the producer has observed the successful authoritative workflow run. The
|
|
59
|
+
stable gate still verifies the workflow identity, exact runtime SHA, target URL,
|
|
60
|
+
and allowed attestor before accepting that status.
|
|
61
|
+
|
|
62
|
+
## Repository policy
|
|
63
|
+
|
|
64
|
+
Declare the default once in `.buildchain/buildchain.toml`:
|
|
65
|
+
|
|
66
|
+
```toml
|
|
67
|
+
[release.stable]
|
|
68
|
+
strategy = "latest-qualified-alpha"
|
|
69
|
+
timezone = "Asia/Shanghai"
|
|
70
|
+
publish_at = "03:00"
|
|
71
|
+
minimum_soak_seconds = 3600
|
|
72
|
+
required_checks = [
|
|
73
|
+
"alpha-release",
|
|
74
|
+
"workflow:Build",
|
|
75
|
+
"status:buildchain-canary/consumer",
|
|
76
|
+
]
|
|
77
|
+
auto_promote = true
|
|
78
|
+
auto_merge = true
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Check identifiers use these forms:
|
|
82
|
+
|
|
83
|
+
- `alpha-release`: the exact GitHub prerelease publication fact;
|
|
84
|
+
- `workflow:<name>`: a successful Actions workflow run on the exact candidate SHA;
|
|
85
|
+
- `status:<context>`: a successful commit status on the exact candidate SHA;
|
|
86
|
+
- an unprefixed value: an exact status context or check-run name.
|
|
87
|
+
|
|
88
|
+
Candidate discovery uses immutable exact alpha Git tags. When a repository also
|
|
89
|
+
publishes GitHub prereleases, `alpha-release` binds qualification to that public
|
|
90
|
+
release fact. Repositories that intentionally disable GitHub Releases omit
|
|
91
|
+
`alpha-release` and declare their own exact-SHA workflow/status evidence; tag
|
|
92
|
+
commit time remains the earliest possible soak start.
|
|
93
|
+
|
|
94
|
+
`publish_at` and `timezone` are the auditable policy declaration. GitHub only
|
|
95
|
+
starts scheduled workflows from caller-owned cron, so the thin caller keeps the
|
|
96
|
+
matching UTC trigger:
|
|
97
|
+
|
|
98
|
+
```yaml
|
|
99
|
+
name: Stable Candidate Patrol
|
|
100
|
+
|
|
101
|
+
on:
|
|
102
|
+
schedule:
|
|
103
|
+
- cron: "0 19 * * *" # 03:00 Asia/Shanghai
|
|
104
|
+
workflow_dispatch:
|
|
105
|
+
inputs:
|
|
106
|
+
release-now:
|
|
107
|
+
description: Exact alpha selected by explicit human authority
|
|
108
|
+
required: false
|
|
109
|
+
default: ""
|
|
110
|
+
|
|
111
|
+
permissions:
|
|
112
|
+
contents: write
|
|
113
|
+
pull-requests: write
|
|
114
|
+
checks: read
|
|
115
|
+
statuses: read
|
|
116
|
+
|
|
117
|
+
jobs:
|
|
118
|
+
stable:
|
|
119
|
+
uses: kungfu-systems/buildchain/.github/workflows/stable-candidate-patrol.yml@v2
|
|
120
|
+
with:
|
|
121
|
+
release-now: ${{ inputs.release-now }}
|
|
122
|
+
dry-run: false
|
|
123
|
+
secrets:
|
|
124
|
+
promotion-token: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The promotion token must be repository-owned and capable of creating the
|
|
128
|
+
machine ledger branch, exact source-lock branch, and pull request. Repositories
|
|
129
|
+
that require an approving review can set `auto-approve: true` only when their
|
|
130
|
+
GitHub Actions policy explicitly allows the caller token to approve the PR;
|
|
131
|
+
otherwise a repository-owned App or review bot supplies the approval. The
|
|
132
|
+
generated PR may use auto-merge, but it never bypasses the target branch checks.
|
|
133
|
+
|
|
134
|
+
## Exact-source stable promotion
|
|
135
|
+
|
|
136
|
+
For a selected `2.12.0-alpha.4`, Patrol creates the immutable source branch:
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
publish-gate/release/v2/v2.12/2.12.0-alpha.4
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
and opens it against `release/v2/v2.12`. This is an existing strict Buildchain
|
|
143
|
+
governance path. The PR freezes the qualified candidate even if `v2.12-alpha`
|
|
144
|
+
or `alpha/v2/v2.12` has already moved to alpha.5. Normal Verify,
|
|
145
|
+
release-candidate resolution, source-tree equivalence, publish transaction,
|
|
146
|
+
passport, registry, tag, and floating-ref checks still run.
|
|
147
|
+
|
|
148
|
+
## Hold, revoke, and immediate release
|
|
149
|
+
|
|
150
|
+
Persistent repository controls can be supplied as reusable-workflow inputs or
|
|
151
|
+
repository variables:
|
|
152
|
+
|
|
153
|
+
```text
|
|
154
|
+
BUILDCHAIN_STABLE_HOLD=true
|
|
155
|
+
BUILDCHAIN_STABLE_HOLD_REASON=release freeze
|
|
156
|
+
BUILDCHAIN_STABLE_REVOKED_ALPHA_VERSIONS=2.12.0-alpha.5,2.12.0-alpha.7
|
|
157
|
+
BUILDCHAIN_STABLE_REVOKE_REASON=consumer regression
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Revocation is explicit evidence; publishing a newer alpha alone is not
|
|
161
|
+
revocation. A manual `workflow_dispatch` `release-now` chooses one exact,
|
|
162
|
+
non-revoked candidate immediately. It may bypass the scheduled soak decision,
|
|
163
|
+
but cannot change candidate SHA, reuse a consumed stable version, or bypass the
|
|
164
|
+
source-lock PR and publish transaction.
|
|
165
|
+
|
|
166
|
+
For Buildchain's own stable gate, Patrol automatically projects that explicit
|
|
167
|
+
human decision into the exact-candidate `BUILDCHAIN_STABLE_RELEASE_NOW` and
|
|
168
|
+
reason variables. A later Patrol run removes them after it observes the public
|
|
169
|
+
stable release. This is an internal compatibility projection, not a manual user
|
|
170
|
+
step; the durable authority record remains the candidate ledger entry and PR.
|
|
171
|
+
|
|
172
|
+
## Durable recovery
|
|
173
|
+
|
|
174
|
+
The default ledger ref is derived from the release line, for example:
|
|
175
|
+
|
|
176
|
+
```text
|
|
177
|
+
buildchain/candidate-ledger/v2/v2.12
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
It stores `.buildchain/stable-candidate-ledger.json`. Patrol runs are serialized
|
|
181
|
+
per repository and release line. Repeated runs reuse the same exact source-lock
|
|
182
|
+
branch and PR, while later runs observe the public stable release and mark the
|
|
183
|
+
candidate `promoted`.
|
|
184
|
+
|
|
185
|
+
When Patrol is enabled after a stable version already exists, it reconstructs
|
|
186
|
+
that consumed patch from GitHub Release truth and closes historical alpha
|
|
187
|
+
candidates for the same stable version. It never attempts to republish an
|
|
188
|
+
already claimed exact stable version.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "2.12.
|
|
3
|
+
"version": "2.12.1-alpha.10",
|
|
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",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"./publication-package": "./packages/core/publication-package.js",
|
|
31
31
|
"./kfd-gate": "./packages/core/kfd-gate.js",
|
|
32
32
|
"./release-candidate": "./packages/core/release-candidate.js",
|
|
33
|
+
"./stable-candidate-ledger": "./packages/core/stable-candidate-ledger.js",
|
|
33
34
|
"./release-passport": "./packages/core/release-passport.js",
|
|
34
35
|
"./release-propagation": "./packages/core/release-propagation.js",
|
|
35
36
|
"./surface-manifest": "./packages/core/surface-manifest.js",
|
|
@@ -83,6 +84,7 @@
|
|
|
83
84
|
"check:site": "node scripts/generate-site-bundle.mjs --check",
|
|
84
85
|
"test:unit": "node --test tests/*.test.mjs",
|
|
85
86
|
"build": "pnpm -r --filter \"./actions/**\" build",
|
|
87
|
+
"binary:build": "node scripts/build-standalone-binary.mjs",
|
|
86
88
|
"package": "npm pack --dry-run --json --registry=https://registry.npmjs.org/",
|
|
87
89
|
"pack:check": "npm pack --dry-run --json --registry=https://registry.npmjs.org/"
|
|
88
90
|
},
|
|
@@ -19,6 +19,7 @@ const SUPPORTED_PUBLISH_MODES = new Set(["publish-final-version", "promote-exist
|
|
|
19
19
|
const SUPPORTED_PUBLISH_AUTH = new Set(["trusted-publishing", "npm-token"]);
|
|
20
20
|
const SUPPORTED_PUBLISH_KINDS = new Set(["npm-package", "npm-paper-package"]);
|
|
21
21
|
const SUPPORTED_PACKAGE_SET_ORDER = new Set(["as-provided", "platforms-first-main-last"]);
|
|
22
|
+
const SUPPORTED_STABLE_RELEASE_STRATEGIES = new Set(["manual", "latest-qualified-alpha"]);
|
|
22
23
|
const SUPPORTED_NATIVE_COMPILER_CACHE = new Set(["auto", "ccache", "sccache", "none"]);
|
|
23
24
|
const WEB_SURFACE_CHANNELS = ["preview", "staging", "production"];
|
|
24
25
|
const SUPPORTED_CHANNEL_VISIBILITY = new Set(["ephemeral", "protected", "public", "internal"]);
|
|
@@ -180,6 +181,9 @@ export function normalizeBuildchainConfig(config) {
|
|
|
180
181
|
if (normalized.publish !== undefined) {
|
|
181
182
|
normalized.publish = normalizePublishSection(normalized.publish);
|
|
182
183
|
}
|
|
184
|
+
if (normalized.release !== undefined) {
|
|
185
|
+
normalized.release = normalizeReleaseSection(normalized.release);
|
|
186
|
+
}
|
|
183
187
|
if (normalized.channels !== undefined) {
|
|
184
188
|
normalized.channels = normalizeChannelsSection(normalized.channels, normalized.project);
|
|
185
189
|
}
|
|
@@ -215,6 +219,59 @@ export function normalizeBuildchainConfig(config) {
|
|
|
215
219
|
return normalized;
|
|
216
220
|
}
|
|
217
221
|
|
|
222
|
+
function normalizeReleaseSection(release) {
|
|
223
|
+
assertPlainObject(release, "release");
|
|
224
|
+
const normalized = { ...release };
|
|
225
|
+
if (release.stable !== undefined) {
|
|
226
|
+
assertPlainObject(release.stable, "release.stable");
|
|
227
|
+
const strategy = release.stable.strategy === undefined
|
|
228
|
+
? "manual"
|
|
229
|
+
: assertString(release.stable.strategy, "release.stable.strategy");
|
|
230
|
+
if (!SUPPORTED_STABLE_RELEASE_STRATEGIES.has(strategy)) {
|
|
231
|
+
throw new Error("release.stable.strategy must be one of manual or latest-qualified-alpha");
|
|
232
|
+
}
|
|
233
|
+
const minimumSoakSeconds = release.stable.minimum_soak_seconds === undefined
|
|
234
|
+
? 3600
|
|
235
|
+
: Number(release.stable.minimum_soak_seconds);
|
|
236
|
+
if (!Number.isInteger(minimumSoakSeconds) || minimumSoakSeconds < 0) {
|
|
237
|
+
throw new Error("release.stable.minimum_soak_seconds must be a non-negative integer");
|
|
238
|
+
}
|
|
239
|
+
normalized.stable = {
|
|
240
|
+
strategy,
|
|
241
|
+
timezone: release.stable.timezone === undefined
|
|
242
|
+
? "UTC"
|
|
243
|
+
: assertString(release.stable.timezone, "release.stable.timezone"),
|
|
244
|
+
publishAt: release.stable.publish_at === undefined
|
|
245
|
+
? "03:00"
|
|
246
|
+
: assertString(release.stable.publish_at, "release.stable.publish_at"),
|
|
247
|
+
minimumSoakSeconds,
|
|
248
|
+
requiredChecks: normalizeStringArray(release.stable.required_checks, "release.stable.required_checks"),
|
|
249
|
+
ledgerRef: release.stable.ledger_ref === undefined
|
|
250
|
+
? ""
|
|
251
|
+
: assertString(release.stable.ledger_ref, "release.stable.ledger_ref"),
|
|
252
|
+
autoPromote: optionalBoolean(release.stable.auto_promote, strategy === "latest-qualified-alpha"),
|
|
253
|
+
autoMerge: optionalBoolean(release.stable.auto_merge, false),
|
|
254
|
+
};
|
|
255
|
+
if (!/^([01]\d|2[0-3]):[0-5]\d$/.test(normalized.stable.publishAt)) {
|
|
256
|
+
throw new Error("release.stable.publish_at must be HH:MM in 24-hour time");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return normalized;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export function getStableReleasePolicy(loadedConfig) {
|
|
263
|
+
return loadedConfig?.config?.release?.stable || {
|
|
264
|
+
strategy: "manual",
|
|
265
|
+
timezone: "UTC",
|
|
266
|
+
publishAt: "03:00",
|
|
267
|
+
minimumSoakSeconds: 3600,
|
|
268
|
+
requiredChecks: [],
|
|
269
|
+
ledgerRef: "",
|
|
270
|
+
autoPromote: false,
|
|
271
|
+
autoMerge: false,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
218
275
|
function normalizeFactsSection(facts) {
|
|
219
276
|
assertPlainObject(facts, "facts");
|
|
220
277
|
return {
|
|
@@ -1389,6 +1446,7 @@ export function validateBuildchainConfig(
|
|
|
1389
1446
|
})),
|
|
1390
1447
|
lifecycleStages,
|
|
1391
1448
|
publish: loadedConfig.config.publish,
|
|
1449
|
+
release: loadedConfig.config.release,
|
|
1392
1450
|
facts: loadedConfig.config.facts,
|
|
1393
1451
|
publication: loadedConfig.config.publication,
|
|
1394
1452
|
};
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
BUILDCHAIN_PUBLIC_SURFACE_AUDIT_CONTRACT,
|
|
7
7
|
collectPublicSurfaceReverseAudit,
|
|
8
8
|
} from "./public-surface-audit.js";
|
|
9
|
+
import { normalizeKfd3DistributionDeclaration } from "./kfd3-surface-register.js";
|
|
9
10
|
|
|
10
11
|
export const BUILDCHAIN_KFD_CLAIM_REGISTRY_CONTRACT = "kungfu-buildchain-kfd-claim-registry";
|
|
11
12
|
export const BUILDCHAIN_KFD_COLLABORATION_INTERFACE_CONTRACT = "kungfu-buildchain-kfd-collaboration-interface";
|
|
@@ -307,6 +308,23 @@ export function createBuildchainPublicClaimDefinitions() {
|
|
|
307
308
|
"dist/site/public-surface-audit.json",
|
|
308
309
|
],
|
|
309
310
|
},
|
|
311
|
+
{
|
|
312
|
+
id: "claim:buildchain-shifu-kfd-discovery",
|
|
313
|
+
claim: "Buildchain exposes a versioned repository-layout discovery contract and KFD-3 distribution declarations so Shifu can locate the registry and determine jurisdiction without copying Buildchain layout paths.",
|
|
314
|
+
sourcePaths: [
|
|
315
|
+
"packages/core/buildchain-layout.js",
|
|
316
|
+
"packages/core/buildchain-kfd-claims.js",
|
|
317
|
+
"packages/core/kfd3-surface-register.js",
|
|
318
|
+
"bin/buildchain.mjs",
|
|
319
|
+
"scripts/build-standalone-binary.mjs",
|
|
320
|
+
"docs/kfd-support.md",
|
|
321
|
+
],
|
|
322
|
+
artifactPaths: [
|
|
323
|
+
"dist/site/kfd-claims.json",
|
|
324
|
+
"dist/site/cli-registry.json",
|
|
325
|
+
"dist/site/node-api-registry.json",
|
|
326
|
+
],
|
|
327
|
+
},
|
|
310
328
|
{
|
|
311
329
|
id: "claim:buildchain-readme-badge-facts",
|
|
312
330
|
claim: "Buildchain README status badges are generated from repository-owned facts and verified release-passport evidence, not hand-maintained README prose.",
|
|
@@ -431,6 +449,37 @@ export function createBuildchainKfdSurfaceRegistry({ root = process.cwd() } = {}
|
|
|
431
449
|
];
|
|
432
450
|
const siteConsumptionContracts = SITE_CONTRACT_FILES.map((relPath) => surface(`site:${relPath}`, "site-consumption-contract", relPath));
|
|
433
451
|
const controlSurfaces = WORKFLOW_AND_ACTION_FILES.map((relPath) => surface(`control:${relPath}`, relPath.includes("actions/") ? "action" : "workflow", relPath));
|
|
452
|
+
const standaloneDistribution = surface(
|
|
453
|
+
"distribution:buildchain-standalone",
|
|
454
|
+
"binary-distribution",
|
|
455
|
+
"scripts/build-standalone-binary.mjs",
|
|
456
|
+
{
|
|
457
|
+
name: "Buildchain standalone binary distribution",
|
|
458
|
+
artifactPath: "dist/binary",
|
|
459
|
+
evidencePath: ".github/workflows/binary-distribution.yml",
|
|
460
|
+
distribution: normalizeKfd3DistributionDeclaration({
|
|
461
|
+
registrar: "shifu",
|
|
462
|
+
tasks: ["binary:build"],
|
|
463
|
+
artifacts: [
|
|
464
|
+
{
|
|
465
|
+
kind: "binary",
|
|
466
|
+
platform: "linux",
|
|
467
|
+
pathGlob: "dist/binary/buildchain-x86_64-unknown-linux-gnu.tar.gz",
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
kind: "binary",
|
|
471
|
+
platform: "macos",
|
|
472
|
+
pathGlob: "dist/binary/buildchain-aarch64-apple-darwin.tar.gz",
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
kind: "binary",
|
|
476
|
+
platform: "windows",
|
|
477
|
+
pathGlob: "dist/binary/buildchain-x86_64-pc-windows-msvc.zip",
|
|
478
|
+
},
|
|
479
|
+
],
|
|
480
|
+
}, { surfaceId: "distribution:buildchain-standalone" }),
|
|
481
|
+
},
|
|
482
|
+
);
|
|
434
483
|
const reverseEnumeratedSurfaces = uniqueById([
|
|
435
484
|
...reverseAudit.enumerated.cliCommands.map((entry) => surface(`cli:${entry.id}`, "cli-command", "bin/buildchain.mjs", {
|
|
436
485
|
name: entry.usage,
|
|
@@ -463,8 +512,8 @@ export function createBuildchainKfdSurfaceRegistry({ root = process.cwd() } = {}
|
|
|
463
512
|
siteConsumptionContracts,
|
|
464
513
|
reverseEnumeratedSurfaces,
|
|
465
514
|
},
|
|
466
|
-
additionalSurfaces: controlSurfaces,
|
|
467
|
-
publicSurfaceCount: docs.length + schemas.length + standardsMetadata.length + exports.length + siteConsumptionContracts.length + reverseEnumeratedSurfaces.length + controlSurfaces.length,
|
|
515
|
+
additionalSurfaces: [...controlSurfaces, standaloneDistribution],
|
|
516
|
+
publicSurfaceCount: docs.length + schemas.length + standardsMetadata.length + exports.length + siteConsumptionContracts.length + reverseEnumeratedSurfaces.length + controlSurfaces.length + 1,
|
|
468
517
|
};
|
|
469
518
|
}
|
|
470
519
|
|
|
@@ -2,6 +2,8 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
4
|
export const BUILDCHAIN_DIR = ".buildchain";
|
|
5
|
+
export const BUILDCHAIN_VERSION_PIN_PATH = ".buildchain-version";
|
|
6
|
+
export const BUILDCHAIN_LAYOUT_DISCOVERY_CONTRACT = "kungfu-buildchain-layout-discovery";
|
|
5
7
|
export const BUILDCHAIN_CONFIG_PATH = ".buildchain/buildchain.toml";
|
|
6
8
|
export const LEGACY_BUILDCHAIN_CONFIG_PATH = "buildchain.toml";
|
|
7
9
|
export const BUILDCHAIN_CONTRACT_LOCK_PATH = ".buildchain/contract-lock.json";
|
|
@@ -94,6 +96,50 @@ export function resolveReleasePassportPath(cwd = process.cwd()) {
|
|
|
94
96
|
]);
|
|
95
97
|
}
|
|
96
98
|
|
|
99
|
+
export function createBuildchainLayoutDiscovery({
|
|
100
|
+
cwd = process.cwd(),
|
|
101
|
+
buildchainVersion = "",
|
|
102
|
+
} = {}) {
|
|
103
|
+
const resolvedCwd = path.resolve(cwd);
|
|
104
|
+
const configPath = resolveBuildchainConfigPath(resolvedCwd);
|
|
105
|
+
const kfd3RegistryPath = resolveKfd3SurfaceRegistryPath(resolvedCwd);
|
|
106
|
+
return {
|
|
107
|
+
schemaVersion: 1,
|
|
108
|
+
contract: BUILDCHAIN_LAYOUT_DISCOVERY_CONTRACT,
|
|
109
|
+
buildchain: {
|
|
110
|
+
version: String(buildchainVersion || ""),
|
|
111
|
+
versionPinPath: BUILDCHAIN_VERSION_PIN_PATH,
|
|
112
|
+
},
|
|
113
|
+
repository: {
|
|
114
|
+
root: resolvedCwd,
|
|
115
|
+
managed: fs.existsSync(repoPath(resolvedCwd, configPath))
|
|
116
|
+
|| fs.existsSync(repoPath(resolvedCwd, BUILDCHAIN_VERSION_PIN_PATH))
|
|
117
|
+
|| fs.existsSync(repoPath(resolvedCwd, kfd3RegistryPath)),
|
|
118
|
+
configPath,
|
|
119
|
+
},
|
|
120
|
+
kfd: {
|
|
121
|
+
root: BUILDCHAIN_KFD_ROOT,
|
|
122
|
+
registries: {
|
|
123
|
+
"kfd-3": {
|
|
124
|
+
standard: "kfd-3",
|
|
125
|
+
schemaVersion: 1,
|
|
126
|
+
path: kfd3RegistryPath,
|
|
127
|
+
canonicalPath: BUILDCHAIN_KFD3_SURFACE_REGISTRY_PATH,
|
|
128
|
+
exists: fs.existsSync(repoPath(resolvedCwd, kfd3RegistryPath)),
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
shifu: {
|
|
133
|
+
jurisdiction: {
|
|
134
|
+
source: "kfd-3-distribution-declaration",
|
|
135
|
+
registryStandard: "kfd-3",
|
|
136
|
+
field: "surfaces[].distribution.registrar",
|
|
137
|
+
value: "shifu",
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
97
143
|
export function discoverBuildchainRepoFiles(cwd = process.cwd()) {
|
|
98
144
|
const files = [
|
|
99
145
|
{
|
package/packages/core/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export {
|
|
|
3
3
|
getLifecycleStage,
|
|
4
4
|
getNativeDiagnosticsProfile,
|
|
5
5
|
getPublishContract,
|
|
6
|
+
getStableReleasePolicy,
|
|
6
7
|
getVersionStrategy,
|
|
7
8
|
loadBuildchainConfig,
|
|
8
9
|
loadConfiguredAnchorManifest,
|
|
@@ -13,6 +14,20 @@ export {
|
|
|
13
14
|
validateBuildchainConfig,
|
|
14
15
|
} from "./buildchain-config.js";
|
|
15
16
|
|
|
17
|
+
export {
|
|
18
|
+
STABLE_CANDIDATE_LEDGER_CONTRACT,
|
|
19
|
+
STABLE_CANDIDATE_STATES,
|
|
20
|
+
createStableCandidateLedger,
|
|
21
|
+
markStableCandidatePromoted,
|
|
22
|
+
normalizeStableCandidateLedger,
|
|
23
|
+
qualifyStableCandidate,
|
|
24
|
+
registerStableCandidate,
|
|
25
|
+
revokeStableCandidate,
|
|
26
|
+
selectStableCandidate,
|
|
27
|
+
setStableCandidateHold,
|
|
28
|
+
stableCandidatePromotionRefs,
|
|
29
|
+
} from "./stable-candidate-ledger.js";
|
|
30
|
+
|
|
16
31
|
export {
|
|
17
32
|
assertPackageManager,
|
|
18
33
|
commandForKungfuUpgrade,
|
|
@@ -277,6 +292,8 @@ export {
|
|
|
277
292
|
BUILDCHAIN_CONFIG_PATH,
|
|
278
293
|
BUILDCHAIN_CONTRACT_LOCK_PATH,
|
|
279
294
|
BUILDCHAIN_DIR,
|
|
295
|
+
BUILDCHAIN_LAYOUT_DISCOVERY_CONTRACT,
|
|
296
|
+
BUILDCHAIN_VERSION_PIN_PATH,
|
|
280
297
|
BUILDCHAIN_GENERATED_DIRS,
|
|
281
298
|
BUILDCHAIN_KFD_ROOT,
|
|
282
299
|
BUILDCHAIN_KFD1_CONTRACT_WORLD_WITNESS_PATH,
|
|
@@ -302,6 +319,7 @@ export {
|
|
|
302
319
|
LEGACY_BUILDCHAIN_KFD3_SURFACE_REGISTRY_PATHS,
|
|
303
320
|
LEGACY_BUILDCHAIN_RELEASE_PASSPORT_PATH,
|
|
304
321
|
discoverBuildchainRepoFiles,
|
|
322
|
+
createBuildchainLayoutDiscovery,
|
|
305
323
|
migrateBuildchainLayout,
|
|
306
324
|
planBuildchainLayoutMigration,
|
|
307
325
|
resolveBuildchainConfigPath,
|
|
@@ -344,6 +362,7 @@ export {
|
|
|
344
362
|
auditKfd3Surfaces,
|
|
345
363
|
createKfd3SurfaceWitness,
|
|
346
364
|
detectKfd3Surfaces,
|
|
365
|
+
normalizeKfd3DistributionDeclaration,
|
|
347
366
|
queryKfd3Capabilities,
|
|
348
367
|
readKfd3SurfaceRegistry,
|
|
349
368
|
registerKfd3Surfaces,
|