@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
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# Reusable Build Surface
|
|
2
|
+
|
|
3
|
+
Buildchain v2 provides a reusable build workflow for repositories that need
|
|
4
|
+
Buildchain's release semantics but cannot be described as a simple Node package.
|
|
5
|
+
The first target shape is `libnode`: expensive native builds, multiple operating
|
|
6
|
+
systems, self-hosted runner labels, and release artifacts that must be auditable.
|
|
7
|
+
|
|
8
|
+
## Workflow
|
|
9
|
+
|
|
10
|
+
Stable consumers call:
|
|
11
|
+
|
|
12
|
+
```yaml
|
|
13
|
+
jobs:
|
|
14
|
+
build:
|
|
15
|
+
uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2
|
|
16
|
+
with:
|
|
17
|
+
working-directory: .
|
|
18
|
+
artifact-name: libnode
|
|
19
|
+
runner-preset: kungfu-v4-self-hosted
|
|
20
|
+
artifact-name-template: "{artifact}-{platform}-{sha}"
|
|
21
|
+
artifact-paths: |
|
|
22
|
+
dist
|
|
23
|
+
build/stage
|
|
24
|
+
expected-artifacts-json: >-
|
|
25
|
+
{"minFiles":2,"requiredPaths":["dist/libnode.tar.gz","dist/checksums.txt"]}
|
|
26
|
+
publish-channel: release
|
|
27
|
+
publish-source-ref: publish-gate/release/v22/v22.22/22.22.3-kf.0
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`runner-preset` is the stable first-class surface for known runner fleets:
|
|
31
|
+
|
|
32
|
+
| Preset | Platforms |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| `github-hosted` | `ubuntu-24.04`, `macos-latest`, `windows-2022` |
|
|
35
|
+
| `kungfu-v4-self-hosted` | Kungfu Linux x64, macOS ARM64, and Windows x64 self-hosted runner labels |
|
|
36
|
+
| `custom` | Requires `platforms-json` |
|
|
37
|
+
|
|
38
|
+
Callers can still provide a custom matrix with `platforms-json`. Each platform
|
|
39
|
+
object has:
|
|
40
|
+
|
|
41
|
+
| Field | Meaning |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| `id` | Stable artifact/platform key, such as `linux-x64` |
|
|
44
|
+
| `name` | Human-readable job name |
|
|
45
|
+
| `runner` | JSON string passed to `runs-on` after `fromJSON` |
|
|
46
|
+
|
|
47
|
+
The runner field is intentionally a JSON string so callers can pass either
|
|
48
|
+
GitHub-hosted runners or multi-label self-hosted runners without Buildchain
|
|
49
|
+
guessing the labels.
|
|
50
|
+
|
|
51
|
+
Only include platforms that should run. GitHub schedules matrix jobs before
|
|
52
|
+
steps execute, so a disabled entry with unavailable runner labels can still
|
|
53
|
+
block the workflow queue.
|
|
54
|
+
|
|
55
|
+
## Workflow Outputs
|
|
56
|
+
|
|
57
|
+
The reusable workflow exposes the resolved contract:
|
|
58
|
+
|
|
59
|
+
| Output | Meaning |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| `runner-preset` | Resolved preset, or `custom` when `platforms-json` was provided |
|
|
62
|
+
| `platforms-json` | Exact matrix JSON used by the build job |
|
|
63
|
+
| `platform-count` | Number of matrix platforms |
|
|
64
|
+
| `build-summary-artifact` | Uploaded aggregate summary artifact name |
|
|
65
|
+
| `build-summary-json` | Compact aggregate JSON with platform count, file count, and byte total |
|
|
66
|
+
| `trusted-event` | `true` when the event is trusted enough to reach build runners |
|
|
67
|
+
| `publish-channel` | Resolved publish channel requested by the caller |
|
|
68
|
+
| `publish-allowed` | `true` only when this event/ref may publish after verification |
|
|
69
|
+
| `publish-reason` | Human-readable reason for the publish gate decision |
|
|
70
|
+
| `publish-source-ref` | Gate source ref that was resolved before checkout |
|
|
71
|
+
| `publish-source-sha` | Exact source commit used by checkout, build, verify, and artifacts |
|
|
72
|
+
| `publish-source-locked` | `true` when a `publish-gate/*` source ref was explicitly locked |
|
|
73
|
+
| `publish-source-channel` | `alpha`, `release`, `anchor`, or `major` parsed from the source ref |
|
|
74
|
+
| `publish-source-line` | Product line parsed from source refs such as `v22/v22.22` |
|
|
75
|
+
| `publish-source-consumer-version` | Consumer package version parsed from source refs |
|
|
76
|
+
| `release-manifest-json` | Resolved release manifest including source lock, version state, and anchor data |
|
|
77
|
+
|
|
78
|
+
The aggregate summary is intentionally an artifact as well as an output. GitHub
|
|
79
|
+
Actions matrix outputs are not a reliable place to carry every platform's full
|
|
80
|
+
manifest, so Buildchain uploads each platform manifest and then emits one
|
|
81
|
+
aggregate summary artifact after the matrix completes.
|
|
82
|
+
|
|
83
|
+
## Publish Gate
|
|
84
|
+
|
|
85
|
+
Buildchain separates "may build/verify" from "may publish." A same-repository
|
|
86
|
+
pull request may be trusted enough to run the build matrix, but it still must
|
|
87
|
+
not publish packages, S3 objects, release pages, or preview aliases. Publishing
|
|
88
|
+
is allowed only when the caller explicitly requests a channel and the current
|
|
89
|
+
event/ref matches that channel.
|
|
90
|
+
|
|
91
|
+
Use `publish-channel` to request a channel:
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
jobs:
|
|
95
|
+
build:
|
|
96
|
+
uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v2
|
|
97
|
+
with:
|
|
98
|
+
publish-channel: release
|
|
99
|
+
|
|
100
|
+
publish:
|
|
101
|
+
needs: build
|
|
102
|
+
if: ${{ needs.build.outputs.publish-allowed == 'true' }}
|
|
103
|
+
runs-on: ubuntu-24.04
|
|
104
|
+
steps:
|
|
105
|
+
- run: ./scripts/publish.sh
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Default channels are:
|
|
109
|
+
|
|
110
|
+
| Channel | Allowed refs |
|
|
111
|
+
| --- | --- |
|
|
112
|
+
| `none` | Never publishes; this is the default |
|
|
113
|
+
| `alpha` | `alpha/vN/vN.M` branches or exact `vN.M.P-alpha.K` tags |
|
|
114
|
+
| `release` | `release/vN/vN.M` branches or release tags such as `vN.M.P`, `vN.M`, `vN` |
|
|
115
|
+
| `major` | `publish-gate/major`, legacy `major-gate`, or next-major release tags such as `vN.0.0`, `vN.0`, `vN` |
|
|
116
|
+
|
|
117
|
+
Pull request events always produce `publish-allowed=false`, even when the PR is
|
|
118
|
+
from the same repository. Untrusted fork events also produce
|
|
119
|
+
`publish-allowed=false`; with the default `untrusted-policy: fail`, the workflow
|
|
120
|
+
then fails before any build runner starts.
|
|
121
|
+
|
|
122
|
+
Projects with their own channel names can pass `publish-refs-json`:
|
|
123
|
+
|
|
124
|
+
```yaml
|
|
125
|
+
with:
|
|
126
|
+
publish-channel: nightly
|
|
127
|
+
publish-refs-json: >-
|
|
128
|
+
{"nightly":["^refs/heads/nightly/v\\d+$"]}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The aggregate build summary includes the same publish gate decision under
|
|
132
|
+
`publishGate`, so a downloaded artifact summary explains both what was built and
|
|
133
|
+
why it was or was not eligible to publish.
|
|
134
|
+
|
|
135
|
+
## Publish Source Lock
|
|
136
|
+
|
|
137
|
+
`publish-channel` answers "may this event publish?" Source lock answers "which
|
|
138
|
+
source tree is the publish decision about?" A caller can pass `publish-source-ref`
|
|
139
|
+
to bind a publish run to a reviewed gate branch before any checkout happens:
|
|
140
|
+
|
|
141
|
+
| Ref | Meaning |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| `publish-gate/alpha/<line>/<consumer-version>` | Build and publish an alpha candidate for a consumer line |
|
|
144
|
+
| `publish-gate/release/<line>/<consumer-version>` | Build and publish a production candidate for a consumer line |
|
|
145
|
+
| `publish-gate/anchor` | Resolve an explicit anchor request; it does not publish artifacts by itself |
|
|
146
|
+
| `publish-gate/major` | Gate the next major source state |
|
|
147
|
+
| `major-gate` | Legacy compatibility alias for the major gate |
|
|
148
|
+
|
|
149
|
+
For alpha and release refs, `<line>` is intentionally allowed to contain `/`, so
|
|
150
|
+
Kungfu-style lines such as `v22/v22.22` stay readable. The final path segment is
|
|
151
|
+
the consumer-visible version, for example `22.22.3-kf.0`.
|
|
152
|
+
|
|
153
|
+
The reusable workflow resolves the branch tip to `publish-source-sha`, checks out
|
|
154
|
+
that SHA in every build job, and uses the same SHA in artifact names, manifests,
|
|
155
|
+
and aggregate summaries. Reruns therefore rebuild the same source tree even if a
|
|
156
|
+
gate branch moves later.
|
|
157
|
+
|
|
158
|
+
The resolved release manifest is uploaded as an artifact and emitted as
|
|
159
|
+
`release-manifest-json`. It records:
|
|
160
|
+
|
|
161
|
+
- source ref, source SHA, channel, line, and consumer version;
|
|
162
|
+
- configured version strategy and configured version-state files;
|
|
163
|
+
- each version file's value, with release gates failing closed if the configured
|
|
164
|
+
files do not equal the consumer version;
|
|
165
|
+
- anchor manifest summary for anchored/manual projects;
|
|
166
|
+
- explicit anchor request JSON for `publish-gate/anchor`;
|
|
167
|
+
- publish registry, dist-tag, and gate visibility metadata.
|
|
168
|
+
|
|
169
|
+
Publish side-effect jobs should verify the lock immediately before publishing:
|
|
170
|
+
|
|
171
|
+
```yaml
|
|
172
|
+
- name: Verify publish gate did not move
|
|
173
|
+
run: node .buildchain/runtime/scripts/verify-publish-source-lock.mjs
|
|
174
|
+
env:
|
|
175
|
+
BUILDCHAIN_PUBLISH_SOURCE_REF: ${{ needs.build.outputs.publish-source-ref }}
|
|
176
|
+
BUILDCHAIN_PUBLISH_SOURCE_SHA: ${{ needs.build.outputs.publish-source-sha }}
|
|
177
|
+
BUILDCHAIN_SOURCE_REPOSITORY: ${{ github.repository }}
|
|
178
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
If the branch tip no longer matches the manifest SHA, the publish job must fail
|
|
182
|
+
closed. Moving a gate branch creates a new publish decision and should produce a
|
|
183
|
+
new build run.
|
|
184
|
+
|
|
185
|
+
## Package-Set Publish Plan
|
|
186
|
+
|
|
187
|
+
Projects that publish multiple packages should treat package publication as a
|
|
188
|
+
package-set operation. Buildchain's package-set planner uses these rules:
|
|
189
|
+
|
|
190
|
+
- platform packages publish first;
|
|
191
|
+
- the main package publishes last;
|
|
192
|
+
- the dist-tag move happens only after the full package set is present;
|
|
193
|
+
- reruns accept already-published packages only when package name, version, and
|
|
194
|
+
integrity match;
|
|
195
|
+
- an existing package with different integrity is a hard failure.
|
|
196
|
+
|
|
197
|
+
This keeps a consumer from observing a floating dist-tag that points to a main
|
|
198
|
+
package before all platform artifacts for the same source SHA are available.
|
|
199
|
+
|
|
200
|
+
## Command Sources
|
|
201
|
+
|
|
202
|
+
The workflow runs `buildchain.toml` lifecycle stages by default:
|
|
203
|
+
|
|
204
|
+
```toml
|
|
205
|
+
[lifecycle.install]
|
|
206
|
+
command = "corepack yarn install --immutable"
|
|
207
|
+
|
|
208
|
+
[lifecycle.build]
|
|
209
|
+
commands = [
|
|
210
|
+
"corepack yarn make",
|
|
211
|
+
"corepack yarn build",
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
[lifecycle.verify]
|
|
215
|
+
command = "corepack yarn test"
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Callers can override any stage for one invocation:
|
|
219
|
+
|
|
220
|
+
```yaml
|
|
221
|
+
with:
|
|
222
|
+
build-command: cmake --build build --config Release
|
|
223
|
+
verify-command: ctest --test-dir build --output-on-failure
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
For custom workflows, use the action directly:
|
|
227
|
+
|
|
228
|
+
```yaml
|
|
229
|
+
- uses: kungfu-systems/buildchain/actions/run-lifecycle@v2
|
|
230
|
+
with:
|
|
231
|
+
stage: build
|
|
232
|
+
required: "true"
|
|
233
|
+
artifact-name: libnode-linux-x64-${{ github.sha }}
|
|
234
|
+
artifact-paths: |
|
|
235
|
+
dist
|
|
236
|
+
build/stage
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Artifact Contract
|
|
240
|
+
|
|
241
|
+
Each platform upload uses `artifact-name-template`. The default is:
|
|
242
|
+
|
|
243
|
+
```text
|
|
244
|
+
{artifact}-{platform}-{sha}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Supported placeholders are `{artifact}`, `{artifactName}`, `{platform}`,
|
|
248
|
+
`{platformId}`, `{platformName}`, `{sha}`, `{shortSha}`, `{ref}`, `{runId}`,
|
|
249
|
+
and `{runAttempt}`. Invalid GitHub artifact name characters are normalized to
|
|
250
|
+
`-`, so `{ref}` remains deterministic even for refs such as
|
|
251
|
+
`refs/heads/dev/v2/v2.0`.
|
|
252
|
+
|
|
253
|
+
Each platform also writes and uploads:
|
|
254
|
+
|
|
255
|
+
```text
|
|
256
|
+
.buildchain/artifacts/<platform-id>/manifest.json
|
|
257
|
+
.buildchain/artifacts/<platform-id>/summary.json
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
The manifest schema is:
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"schemaVersion": 1,
|
|
265
|
+
"contract": "kungfu-buildchain-artifact",
|
|
266
|
+
"artifactName": "libnode-linux-x64-<sha>",
|
|
267
|
+
"platform": {
|
|
268
|
+
"id": "linux-x64",
|
|
269
|
+
"name": "Linux x64",
|
|
270
|
+
"os": "Linux",
|
|
271
|
+
"arch": "X64"
|
|
272
|
+
},
|
|
273
|
+
"git": {
|
|
274
|
+
"repository": "kungfu-systems/libnode",
|
|
275
|
+
"sha": "<sha>",
|
|
276
|
+
"ref": "<ref>",
|
|
277
|
+
"runId": "<run id>",
|
|
278
|
+
"runAttempt": "<attempt>"
|
|
279
|
+
},
|
|
280
|
+
"lifecycle": {
|
|
281
|
+
"stage": "verify",
|
|
282
|
+
"commandSource": "buildchain.toml",
|
|
283
|
+
"executed": true
|
|
284
|
+
},
|
|
285
|
+
"summary": {
|
|
286
|
+
"contract": "kungfu-buildchain-artifact-summary",
|
|
287
|
+
"artifactName": "libnode-linux-x64-<sha>",
|
|
288
|
+
"fileCount": 1,
|
|
289
|
+
"totalBytes": 1234,
|
|
290
|
+
"digest": "<hex>"
|
|
291
|
+
},
|
|
292
|
+
"expectedArtifacts": {
|
|
293
|
+
"ok": true,
|
|
294
|
+
"source": "expected-artifacts-json",
|
|
295
|
+
"checks": []
|
|
296
|
+
},
|
|
297
|
+
"files": [
|
|
298
|
+
{
|
|
299
|
+
"path": "dist/example.zip",
|
|
300
|
+
"size": 1234,
|
|
301
|
+
"sha256": "<hex>"
|
|
302
|
+
}
|
|
303
|
+
]
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Artifact names do not include actor names, timestamps, or retry counters. Reruns
|
|
308
|
+
produce a new GitHub Actions run but keep the same source SHA/platform contract.
|
|
309
|
+
|
|
310
|
+
`expected-artifacts-json` fails the build before upload when the artifact does
|
|
311
|
+
not match the caller's declared contract. Supported checks are:
|
|
312
|
+
|
|
313
|
+
| Field | Meaning |
|
|
314
|
+
| --- | --- |
|
|
315
|
+
| `minFiles` | Minimum number of manifest files |
|
|
316
|
+
| `maxFiles` | Maximum number of manifest files |
|
|
317
|
+
| `minTotalBytes` | Minimum total byte count |
|
|
318
|
+
| `requiredPaths` | Exact manifest paths that must exist |
|
|
319
|
+
|
|
320
|
+
## Trusted Event Gate
|
|
321
|
+
|
|
322
|
+
The workflow has an explicit `trust-gate` job. By default, pull requests from
|
|
323
|
+
forks fail before any build job can reach self-hosted runners, secrets,
|
|
324
|
+
publishing credentials, or heavyweight build commands. Same-repository PRs,
|
|
325
|
+
workflow dispatches, and protected branch events can proceed.
|
|
326
|
+
|
|
327
|
+
If a repository wants fork PRs to skip rather than fail, it can set:
|
|
328
|
+
|
|
329
|
+
```yaml
|
|
330
|
+
with:
|
|
331
|
+
untrusted-policy: skip
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Do not set `require-trusted-event: false` for workflows that use self-hosted
|
|
335
|
+
runners or secrets.
|
|
336
|
+
|
|
337
|
+
`require-trusted-event` controls access to build runners. It does not override
|
|
338
|
+
the publish gate: pull requests remain non-publishing events.
|
|
339
|
+
|
|
340
|
+
## Fixture
|
|
341
|
+
|
|
342
|
+
`fixtures/libnode-shaped` is the contract fixture. It has:
|
|
343
|
+
|
|
344
|
+
- `package.json` version state;
|
|
345
|
+
- `buildchain.toml` with `install`, `build`, and `verify`;
|
|
346
|
+
- cross-platform Node scripts that create small `dist/` outputs;
|
|
347
|
+
- `Build Surface Fixture` workflow coverage.
|
|
348
|
+
|
|
349
|
+
The fixture proves the reusable surface without running the real libnode native
|
|
350
|
+
build.
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Web-Surface Deployment Contract
|
|
2
|
+
|
|
3
|
+
Buildchain supports `project.type = "web-surface"` for repositories that publish
|
|
4
|
+
sites, docs, product pages, operator consoles, or browser apps. These projects
|
|
5
|
+
need auditable deployment semantics, but they are not package release lines and
|
|
6
|
+
should not be forced into `dev/alpha/release` version-state automation.
|
|
7
|
+
|
|
8
|
+
The release object for a web surface is:
|
|
9
|
+
|
|
10
|
+
```text
|
|
11
|
+
source commit + build artifact + deploy target + channel + deployment manifest
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
This keeps the evidence chain clear:
|
|
15
|
+
|
|
16
|
+
- the source SHA explains what code was built;
|
|
17
|
+
- the artifact hash explains exactly what was deployed;
|
|
18
|
+
- the channel explains who can see it and whether it is promotable;
|
|
19
|
+
- the deploy target and adapter explain where it would be published;
|
|
20
|
+
- the deployment manifest records retention, rollback, security, and secret
|
|
21
|
+
reference metadata.
|
|
22
|
+
|
|
23
|
+
## Configuration
|
|
24
|
+
|
|
25
|
+
`buildchain.toml` is the source of truth. Web-surface projects must declare
|
|
26
|
+
preview, staging, and production channels plus a deploy adapter for each.
|
|
27
|
+
|
|
28
|
+
```toml
|
|
29
|
+
schema = 1
|
|
30
|
+
|
|
31
|
+
[project]
|
|
32
|
+
type = "web-surface"
|
|
33
|
+
name = "site-kungfu-tech"
|
|
34
|
+
site = "kungfu-tech"
|
|
35
|
+
|
|
36
|
+
[channels.preview]
|
|
37
|
+
url_pattern = "https://{alias}.preview.kungfu.tech"
|
|
38
|
+
visibility = "ephemeral"
|
|
39
|
+
requires_auth = false
|
|
40
|
+
noindex = true
|
|
41
|
+
|
|
42
|
+
[channels.staging]
|
|
43
|
+
url = "https://staging.kungfu.tech"
|
|
44
|
+
visibility = "protected"
|
|
45
|
+
requires_auth = true
|
|
46
|
+
noindex = true
|
|
47
|
+
promotable = true
|
|
48
|
+
|
|
49
|
+
[channels.production]
|
|
50
|
+
url = "https://kungfu.tech"
|
|
51
|
+
visibility = "public"
|
|
52
|
+
canonical = true
|
|
53
|
+
noindex = false
|
|
54
|
+
|
|
55
|
+
[deploy.preview]
|
|
56
|
+
adapter = "aws-s3-cloudfront"
|
|
57
|
+
bucket = "kungfu-tech-preview"
|
|
58
|
+
cloudfront_distribution = "E-PREVIEW"
|
|
59
|
+
artifact_path = "dist"
|
|
60
|
+
secret_refs = ["AWS_ROLE_ARN"]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Buildchain validates these hard constraints:
|
|
64
|
+
|
|
65
|
+
- `channels.preview.url_pattern` is required and must contain the alias shape
|
|
66
|
+
used by preview deployments.
|
|
67
|
+
- `channels.staging.requires_auth = true` is required.
|
|
68
|
+
- `channels.staging.noindex = true` is required.
|
|
69
|
+
- `channels.production.url` is required.
|
|
70
|
+
- deploy adapters must be declared per channel.
|
|
71
|
+
- secret material must be declared as reference names, such as
|
|
72
|
+
`secret_refs = ["AWS_ROLE_ARN"]`; inline secret-like deploy keys are rejected.
|
|
73
|
+
|
|
74
|
+
Supported adapter names are:
|
|
75
|
+
|
|
76
|
+
| Adapter | Initial use |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `aws-s3-cloudfront` | Static site artifact sync plus CDN invalidation plan |
|
|
79
|
+
| `aws-elastic-beanstalk` | Future dynamic app environment adapter |
|
|
80
|
+
| `aws-ecs-service` | Future dynamic service adapter |
|
|
81
|
+
|
|
82
|
+
The channel ontology is independent of the adapter. A future dynamic staging
|
|
83
|
+
environment still remains `channel = "staging"` with protected/noindex/security
|
|
84
|
+
requirements.
|
|
85
|
+
|
|
86
|
+
## Preview Aliases
|
|
87
|
+
|
|
88
|
+
Preview uses subdomains, not path prefixes:
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
https://pr-123.preview.kungfu.tech
|
|
92
|
+
https://sha-abcdef123456.preview.kungfu.tech
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Alias semantics are explicit:
|
|
96
|
+
|
|
97
|
+
| Alias | Meaning | Mutable | Retention |
|
|
98
|
+
| --- | --- | --- | --- |
|
|
99
|
+
| `pr-123` | Current preview for a pull request | yes | short-lived |
|
|
100
|
+
| `sha-abcdef123456` | Immutable preview for one source SHA | no | longer-lived |
|
|
101
|
+
|
|
102
|
+
This allows PR comments to stay stable while preserving immutable evidence for a
|
|
103
|
+
specific source commit.
|
|
104
|
+
|
|
105
|
+
## Deployment Manifest
|
|
106
|
+
|
|
107
|
+
Buildchain emits a manifest with the deployment facts that matter for audit and
|
|
108
|
+
rollback:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"schemaVersion": 1,
|
|
113
|
+
"contract": "kungfu-buildchain-web-surface-deployment",
|
|
114
|
+
"site": "kungfu-tech",
|
|
115
|
+
"channel": "preview",
|
|
116
|
+
"alias": "sha-abcdef123456",
|
|
117
|
+
"url": "https://sha-abcdef123456.preview.kungfu.tech",
|
|
118
|
+
"sourceSha": "...",
|
|
119
|
+
"artifactHash": "...",
|
|
120
|
+
"deployTarget": "kungfu-tech-preview",
|
|
121
|
+
"adapter": "aws-s3-cloudfront",
|
|
122
|
+
"deployedAt": "2026-07-01T00:00:00.000Z",
|
|
123
|
+
"retentionClass": "preview-sha-immutable",
|
|
124
|
+
"expiresAt": "2026-09-29T00:00:00.000Z",
|
|
125
|
+
"secretRefs": ["AWS_ROLE_ARN"]
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Dynamic adapters can also fill `runtimeId`, `configFingerprint`,
|
|
130
|
+
`healthCheck`, `migrationState`, `rollbackPointer`, and
|
|
131
|
+
`rollbackLimitations`. Buildchain records secret reference names only, never
|
|
132
|
+
secret values.
|
|
133
|
+
|
|
134
|
+
## Dry-Run Deploy Plans
|
|
135
|
+
|
|
136
|
+
The first implementation is deliberately dry-run only. It plans the adapter
|
|
137
|
+
steps and writes manifest JSON, but it does not touch AWS, DNS, CloudFront, or
|
|
138
|
+
deployment credentials.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
node scripts/web-surface.mjs \
|
|
142
|
+
--mode deploy-plan \
|
|
143
|
+
--cwd fixtures/web-surface-shaped \
|
|
144
|
+
--source-sha aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
|
|
145
|
+
--alias sha-aaaaaaaaaaaa
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
For manifest-only output:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
node scripts/web-surface.mjs \
|
|
152
|
+
--mode manifest \
|
|
153
|
+
--cwd fixtures/web-surface-shaped \
|
|
154
|
+
--source-sha aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
|
|
155
|
+
--alias pr-123 \
|
|
156
|
+
--output .buildchain/web-surface-manifest.json
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The CLI emits GitHub outputs when `GITHUB_OUTPUT` is present:
|
|
160
|
+
|
|
161
|
+
- `web-surface-channel`
|
|
162
|
+
- `web-surface-alias`
|
|
163
|
+
- `web-surface-url`
|
|
164
|
+
- `web-surface-artifact-hash`
|
|
165
|
+
- `web-surface-manifest-json`
|
|
166
|
+
|
|
167
|
+
## Cleanup Plans
|
|
168
|
+
|
|
169
|
+
Preview cleanup is also a dry-run plan:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
node scripts/web-surface.mjs \
|
|
173
|
+
--mode cleanup-plan \
|
|
174
|
+
--cwd fixtures/web-surface-shaped \
|
|
175
|
+
--aliases pr-123,sha-abcdef123456
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The plan keeps mutable PR aliases and immutable SHA aliases distinct so a caller
|
|
179
|
+
can expire them with different retention windows.
|
|
180
|
+
|
|
181
|
+
## Site Repository Shape
|
|
182
|
+
|
|
183
|
+
A site repository can start with:
|
|
184
|
+
|
|
185
|
+
```toml
|
|
186
|
+
schema = 1
|
|
187
|
+
|
|
188
|
+
[project]
|
|
189
|
+
type = "web-surface"
|
|
190
|
+
name = "site-kungfu-tech"
|
|
191
|
+
site = "kungfu-tech"
|
|
192
|
+
|
|
193
|
+
[lifecycle.build]
|
|
194
|
+
command = "pnpm run build"
|
|
195
|
+
|
|
196
|
+
[lifecycle.verify]
|
|
197
|
+
command = "pnpm run check"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Then add the channel, deploy, retention, and security declarations shown above.
|
|
201
|
+
The project may use pnpm, npm, yarn, Vite, Astro, Next static export, Sphinx,
|
|
202
|
+
MkDocs, CMake-generated docs, or another lifecycle command source. Buildchain
|
|
203
|
+
only needs a deterministic artifact path and the manifest facts.
|
|
204
|
+
|
|
205
|
+
## Boundaries
|
|
206
|
+
|
|
207
|
+
Buildchain does not currently perform live AWS mutations for web surfaces.
|
|
208
|
+
Production deploy, DNS changes, staging auth implementation, CloudFront
|
|
209
|
+
distribution creation, and credential provisioning remain explicitly authorized
|
|
210
|
+
operations outside the dry-run contract.
|
|
211
|
+
|
package/package.json
CHANGED
|
@@ -1,4 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.13-alpha.10",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Kungfu Buildchain reusable workflows, release governance, and CLI tooling.",
|
|
6
|
+
"repository": "https://github.com/kungfu-systems/buildchain",
|
|
7
|
+
"author": "Keren Dong",
|
|
8
|
+
"license": "Apache-2.0",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"bin": {
|
|
11
|
+
"buildchain": "./bin/buildchain.mjs"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": "./packages/core/index.js",
|
|
15
|
+
"./core": "./packages/core/index.js",
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"bin/",
|
|
20
|
+
"scripts/*.mjs",
|
|
21
|
+
"packages/core/",
|
|
22
|
+
"README.md",
|
|
23
|
+
"docs/cli.md",
|
|
24
|
+
"docs/lifecycle-protocol.md",
|
|
25
|
+
"docs/publish-transaction.md",
|
|
26
|
+
"docs/reusable-build-surface.md",
|
|
27
|
+
"docs/web-surface-deployments.md"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22.14.0"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public",
|
|
34
|
+
"registry": "https://registry.npmjs.org/"
|
|
35
|
+
},
|
|
36
|
+
"packageManager": "pnpm@11.7.0",
|
|
37
|
+
"scripts": {
|
|
38
|
+
"check": "node scripts/check-inventory.mjs && pnpm run check:workflows && pnpm run test:unit && pnpm -r --filter \"./actions/**\" build",
|
|
39
|
+
"check:workflows": "bash scripts/check-workflows.sh",
|
|
40
|
+
"test:unit": "node --test tests/*.test.mjs",
|
|
41
|
+
"build": "pnpm -r --filter \"./actions/**\" build",
|
|
42
|
+
"package": "npm pack --dry-run --json --registry=https://registry.npmjs.org/",
|
|
43
|
+
"pack:check": "npm pack --dry-run --json --registry=https://registry.npmjs.org/"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^24.0.0",
|
|
47
|
+
"prettier": "^3.9.0",
|
|
48
|
+
"tsup": "^8.5.0",
|
|
49
|
+
"typescript": "^5.9.0",
|
|
50
|
+
"vitest": "^4.1.9"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"smol-toml": "1.7.0"
|
|
54
|
+
}
|
|
4
55
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Buildchain Core Package
|
|
2
|
+
|
|
3
|
+
Shared code lives here when workflow and action migration shows repeated logic
|
|
4
|
+
that is worth centralizing.
|
|
5
|
+
|
|
6
|
+
Current shared surfaces:
|
|
7
|
+
|
|
8
|
+
- `buildchain.toml` loading and normalization;
|
|
9
|
+
- version-state file discovery and update helpers;
|
|
10
|
+
- lifecycle stage normalization and execution;
|
|
11
|
+
- config validation for release-package and `web-surface` projects.
|
|
12
|
+
|
|
13
|
+
Web-surface validation stays in core because both local scripts and GitHub
|
|
14
|
+
Actions need the same fail-closed interpretation of project, channel, deploy,
|
|
15
|
+
retention, and staging security declarations.
|