@kungfu-tech/buildchain 0.0.0-bootstrap.0 → 2.0.13-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/README.md +262 -0
- package/bin/buildchain.mjs +222 -0
- package/docs/cli.md +124 -0
- package/docs/lifecycle-protocol.md +422 -0
- package/docs/publish-transaction.md +285 -0
- package/docs/reusable-build-surface.md +350 -0
- package/docs/web-surface-deployments.md +211 -0
- package/package.json +52 -1
- package/packages/core/README.md +15 -0
- package/packages/core/buildchain-config.js +721 -0
- package/packages/core/index.js +40 -0
- package/packages/core/package-manager.js +291 -0
- package/packages/core/publish-transaction.js +418 -0
- package/packages/core/release-line-dry-run.js +296 -0
- package/scripts/aggregate-build-summary.mjs +88 -0
- package/scripts/build-contract-core.mjs +731 -0
- package/scripts/check-inventory.mjs +325 -0
- package/scripts/init-repo.mjs +316 -0
- package/scripts/npm-publish-dry-run.mjs +176 -0
- package/scripts/npm-publish-transaction.mjs +268 -0
- package/scripts/publish-source-ref-resolver.mjs +113 -0
- package/scripts/release-line-dry-run.mjs +53 -0
- package/scripts/release-line-policy.mjs +141 -0
- package/scripts/release-transaction.mjs +212 -0
- package/scripts/resolve-build-contract.mjs +63 -0
- package/scripts/resolve-publish-gate.mjs +33 -0
- package/scripts/resolve-publish-source.mjs +99 -0
- package/scripts/run-lifecycle-core.mjs +162 -0
- package/scripts/run-lifecycle.mjs +40 -0
- package/scripts/strip-trailing-whitespace.mjs +14 -0
- package/scripts/tsup-action.config.mjs +19 -0
- package/scripts/verify-publish-source-lock.mjs +37 -0
- package/scripts/verify-release-pr.mjs +34 -0
- package/scripts/web-surface-core.mjs +382 -0
- package/scripts/web-surface.mjs +112 -0
package/README.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Kungfu Buildchain
|
|
2
|
+
|
|
3
|
+
Kungfu Buildchain is the v2 source of truth for Kungfu reusable GitHub
|
|
4
|
+
workflows, the minimal Buildchain-native GitHub Actions surface, and release-line
|
|
5
|
+
automation.
|
|
6
|
+
|
|
7
|
+
The repository does more than collect workflow files. Its main job is to make a
|
|
8
|
+
Kungfu release auditable and repeatable: a protected branch merge should produce
|
|
9
|
+
the right version commit, exact release tag, floating channel tag, and next
|
|
10
|
+
alpha line without a maintainer hand-moving refs or repairing package metadata
|
|
11
|
+
after the fact.
|
|
12
|
+
|
|
13
|
+
## Why This Exists
|
|
14
|
+
|
|
15
|
+
Kungfu release automation has to solve a few problems at the same time:
|
|
16
|
+
|
|
17
|
+
- consumers need stable refs such as `v2`, `v2.0`, and `v2.0-alpha`;
|
|
18
|
+
- maintainers need exact immutable refs such as `v2.0.2` and
|
|
19
|
+
`v2.0.3-alpha.0` for audit and rollback;
|
|
20
|
+
- package manifests must record the same version that the release tag
|
|
21
|
+
advertises;
|
|
22
|
+
- alpha and release promotion must follow reviewed PRs, not local scripts or
|
|
23
|
+
manually edited tags;
|
|
24
|
+
- the release toolchain itself must be released by the same governance model
|
|
25
|
+
that it applies to product repositories.
|
|
26
|
+
|
|
27
|
+
The older ABV model solved this by treating a GitHub release PR as the release
|
|
28
|
+
intent. Buildchain v2 keeps that semantic contract, but implements it inside a
|
|
29
|
+
modern monorepo with Node 24 actions, pnpm, tsup bundles, committed `dist`
|
|
30
|
+
outputs, reusable workflow tests, package-manager adapters, and a TOML lifecycle
|
|
31
|
+
protocol for non-Node projects.
|
|
32
|
+
|
|
33
|
+
## Mental Model
|
|
34
|
+
|
|
35
|
+
Buildchain release automation is branch-driven:
|
|
36
|
+
|
|
37
|
+
| Merge path | Meaning | Exact tag | Floating tags and branches |
|
|
38
|
+
| --- | --- | --- | --- |
|
|
39
|
+
| `dev/vX/vX.Y -> alpha/vX/vX.Y` | publish the next testable alpha for a minor line | `vX.Y.Z-alpha.N` | `vX.Y-alpha`, `alpha/vX/vX.Y`, `dev/vX/vX.Y` |
|
|
40
|
+
| `alpha/vX/vX.Y -> release/vX/vX.Y` | publish production for that minor line | `vX.Y.Z` | `vX.Y`, usually `vX`, `release/vX/vX.Y` |
|
|
41
|
+
| `release/vX/vX.Y -> publish-gate/major` | publish the next major from a reviewed production line | `v(X+1).0.0` | `v(X+1)`, `v(X+1).0`, `release/v(X+1)/v(X+1).0` |
|
|
42
|
+
|
|
43
|
+
After a production release, Buildchain prepares the next alpha source commit for
|
|
44
|
+
the same minor line and moves `dev/vX/vX.Y`, `alpha/vX/vX.Y`, and
|
|
45
|
+
`vX.Y-alpha` to that next prerelease state. This is why a release can leave the
|
|
46
|
+
production channel at `v2.0.2` while the test channel is already at
|
|
47
|
+
`v2.0.3-alpha.0`.
|
|
48
|
+
|
|
49
|
+
Kungfu treats minor lines as long-running product trains. `v2.0`, `v2.1`, and
|
|
50
|
+
future minor refs can each receive many patch releases such as `v2.0.1234`.
|
|
51
|
+
`v2` points at the selected stable major line, while `v2.0` points at the
|
|
52
|
+
latest production patch for that minor line.
|
|
53
|
+
|
|
54
|
+
`publish-gate/major` replaces the old ABV `main` channel. It is deliberately
|
|
55
|
+
not named `main` because it is not the active development trunk. Maintainers use
|
|
56
|
+
the same PR flow as other channel promotions: merging
|
|
57
|
+
`release/v2/v2.0 -> publish-gate/major` means "publish the next major line from
|
|
58
|
+
this production state." The promotion then creates the next major production
|
|
59
|
+
version, for example `v3.0.0`, and prepares `dev/v3/v3.0` plus
|
|
60
|
+
`alpha/v3/v3.0` at `v3.0.1-alpha.0`. The older `major-gate` branch name is only
|
|
61
|
+
a compatibility alias during migration.
|
|
62
|
+
|
|
63
|
+
Exact tags are always v-prefixed. Use `v2.0.0` and `v2.0.1-alpha.0`; bare tags
|
|
64
|
+
such as `1.0.0` are not maintained as Buildchain release entrypoints.
|
|
65
|
+
|
|
66
|
+
## Repository Layout
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
.github/workflows/ Repository checks, reusable workflows, and release promotion
|
|
70
|
+
actions/ GitHub Actions implementations, grouped by action
|
|
71
|
+
fixtures/ Safe fixture repositories or fixture descriptors
|
|
72
|
+
packages/ Shared libraries, added only when justified
|
|
73
|
+
tests/ Inventory and contract data used by checks
|
|
74
|
+
docs/ Governance, migration, architecture, and rollback notes
|
|
75
|
+
scripts/ Local verification scripts
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Buildchain v2 Contract
|
|
79
|
+
|
|
80
|
+
Buildchain v2 ships these active surfaces:
|
|
81
|
+
|
|
82
|
+
- repository workflows under `.github/workflows`;
|
|
83
|
+
- the public npm package `@kungfu-tech/buildchain` and `buildchain` CLI for
|
|
84
|
+
initializing and validating new repositories;
|
|
85
|
+
- the active reusable build workflow `.github/workflows/.build.yml`;
|
|
86
|
+
- exactly three GitHub Actions under `actions/<name>`:
|
|
87
|
+
`validate-config`, `run-lifecycle`, and `promote-buildchain-ref`;
|
|
88
|
+
- action runtime on Node 24;
|
|
89
|
+
- workspace package management with pnpm;
|
|
90
|
+
- action bundling through tsup;
|
|
91
|
+
- committed `dist/index.js` bundles for direct GitHub Actions consumption;
|
|
92
|
+
- package-manager adapters for pnpm, npm, and yarn version-state updates;
|
|
93
|
+
- `buildchain.toml` lifecycle configuration for custom version files and
|
|
94
|
+
verification commands;
|
|
95
|
+
- `project.type = "web-surface"` configuration for site and app repositories
|
|
96
|
+
that need preview/staging/production deployment manifests and dry-run deploy
|
|
97
|
+
plans without package-release version semantics;
|
|
98
|
+
- opt-in anchored/manual version strategy for packages whose version is pinned
|
|
99
|
+
to an explicit upstream release instead of derived from the Buildchain tag;
|
|
100
|
+
- `actions/validate-config` migration preflight for TOML version-state and
|
|
101
|
+
lifecycle declarations without running heavyweight builds;
|
|
102
|
+
- `.github/workflows/.build.yml` as the reusable build surface for
|
|
103
|
+
tri-platform runner presets, custom runner matrices, caller-provided lifecycle
|
|
104
|
+
commands, trusted event gating, artifact name templates, expected artifact
|
|
105
|
+
checks, deterministic artifact manifests, publish-gate source locks, resolved
|
|
106
|
+
release manifests, and aggregate build summaries;
|
|
107
|
+
- `actions/run-lifecycle` for callers that need the same lifecycle/manifest
|
|
108
|
+
contract inside their own workflows;
|
|
109
|
+
- governance-closed self-promotion through `Buildchain Ref Promotion`.
|
|
110
|
+
|
|
111
|
+
Stable consumers should reference actions as:
|
|
112
|
+
|
|
113
|
+
```yaml
|
|
114
|
+
uses: kungfu-systems/buildchain/actions/<name>@v2
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Documented reusable workflows should be referenced as:
|
|
118
|
+
|
|
119
|
+
```yaml
|
|
120
|
+
uses: kungfu-systems/buildchain/.github/workflows/<workflow>.yml@v2
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Repositories can also bootstrap local integration through the CLI:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npx @kungfu-tech/buildchain init --type package
|
|
127
|
+
npx @kungfu-tech/buildchain validate --require-version-state
|
|
128
|
+
npx @kungfu-tech/buildchain release --dry-run --target-ref alpha/v2/v2.0
|
|
129
|
+
npx @kungfu-tech/buildchain npm dry-run --json
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Standalone `workflows` and `action-*` repositories are historical rollback
|
|
133
|
+
anchors. Buildchain no longer ships migrated copies of legacy action
|
|
134
|
+
repositories.
|
|
135
|
+
|
|
136
|
+
Some hidden reusable workflow files from the old `workflows` repository still
|
|
137
|
+
exist in this repository so they can be linted, audited, and made fail-closed
|
|
138
|
+
while consumers migrate. They are not Buildchain-native release or publish
|
|
139
|
+
surfaces unless this README or a dedicated document explicitly names them.
|
|
140
|
+
Modern package, artifact, and publish integrations should use `.build.yml`,
|
|
141
|
+
`buildchain.toml`, lifecycle commands, and publish transaction evidence instead
|
|
142
|
+
of retired legacy action paths.
|
|
143
|
+
|
|
144
|
+
## Release Governance
|
|
145
|
+
|
|
146
|
+
Buildchain promotes its own release refs through
|
|
147
|
+
`.github/workflows/buildchain-ref-promotion.yml` and
|
|
148
|
+
`actions/promote-buildchain-ref`.
|
|
149
|
+
|
|
150
|
+
The important constraints are:
|
|
151
|
+
|
|
152
|
+
- non-dry-run promotion is not available from manual dispatch;
|
|
153
|
+
- promotion must be backed by a protected same-repository PR channel path;
|
|
154
|
+
- protected channel details must be readable and must enforce protection for
|
|
155
|
+
administrators;
|
|
156
|
+
- alpha promotion must come from `dev/vX/vX.Y -> alpha/vX/vX.Y`;
|
|
157
|
+
- release promotion must come from `alpha/vX/vX.Y -> release/vX/vX.Y`;
|
|
158
|
+
- major promotion must come from `release/vX/vX.Y -> publish-gate/major`;
|
|
159
|
+
- release promotion must match an existing same-patch alpha tag tree;
|
|
160
|
+
- generated version-state commits must pass the configured verification command
|
|
161
|
+
before refs move;
|
|
162
|
+
- `BUILDCHAIN_PROMOTION_TOKEN` is the release authority used to move protected
|
|
163
|
+
refs when repository policy requires it.
|
|
164
|
+
|
|
165
|
+
Buildchain's top-level `Release - New Version` workflow is intentionally a
|
|
166
|
+
no-op for this repository. Buildchain itself is promoted only by
|
|
167
|
+
`Buildchain Ref Promotion` after `Verify` succeeds. The legacy hidden
|
|
168
|
+
`.release-new-version.yml` file is not the modern publish surface; it remains a
|
|
169
|
+
migration boundary for old consumers and must not be used for new Buildchain v2
|
|
170
|
+
integrations.
|
|
171
|
+
|
|
172
|
+
Buildchain's npm package is published by the same promotion transaction that
|
|
173
|
+
creates exact v-prefixed release tags. Alpha releases such as
|
|
174
|
+
`v2.0.13-alpha.0` publish to npm with dist-tag `alpha`; stable releases such as
|
|
175
|
+
`v2.0.13` publish with dist-tag `latest`. Floating refs like `v2`, `v2.0`, and
|
|
176
|
+
`v2.0-alpha` never publish npm packages. The old tag-push npm workflow is
|
|
177
|
+
dry-run only; real npm publish happens before public release refs move, using
|
|
178
|
+
the evidence contract in `lifecycle.publish`.
|
|
179
|
+
|
|
180
|
+
For release-line planning, use the CLI dry-run:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
buildchain release --dry-run --target-ref release/v2/v2.0 --sha <verified-sha>
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
This prints the branch/tag/version-state/governance plan that a channel merge
|
|
187
|
+
would trigger. It is intentionally separate from `buildchain npm dry-run`, which
|
|
188
|
+
only checks package publish shape.
|
|
189
|
+
|
|
190
|
+
## Read Next
|
|
191
|
+
|
|
192
|
+
- [Release governance](docs/release-governance.md) explains the design problem,
|
|
193
|
+
old ABV compatibility, hard constraints, and operational guarantees.
|
|
194
|
+
- [Release flow diagrams](docs/release-flow.md) gives the branch/tag state
|
|
195
|
+
machine and Mermaid diagrams.
|
|
196
|
+
- [Migration inventory](docs/migration-inventory.md) lists migrated and retired
|
|
197
|
+
action repositories.
|
|
198
|
+
- [Ownership rules](docs/ownership.md) defines source-of-truth and rollback
|
|
199
|
+
boundaries.
|
|
200
|
+
- [promote-buildchain-ref](actions/promote-buildchain-ref/README.md) documents
|
|
201
|
+
the internal promotion action.
|
|
202
|
+
- [Lifecycle protocol](docs/lifecycle-protocol.md) documents `buildchain.toml`
|
|
203
|
+
for custom version-state files and lifecycle commands.
|
|
204
|
+
- [Reusable build surface](docs/reusable-build-surface.md) documents the build
|
|
205
|
+
workflow, local runner matrix, artifact contract, and libnode-shaped fixture.
|
|
206
|
+
- [Web-surface deployments](docs/web-surface-deployments.md) documents the
|
|
207
|
+
preview/staging/production channel ontology, deployment manifest, adapter
|
|
208
|
+
contract, and dry-run cleanup/deploy plans.
|
|
209
|
+
- [Buildchain CLI and npm package](docs/cli.md) documents the npm package,
|
|
210
|
+
command-line entrypoint, repository bootstrap flow, and release-only publish
|
|
211
|
+
gate.
|
|
212
|
+
|
|
213
|
+
## Local Verification
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
corepack enable pnpm
|
|
217
|
+
pnpm install --frozen-lockfile
|
|
218
|
+
pnpm run check
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`pnpm run check` validates inventory data, lints all root workflows including
|
|
222
|
+
hidden reusable workflows, and rebuilds every action bundle.
|
|
223
|
+
|
|
224
|
+
## Lifecycle Configuration
|
|
225
|
+
|
|
226
|
+
Projects can add `buildchain.toml` to declare release version state and
|
|
227
|
+
lifecycle commands. Buildchain v2 supports TOML only. The promotion action uses
|
|
228
|
+
configured version files to create source version commits, then runs
|
|
229
|
+
`lifecycle.verify` before moving release refs. Repositories that publish
|
|
230
|
+
external artifacts can also declare `lifecycle.publish`; with publish
|
|
231
|
+
transactions enabled, Buildchain requires machine-readable evidence before exact
|
|
232
|
+
release tags and floating channel refs move.
|
|
233
|
+
|
|
234
|
+
Web-surface projects can also declare `project.type = "web-surface"` with
|
|
235
|
+
preview, staging, and production channels. Those projects get deployment
|
|
236
|
+
manifests and dry-run deploy/cleanup plans, but they are not forced into package
|
|
237
|
+
version lines or anchored release semantics.
|
|
238
|
+
|
|
239
|
+
## Safety Defaults
|
|
240
|
+
|
|
241
|
+
- Lab workflows are manual by default.
|
|
242
|
+
- Publishing is disabled unless explicitly enabled by a production release
|
|
243
|
+
workflow. Reusable builds expose `publish-channel`, `publish-allowed`, and
|
|
244
|
+
`publish-reason` so callers can gate publish jobs on a reviewed channel ref
|
|
245
|
+
instead of inferring publish eligibility from ad hoc branch checks.
|
|
246
|
+
- Publish gate branches are resolved to a source SHA before checkout. Builds,
|
|
247
|
+
verification, artifact manifests, and summaries use that locked SHA; publish
|
|
248
|
+
jobs must re-check the branch tip before external side effects.
|
|
249
|
+
- Publish transactions are keyed by repository, version, source SHA, and target
|
|
250
|
+
ref rather than run id. Reruns may resume missing artifacts, accept matching
|
|
251
|
+
existing artifacts, and fail closed on conflicting material until an explicit
|
|
252
|
+
repair override is used.
|
|
253
|
+
- Fork pull requests must not reach secrets or self-hosted runners.
|
|
254
|
+
- Candidate refs are expected to come from `kungfu-systems/*`.
|
|
255
|
+
- Self-hosted runner validation is available only through the manual
|
|
256
|
+
`Self-hosted Runner Smoke` workflow.
|
|
257
|
+
|
|
258
|
+
## Read Next
|
|
259
|
+
|
|
260
|
+
- [Lifecycle protocol](docs/lifecycle-protocol.md)
|
|
261
|
+
- [Publish transaction](docs/publish-transaction.md)
|
|
262
|
+
- [Reusable build surface](docs/reusable-build-surface.md)
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { initBuildchainRepo } from "../scripts/init-repo.mjs";
|
|
7
|
+
import { npmPublishDryRun } from "../scripts/npm-publish-dry-run.mjs";
|
|
8
|
+
import { runLifecycle } from "../scripts/run-lifecycle-core.mjs";
|
|
9
|
+
import { validateBuildchainConfig } from "../packages/core/buildchain-config.js";
|
|
10
|
+
import {
|
|
11
|
+
explainReleaseLineDryRun,
|
|
12
|
+
formatReleaseLineDryRun,
|
|
13
|
+
} from "../packages/core/release-line-dry-run.js";
|
|
14
|
+
|
|
15
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
16
|
+
|
|
17
|
+
function usage() {
|
|
18
|
+
return `Usage:
|
|
19
|
+
buildchain --help
|
|
20
|
+
buildchain version
|
|
21
|
+
buildchain init [--cwd <dir>] [--type package|native|web-surface|anchored-package] [--force]
|
|
22
|
+
[--package-manager pnpm|npm|yarn] [--runner-preset <preset>]
|
|
23
|
+
[--artifact-name <template>]
|
|
24
|
+
buildchain validate [--cwd <dir>] [--require-version-state]
|
|
25
|
+
[--require-lifecycle-stages <comma-list>]
|
|
26
|
+
buildchain lifecycle run <stage> [--cwd <dir>] [--required]
|
|
27
|
+
[--artifact-name <name>] [--artifact-path <path>]...
|
|
28
|
+
buildchain npm dry-run [--cwd <dir>] [--expected-tag <tag>] [--registry <url>]
|
|
29
|
+
[--dist-tag <tag>] [--skip-npm-publish-dry-run] [--json]
|
|
30
|
+
buildchain release --dry-run --target-ref <ref> [--sha <sha>] [--source-ref <ref>]
|
|
31
|
+
[--tags <comma-list>] [--json]
|
|
32
|
+
buildchain release dry-run --target-ref <ref> [--sha <sha>] [--source-ref <ref>]
|
|
33
|
+
[--tags <comma-list>] [--json]
|
|
34
|
+
buildchain release <inspect|recover|finalize|abort> ...
|
|
35
|
+
buildchain web-surface ...
|
|
36
|
+
buildchain publish-source <lock|manifest|verify-lock> ...
|
|
37
|
+
buildchain build-contract ...
|
|
38
|
+
|
|
39
|
+
Examples:
|
|
40
|
+
buildchain init --type package --package-manager pnpm
|
|
41
|
+
buildchain validate --require-version-state --require-lifecycle-stages build,verify
|
|
42
|
+
buildchain lifecycle run build --artifact-path dist --artifact-name "{repo}-{version}-{platform}"
|
|
43
|
+
buildchain npm dry-run --json
|
|
44
|
+
buildchain release --dry-run --target-ref alpha/v2/v2.0
|
|
45
|
+
`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function readFlag(args, name, fallback = "") {
|
|
49
|
+
const index = args.indexOf(`--${name}`);
|
|
50
|
+
if (index === -1) {
|
|
51
|
+
return fallback;
|
|
52
|
+
}
|
|
53
|
+
return args[index + 1] || "";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function readBooleanFlag(args, name) {
|
|
57
|
+
return args.includes(`--${name}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function readRepeatedFlag(args, name) {
|
|
61
|
+
const values = [];
|
|
62
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
63
|
+
if (args[index] === `--${name}` && args[index + 1]) {
|
|
64
|
+
values.push(args[index + 1]);
|
|
65
|
+
index += 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return values;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function runScript(scriptName, args) {
|
|
72
|
+
const scriptPath = path.join(root, "scripts", scriptName);
|
|
73
|
+
const result = spawnSync(process.execPath, [scriptPath, ...args], {
|
|
74
|
+
cwd: process.cwd(),
|
|
75
|
+
env: process.env,
|
|
76
|
+
stdio: "inherit",
|
|
77
|
+
});
|
|
78
|
+
if (result.error) {
|
|
79
|
+
throw result.error;
|
|
80
|
+
}
|
|
81
|
+
process.exitCode = result.status ?? 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function printJson(value) {
|
|
85
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function packageVersion() {
|
|
89
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
90
|
+
return packageJson.version;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function main(argv = process.argv.slice(2)) {
|
|
94
|
+
const [command, ...args] = argv;
|
|
95
|
+
if (!command || command === "-h" || command === "--help" || command === "help") {
|
|
96
|
+
process.stdout.write(usage());
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (command === "version" || command === "--version" || command === "-v") {
|
|
101
|
+
process.stdout.write(`${packageVersion()}\n`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (command === "init") {
|
|
106
|
+
const result = initBuildchainRepo({
|
|
107
|
+
cwd: readFlag(args, "cwd", process.cwd()),
|
|
108
|
+
type: readFlag(args, "type", "package"),
|
|
109
|
+
force: readBooleanFlag(args, "force"),
|
|
110
|
+
packageManager: readFlag(args, "package-manager", ""),
|
|
111
|
+
runnerPreset: readFlag(args, "runner-preset", "github-hosted"),
|
|
112
|
+
artifactName: readFlag(args, "artifact-name", "{repo}-{version}-{platform}"),
|
|
113
|
+
});
|
|
114
|
+
printJson(result);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (command === "validate") {
|
|
119
|
+
const lifecycleStages = readFlag(args, "require-lifecycle-stages", "")
|
|
120
|
+
.split(",")
|
|
121
|
+
.map((entry) => entry.trim())
|
|
122
|
+
.filter(Boolean);
|
|
123
|
+
printJson(validateBuildchainConfig(readFlag(args, "cwd", process.cwd()), {
|
|
124
|
+
requireVersionState: readBooleanFlag(args, "require-version-state"),
|
|
125
|
+
requireLifecycleStages: lifecycleStages,
|
|
126
|
+
}));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (command === "lifecycle") {
|
|
131
|
+
const [subcommand, stageName = "", ...lifecycleArgs] = args;
|
|
132
|
+
if (subcommand !== "run" || !stageName) {
|
|
133
|
+
throw new Error("usage: buildchain lifecycle run <stage>");
|
|
134
|
+
}
|
|
135
|
+
const artifactPaths = readRepeatedFlag(lifecycleArgs, "artifact-path");
|
|
136
|
+
const manifest = runLifecycle({
|
|
137
|
+
cwd: readFlag(lifecycleArgs, "cwd", process.cwd()),
|
|
138
|
+
stageName,
|
|
139
|
+
required: readBooleanFlag(lifecycleArgs, "required"),
|
|
140
|
+
artifactName: readFlag(lifecycleArgs, "artifact-name", "buildchain-artifact"),
|
|
141
|
+
artifactPaths,
|
|
142
|
+
expectedArtifactsJson: readFlag(lifecycleArgs, "expected-artifacts-json", ""),
|
|
143
|
+
workspace: process.cwd(),
|
|
144
|
+
});
|
|
145
|
+
printJson(manifest);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (command === "npm") {
|
|
150
|
+
const [subcommand = "", ...npmArgs] = args;
|
|
151
|
+
if (subcommand !== "dry-run") {
|
|
152
|
+
throw new Error("usage: buildchain npm dry-run");
|
|
153
|
+
}
|
|
154
|
+
const result = npmPublishDryRun({
|
|
155
|
+
cwd: readFlag(npmArgs, "cwd", process.cwd()),
|
|
156
|
+
expectedTag: readFlag(npmArgs, "expected-tag", ""),
|
|
157
|
+
registry: readFlag(npmArgs, "registry", "https://registry.npmjs.org/"),
|
|
158
|
+
distTag: readFlag(npmArgs, "dist-tag", ""),
|
|
159
|
+
skipNpmPublishDryRun: readBooleanFlag(npmArgs, "skip-npm-publish-dry-run"),
|
|
160
|
+
});
|
|
161
|
+
if (readBooleanFlag(npmArgs, "json")) {
|
|
162
|
+
printJson(result);
|
|
163
|
+
} else {
|
|
164
|
+
process.stdout.write(`npm publish dry-run ok: ${result.package.name}@${result.package.version} -> ${result.distTag}\n`);
|
|
165
|
+
process.stdout.write(`pack entries: ${result.pack.entryCount}\n`);
|
|
166
|
+
}
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (command === "release") {
|
|
171
|
+
const releaseArgs = args[0] === "dry-run" ? args.slice(1) : args;
|
|
172
|
+
if (args[0] === "dry-run" || readBooleanFlag(args, "dry-run")) {
|
|
173
|
+
const plan = explainReleaseLineDryRun({
|
|
174
|
+
cwd: readFlag(releaseArgs, "cwd", process.cwd()),
|
|
175
|
+
targetRef: readFlag(releaseArgs, "target-ref", ""),
|
|
176
|
+
sourceRef: readFlag(releaseArgs, "source-ref", ""),
|
|
177
|
+
sha: readFlag(releaseArgs, "sha", ""),
|
|
178
|
+
tags: readFlag(releaseArgs, "tags", ""),
|
|
179
|
+
publishTransaction: readBooleanFlag(releaseArgs, "publish-transaction"),
|
|
180
|
+
publishCommand: readFlag(releaseArgs, "publish-command", ""),
|
|
181
|
+
});
|
|
182
|
+
if (readBooleanFlag(releaseArgs, "json")) {
|
|
183
|
+
printJson(plan);
|
|
184
|
+
} else {
|
|
185
|
+
process.stdout.write(formatReleaseLineDryRun(plan));
|
|
186
|
+
}
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
runScript("release-transaction.mjs", args);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (command === "web-surface") {
|
|
194
|
+
runScript("web-surface.mjs", args);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (command === "build-contract") {
|
|
199
|
+
runScript("resolve-build-contract.mjs", args);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (command === "publish-source") {
|
|
204
|
+
const [mode = "lock", ...publishArgs] = args;
|
|
205
|
+
if (mode === "lock" || mode === "manifest") {
|
|
206
|
+
runScript("resolve-publish-source.mjs", ["--mode", mode, ...publishArgs]);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (mode === "verify-lock") {
|
|
210
|
+
runScript("verify-publish-source-lock.mjs", publishArgs);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
throw new Error(`unsupported publish-source command: ${mode}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
throw new Error(`unsupported buildchain command: ${command}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
main().catch((error) => {
|
|
220
|
+
console.error(`buildchain: ${error.message}`);
|
|
221
|
+
process.exitCode = 1;
|
|
222
|
+
});
|
package/docs/cli.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Buildchain CLI and npm Package
|
|
2
|
+
|
|
3
|
+
Buildchain is published as the public npm package
|
|
4
|
+
`@kungfu-tech/buildchain`. The package contains the `buildchain` command,
|
|
5
|
+
the shared core libraries, and the local scripts needed to initialize and
|
|
6
|
+
validate repositories before they use the reusable GitHub workflow surface.
|
|
7
|
+
|
|
8
|
+
The npm package is not the release authority. Release authority still comes
|
|
9
|
+
from the protected Buildchain branch and tag state machine. npm publishing is a
|
|
10
|
+
side effect of an exact release tag that has already been produced by that
|
|
11
|
+
state machine.
|
|
12
|
+
|
|
13
|
+
## Install and Run
|
|
14
|
+
|
|
15
|
+
Use the published package directly:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx @kungfu-tech/buildchain --help
|
|
19
|
+
npx @kungfu-tech/buildchain init --type package
|
|
20
|
+
npx @kungfu-tech/buildchain validate --require-version-state
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or install it in a repository:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add -D @kungfu-tech/buildchain
|
|
27
|
+
pnpm exec buildchain validate
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Commands
|
|
31
|
+
|
|
32
|
+
`buildchain init` writes a starter `buildchain.toml` and a reusable workflow
|
|
33
|
+
caller at `.github/workflows/build.yml`.
|
|
34
|
+
|
|
35
|
+
Supported presets:
|
|
36
|
+
|
|
37
|
+
- `--type package` for Node package repositories with pnpm, npm, or yarn.
|
|
38
|
+
- `--type native` for CMake-style native projects.
|
|
39
|
+
- `--type web-surface` for preview/staging/production site or app deployments.
|
|
40
|
+
- `--type anchored-package` for packages whose version is anchored to an
|
|
41
|
+
explicit upstream release manifest.
|
|
42
|
+
|
|
43
|
+
`buildchain validate` parses `buildchain.toml`, checks configured version-state
|
|
44
|
+
files, and can require named lifecycle stages:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
buildchain validate \
|
|
48
|
+
--require-version-state \
|
|
49
|
+
--require-lifecycle-stages install,build,verify
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`buildchain lifecycle run <stage>` executes a lifecycle stage and writes the
|
|
53
|
+
same deterministic artifact manifest contract used by the reusable workflow:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
buildchain lifecycle run build \
|
|
57
|
+
--artifact-path dist \
|
|
58
|
+
--artifact-name "{repo}-{version}-{platform}"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`buildchain release`, `buildchain web-surface`, `buildchain publish-source`,
|
|
62
|
+
and `buildchain build-contract` route to the same scripts used by Buildchain's
|
|
63
|
+
GitHub Actions workflows. This keeps local inspection and CI behavior on the
|
|
64
|
+
same implementation path.
|
|
65
|
+
|
|
66
|
+
`buildchain release --dry-run` explains the release-line state machine before a
|
|
67
|
+
maintainer opens or merges a channel PR:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
buildchain release --dry-run --target-ref alpha/v2/v2.0
|
|
71
|
+
buildchain release --dry-run --target-ref release/v2/v2.0 --sha <verified-sha>
|
|
72
|
+
buildchain release dry-run --target-ref publish-gate/major --source-ref release/v2/v2.0
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This is a Buildchain-level dry-run, not an npm dry-run. It explains the legal
|
|
76
|
+
source branch, exact release or alpha tags, floating tags, channel branches,
|
|
77
|
+
version-state files, governance checks, and publish transaction behavior that
|
|
78
|
+
would apply if the corresponding PR merge were promoted. It does not move
|
|
79
|
+
branches, move tags, edit files, publish npm packages, or run lifecycle publish
|
|
80
|
+
commands. Pass `--json` for a machine-readable plan.
|
|
81
|
+
|
|
82
|
+
`buildchain npm dry-run` verifies the package shape before a release tag exists:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
buildchain npm dry-run --json
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The command validates `package.json`, infers the exact release tag
|
|
89
|
+
`v${package.json.version}`, chooses npm dist-tag `alpha` for prereleases and
|
|
90
|
+
`latest` for stable releases, runs `npm pack --dry-run --json`, and then runs
|
|
91
|
+
`npm publish --dry-run --access public --tag <alpha|latest>` unless
|
|
92
|
+
`--skip-npm-publish-dry-run` is passed. It never performs a real publish.
|
|
93
|
+
|
|
94
|
+
## npm Publish Gate
|
|
95
|
+
|
|
96
|
+
Buildchain's own npm package is published from
|
|
97
|
+
`.github/workflows/buildchain-ref-promotion.yml`, inside the same publish
|
|
98
|
+
transaction that promotes release refs:
|
|
99
|
+
|
|
100
|
+
- `v2.0.13-alpha.0` publishes to npm with dist-tag `alpha`.
|
|
101
|
+
- `v2.0.13` publishes to npm with dist-tag `latest`.
|
|
102
|
+
- moving refs such as `v2`, `v2.0`, and `v2.0-alpha` do not match the publish
|
|
103
|
+
workflow and do not publish.
|
|
104
|
+
|
|
105
|
+
The promotion workflow uses npm Trusted Publishing through GitHub Actions OIDC.
|
|
106
|
+
It runs on a GitHub-hosted runner with `id-token: write`, generates the
|
|
107
|
+
version-state commit, runs `lifecycle.verify`, runs `lifecycle.publish`, writes
|
|
108
|
+
Buildchain publish evidence, validates that evidence, and only then moves exact
|
|
109
|
+
tags and floating refs.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
node scripts/npm-publish-transaction.mjs
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Before the first real release, configure npm Trusted Publishing for:
|
|
116
|
+
|
|
117
|
+
- package: `@kungfu-tech/buildchain`
|
|
118
|
+
- repository: `kungfu-systems/buildchain`
|
|
119
|
+
- workflow: `.github/workflows/buildchain-ref-promotion.yml`
|
|
120
|
+
|
|
121
|
+
No npm package is published by manual dispatch or ordinary branch builds.
|
|
122
|
+
Manual dispatch on `.github/workflows/npm-publish.yml` remains dry-run only, so
|
|
123
|
+
maintainers can verify package contents and npm publish shape before opening or
|
|
124
|
+
merging the release PR.
|