@kungfu-tech/buildchain 2.1.1-alpha.0 → 2.2.0-alpha.1
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 +8 -0
- package/bin/buildchain.mjs +146 -0
- package/docs/MAP.md +6 -0
- package/docs/cli.md +38 -0
- package/docs/release-passport.md +99 -0
- package/docs/versioning.md +57 -0
- package/package.json +4 -1
- package/packages/core/index.js +21 -0
- package/packages/core/logging.js +84 -0
- package/packages/core/release-passport.js +587 -0
- package/scripts/build-standalone-binary.mjs +377 -0
- package/scripts/check-inventory.mjs +85 -0
package/README.md
CHANGED
|
@@ -51,6 +51,11 @@ future minor refs can each receive many patch releases such as `v2.0.1234`.
|
|
|
51
51
|
`v2` points at the selected stable major line, while `v2.0` points at the
|
|
52
52
|
latest production patch for that minor line.
|
|
53
53
|
|
|
54
|
+
Buildchain opens a new minor line when it adds a new compatible welded surface:
|
|
55
|
+
a reusable workflow contract, public CLI command family, published package
|
|
56
|
+
subpath, evidence protocol, or installable binary distribution shape. The local
|
|
57
|
+
decision log lives in [`docs/versioning.md`](docs/versioning.md).
|
|
58
|
+
|
|
54
59
|
`publish-gate/major` replaces the old ABV `main` channel. It is deliberately
|
|
55
60
|
not named `main` because it is not the active development trunk. Maintainers use
|
|
56
61
|
the same PR flow as other channel promotions: merging
|
|
@@ -111,6 +116,9 @@ Buildchain v2 ships these active surfaces:
|
|
|
111
116
|
- `actions/run-lifecycle` for callers that need the same lifecycle/manifest
|
|
112
117
|
contract inside their own workflows;
|
|
113
118
|
- governance-closed self-promotion through `Buildchain Ref Promotion`.
|
|
119
|
+
- `v2.2` release passport and binary distribution contracts for GitHub Release
|
|
120
|
+
assets, artifact evidence, impact ledgers, agent indexes, and standalone CLI
|
|
121
|
+
archives.
|
|
114
122
|
|
|
115
123
|
Stable consumers should reference actions as:
|
|
116
124
|
|
package/bin/buildchain.mjs
CHANGED
|
@@ -13,13 +13,20 @@ import {
|
|
|
13
13
|
createBuildchainLogger,
|
|
14
14
|
defaultBuildchainLogPath,
|
|
15
15
|
summarizeBuildchainLogEvents,
|
|
16
|
+
verifyBuildchainLogEvents,
|
|
16
17
|
} from "../packages/core/logging.js";
|
|
17
18
|
import {
|
|
18
19
|
explainReleaseLineDryRun,
|
|
19
20
|
formatReleaseLineDryRun,
|
|
20
21
|
} from "../packages/core/release-line-dry-run.js";
|
|
22
|
+
import {
|
|
23
|
+
collectGitHubReleasePassport,
|
|
24
|
+
explainReleasePassport,
|
|
25
|
+
verifyReleasePassport,
|
|
26
|
+
} from "../packages/core/release-passport.js";
|
|
21
27
|
|
|
22
28
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
29
|
+
const embeddedPackageVersion = process.env.BUILDCHAIN_EMBEDDED_PACKAGE_VERSION || "";
|
|
23
30
|
|
|
24
31
|
function usage() {
|
|
25
32
|
return `Usage:
|
|
@@ -42,6 +49,16 @@ function usage() {
|
|
|
42
49
|
[--tags <comma-list>] [--json]
|
|
43
50
|
buildchain release <inspect|recover|finalize|abort> ...
|
|
44
51
|
buildchain transaction inspect ...
|
|
52
|
+
buildchain collect github-release --tag <tag> [--repository <owner/repo>]
|
|
53
|
+
[--assets-dir <dir>] [--assets-json <json-or-path>]
|
|
54
|
+
[--release-json <json-or-path>] [--output-dir <dir>] [--json]
|
|
55
|
+
buildchain verify release-passport <file-or-url> [--json]
|
|
56
|
+
buildchain verify observability-log <jsonl> [--min-events <n>]
|
|
57
|
+
[--require-phase <csv>]
|
|
58
|
+
[--require-component <csv>]
|
|
59
|
+
[--require-event <csv>] [--allow-errors] [--json]
|
|
60
|
+
buildchain explain release --passport <file-or-url> [--for human|agent] [--json]
|
|
61
|
+
buildchain inspect release --passport <file-or-url> [--json]
|
|
45
62
|
buildchain doctor [--cwd <dir>] [--json]
|
|
46
63
|
buildchain log <info|warn|error> --event <name> [--phase <phase>]
|
|
47
64
|
[--component <name>] [--source <name>] [--attribute key=value]...
|
|
@@ -62,6 +79,9 @@ Examples:
|
|
|
62
79
|
buildchain npm dry-run --json
|
|
63
80
|
buildchain release --dry-run --target-ref alpha/v2/v2.0
|
|
64
81
|
buildchain span --event native.build -- cmake --build build
|
|
82
|
+
buildchain collect github-release --tag v2.2.0 --assets-dir dist --output-dir .buildchain/release-passport
|
|
83
|
+
buildchain verify release-passport .buildchain/release-passport/buildchain.release.json
|
|
84
|
+
buildchain verify observability-log .buildchain/logs/events.jsonl --min-events 4 --require-phase build
|
|
65
85
|
`;
|
|
66
86
|
}
|
|
67
87
|
|
|
@@ -176,6 +196,9 @@ function printJson(value) {
|
|
|
176
196
|
}
|
|
177
197
|
|
|
178
198
|
function packageVersion() {
|
|
199
|
+
if (embeddedPackageVersion) {
|
|
200
|
+
return embeddedPackageVersion;
|
|
201
|
+
}
|
|
179
202
|
const packageJson = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
180
203
|
return packageJson.version;
|
|
181
204
|
}
|
|
@@ -396,6 +419,129 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
396
419
|
return;
|
|
397
420
|
}
|
|
398
421
|
|
|
422
|
+
if (command === "collect") {
|
|
423
|
+
const [subcommand = "", ...collectArgs] = args;
|
|
424
|
+
if (subcommand !== "github-release") {
|
|
425
|
+
throw new Error("usage: buildchain collect github-release --tag <tag>");
|
|
426
|
+
}
|
|
427
|
+
const workflow = {
|
|
428
|
+
name: process.env.GITHUB_WORKFLOW || "",
|
|
429
|
+
runId: process.env.GITHUB_RUN_ID || "",
|
|
430
|
+
runAttempt: process.env.GITHUB_RUN_ATTEMPT || "",
|
|
431
|
+
url: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID
|
|
432
|
+
? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`
|
|
433
|
+
: "",
|
|
434
|
+
runnerKind: process.env.BUILDCHAIN_RUNNER_KIND || "github-hosted",
|
|
435
|
+
runnerOs: process.env.RUNNER_OS || process.platform,
|
|
436
|
+
runnerArch: process.env.RUNNER_ARCH || process.arch,
|
|
437
|
+
runnerImage: process.env.ImageOS || "",
|
|
438
|
+
};
|
|
439
|
+
const result = collectGitHubReleasePassport({
|
|
440
|
+
cwd: readFlag(collectArgs, "cwd", process.cwd()),
|
|
441
|
+
tag: readFlag(collectArgs, "tag", ""),
|
|
442
|
+
repository: readFlag(collectArgs, "repository", process.env.GITHUB_REPOSITORY || ""),
|
|
443
|
+
sourceSha: readFlag(collectArgs, "source-sha", process.env.GITHUB_SHA || ""),
|
|
444
|
+
line: readFlag(collectArgs, "line", ""),
|
|
445
|
+
outputDir: readFlag(collectArgs, "output-dir", ".buildchain/release-passport"),
|
|
446
|
+
assetsDir: readFlag(collectArgs, "assets-dir", ""),
|
|
447
|
+
assetsJson: readFlag(collectArgs, "assets-json", ""),
|
|
448
|
+
releaseJson: readFlag(collectArgs, "release-json", ""),
|
|
449
|
+
packageName: readFlag(collectArgs, "package-name", "@kungfu-tech/buildchain"),
|
|
450
|
+
packageVersion: readFlag(collectArgs, "package-version", packageVersion()),
|
|
451
|
+
workflow,
|
|
452
|
+
});
|
|
453
|
+
if (readBooleanFlag(collectArgs, "json")) {
|
|
454
|
+
printJson(result);
|
|
455
|
+
} else {
|
|
456
|
+
process.stdout.write(`release passport collected: ${path.relative(process.cwd(), result.outputDir)}\n`);
|
|
457
|
+
process.stdout.write(`artifacts: ${result.artifactEvidence.artifacts.length}\n`);
|
|
458
|
+
}
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (command === "verify") {
|
|
463
|
+
const [subcommand = "", location = "", ...verifyArgs] = args;
|
|
464
|
+
if (subcommand === "observability-log") {
|
|
465
|
+
if (!location) {
|
|
466
|
+
throw new Error("usage: buildchain verify observability-log <jsonl>");
|
|
467
|
+
}
|
|
468
|
+
const report = verifyBuildchainLogEvents({
|
|
469
|
+
path: location,
|
|
470
|
+
minEvents: Number(readFlag(verifyArgs, "min-events", "1")),
|
|
471
|
+
allowErrors: readBooleanFlag(verifyArgs, "allow-errors"),
|
|
472
|
+
requirePhases: readRepeatedFlag(verifyArgs, "require-phase"),
|
|
473
|
+
requireComponents: readRepeatedFlag(verifyArgs, "require-component"),
|
|
474
|
+
requireEvents: readRepeatedFlag(verifyArgs, "require-event"),
|
|
475
|
+
});
|
|
476
|
+
if (readBooleanFlag(verifyArgs, "json")) {
|
|
477
|
+
printJson(report);
|
|
478
|
+
} else {
|
|
479
|
+
process.stdout.write(`observability log: ${report.ok ? "ok" : "failed"}\n`);
|
|
480
|
+
process.stdout.write(`events: ${report.summary.eventCount}\n`);
|
|
481
|
+
for (const entry of report.issues) {
|
|
482
|
+
process.stdout.write(`- ${entry.level}: ${entry.code}: ${entry.message}\n`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
process.exitCode = report.ok ? 0 : 1;
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
if (subcommand !== "release-passport" || !location) {
|
|
489
|
+
throw new Error("usage: buildchain verify release-passport <file-or-url>");
|
|
490
|
+
}
|
|
491
|
+
const report = await verifyReleasePassport({ passportLocation: location });
|
|
492
|
+
if (readBooleanFlag(verifyArgs, "json")) {
|
|
493
|
+
printJson(report);
|
|
494
|
+
} else {
|
|
495
|
+
process.stdout.write(`release passport: ${report.ok ? "ok" : "failed"}\n`);
|
|
496
|
+
process.stdout.write(`artifacts: ${report.completeness.artifactCount}\n`);
|
|
497
|
+
for (const entry of report.issues) {
|
|
498
|
+
process.stdout.write(`- ${entry.level}: ${entry.code}: ${entry.message}\n`);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
process.exitCode = report.ok ? 0 : 1;
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (command === "explain") {
|
|
506
|
+
const [subcommand = "", ...explainArgs] = args;
|
|
507
|
+
if (subcommand !== "release") {
|
|
508
|
+
throw new Error("usage: buildchain explain release --passport <file-or-url>");
|
|
509
|
+
}
|
|
510
|
+
const passport = readFlag(explainArgs, "passport", "");
|
|
511
|
+
if (!passport) {
|
|
512
|
+
throw new Error("buildchain explain release requires --passport <file-or-url>");
|
|
513
|
+
}
|
|
514
|
+
const explanation = await explainReleasePassport({
|
|
515
|
+
passportLocation: passport,
|
|
516
|
+
forAudience: readFlag(explainArgs, "for", "human"),
|
|
517
|
+
});
|
|
518
|
+
if (readBooleanFlag(explainArgs, "json")) {
|
|
519
|
+
printJson(explanation);
|
|
520
|
+
} else {
|
|
521
|
+
process.stdout.write(`release: ${explanation.release?.tag || "unknown"}\n`);
|
|
522
|
+
process.stdout.write(`trust: ${explanation.trust}\n`);
|
|
523
|
+
process.stdout.write(`next action: ${explanation.nextAction}\n`);
|
|
524
|
+
}
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (command === "inspect") {
|
|
529
|
+
const [subcommand = "", ...inspectArgs] = args;
|
|
530
|
+
if (subcommand !== "release") {
|
|
531
|
+
throw new Error("usage: buildchain inspect release --passport <file-or-url>");
|
|
532
|
+
}
|
|
533
|
+
const passport = readFlag(inspectArgs, "passport", "");
|
|
534
|
+
if (!passport) {
|
|
535
|
+
throw new Error("buildchain inspect release requires --passport <file-or-url>");
|
|
536
|
+
}
|
|
537
|
+
const explanation = await explainReleasePassport({
|
|
538
|
+
passportLocation: passport,
|
|
539
|
+
forAudience: readFlag(inspectArgs, "for", "human"),
|
|
540
|
+
});
|
|
541
|
+
printJson(explanation);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
|
|
399
545
|
if (command === "web-surface") {
|
|
400
546
|
runScript("web-surface.mjs", args);
|
|
401
547
|
return;
|
package/docs/MAP.md
CHANGED
|
@@ -21,11 +21,13 @@ running artifact), *use* (consume / extend) - and a **status**:
|
|
|
21
21
|
| How do I install or run the `buildchain` CLI? | [`cli.md`](cli.md) | use | stable |
|
|
22
22
|
| How do I initialize a new repository? | [`cli.md`](cli.md) + [`lifecycle-protocol.md`](lifecycle-protocol.md) | use | stable |
|
|
23
23
|
| Why does Buildchain use branch-driven release governance? | [`release-governance.md`](release-governance.md) | why | stable |
|
|
24
|
+
| How does Buildchain decide patch, minor, and major release lines? | [`versioning.md`](versioning.md) | why | stable |
|
|
24
25
|
| What exact branch/tag state machine runs on alpha, release, and major gate? | [`release-flow.md`](release-flow.md) | verify | stable |
|
|
25
26
|
| What did Buildchain migrate or retire from old action repositories? | [`migration-inventory.md`](migration-inventory.md) | verify | stable |
|
|
26
27
|
| What is the active action and workflow source of truth? | [`ownership.md`](ownership.md) | verify | stable |
|
|
27
28
|
| How do I declare version files and custom lifecycle commands? | [`lifecycle-protocol.md`](lifecycle-protocol.md) | use | stable |
|
|
28
29
|
| How does publish evidence, recovery, and finalization work? | [`publish-transaction.md`](publish-transaction.md) | verify | stable |
|
|
30
|
+
| How do I publish or verify binary release passport artifacts? | [`release-passport.md`](release-passport.md) | use | stable |
|
|
29
31
|
| How do I call the reusable build workflow? | [`reusable-build-surface.md`](reusable-build-surface.md) | use | stable |
|
|
30
32
|
| How do I deploy a site/app preview, staging, or production surface? | [`web-surface-deployments.md`](web-surface-deployments.md) | use | stable |
|
|
31
33
|
| How do I use the active actions directly? | [`../actions/validate-config/README.md`](../actions/validate-config/README.md), [`../actions/run-lifecycle/README.md`](../actions/run-lifecycle/README.md), [`../actions/promote-buildchain-ref/README.md`](../actions/promote-buildchain-ref/README.md) | use | stable |
|
|
@@ -40,6 +42,8 @@ running artifact), *use* (consume / extend) - and a **status**:
|
|
|
40
42
|
- **v2 / v2.0 / v2.0-alpha / exact tags / floating tags** ->
|
|
41
43
|
[`release-governance.md`](release-governance.md) and
|
|
42
44
|
[`release-flow.md`](release-flow.md).
|
|
45
|
+
- **v2.1 vs v2.2 / when to open a new minor line** ->
|
|
46
|
+
[`versioning.md`](versioning.md).
|
|
43
47
|
- **dry-run / what would happen if this channel PR merges** -> [`cli.md`](cli.md)
|
|
44
48
|
and [`release-flow.md`](release-flow.md).
|
|
45
49
|
- **pnpm / npm / yarn / package-manager adapters** ->
|
|
@@ -51,6 +55,8 @@ running artifact), *use* (consume / extend) - and a **status**:
|
|
|
51
55
|
[`../fixtures/libnode-shaped/README.md`](../fixtures/libnode-shaped/README.md).
|
|
52
56
|
- **Trusted Publishing / npm / publish evidence / recovery** ->
|
|
53
57
|
[`cli.md`](cli.md) and [`publish-transaction.md`](publish-transaction.md).
|
|
58
|
+
- **GitHub Release passport / binary assets / artifact evidence / agent release checks** ->
|
|
59
|
+
[`release-passport.md`](release-passport.md) and [`cli.md`](cli.md).
|
|
54
60
|
- **sites / web previews / staging / production gates** ->
|
|
55
61
|
[`web-surface-deployments.md`](web-surface-deployments.md).
|
|
56
62
|
|
package/docs/cli.md
CHANGED
|
@@ -75,12 +75,16 @@ buildchain mark --event configure.ready --phase configure --attribute target=rel
|
|
|
75
75
|
buildchain span --event native.build --phase build -- cmake --build build
|
|
76
76
|
buildchain log warn --event cache.miss --component conan --attribute token=hidden
|
|
77
77
|
buildchain log summary --json
|
|
78
|
+
buildchain verify observability-log .buildchain/logs/events.jsonl --min-events 4 --require-phase build
|
|
78
79
|
```
|
|
79
80
|
|
|
80
81
|
During `buildchain lifecycle run`, child processes receive
|
|
81
82
|
`BUILDCHAIN_LOG_PATH` and `BUILDCHAIN_LOG_RUN_ID`. A shell, Python, CMake, Conan,
|
|
82
83
|
or JavaScript helper can call `buildchain mark` or `buildchain span` mid-build
|
|
83
84
|
and have those events grouped into the same lifecycle summary.
|
|
85
|
+
`buildchain verify observability-log` is a release gate: it fails when the log
|
|
86
|
+
is missing, has too few events, contains error events, or does not include
|
|
87
|
+
required phases, components, or event names.
|
|
84
88
|
|
|
85
89
|
The event protocol is JSONL and is also available from the SDK:
|
|
86
90
|
|
|
@@ -111,6 +115,40 @@ and `buildchain build-contract` route to the same scripts used by Buildchain's
|
|
|
111
115
|
GitHub Actions workflows. This keeps local inspection and CI behavior on the
|
|
112
116
|
same implementation path.
|
|
113
117
|
|
|
118
|
+
`buildchain collect github-release` creates a release passport bundle from
|
|
119
|
+
GitHub Release assets or a local asset directory:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
buildchain collect github-release \
|
|
123
|
+
--tag v2.2.0 \
|
|
124
|
+
--repository kungfu-systems/buildchain \
|
|
125
|
+
--assets-dir dist \
|
|
126
|
+
--output-dir .buildchain/release-passport
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The bundle includes `buildchain.release.json`, `artifact-evidence.json`,
|
|
130
|
+
`impact.json`, `agent-index.json`, `product-mechanism.json`, `check-report.json`,
|
|
131
|
+
and `llms.txt`. Production binary distribution defaults to GitHub-hosted
|
|
132
|
+
runners so other projects can reproduce the release lane; self-hosted runners
|
|
133
|
+
remain compatibility fixtures and are recorded as runner facts when used.
|
|
134
|
+
|
|
135
|
+
Buildchain dogfoods its observability toolkit in this lane. The standalone
|
|
136
|
+
builder writes API-generated events, while the workflow uses `buildchain mark`,
|
|
137
|
+
`buildchain span`, `buildchain verify observability-log`, and `buildchain log
|
|
138
|
+
summary`; the event logs and summaries are published as release passport assets.
|
|
139
|
+
|
|
140
|
+
Verify and explain release passports:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
buildchain verify release-passport .buildchain/release-passport/buildchain.release.json
|
|
144
|
+
buildchain explain release --passport .buildchain/release-passport/buildchain.release.json --for agent --json
|
|
145
|
+
buildchain inspect release --passport .buildchain/release-passport/buildchain.release.json
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The verifier fails closed when required protocol files are absent, artifacts are
|
|
149
|
+
not covered by evidence, or digests disagree. The explanation output is shaped
|
|
150
|
+
for agents: trust, completeness, impact, recovery route, and next action.
|
|
151
|
+
|
|
114
152
|
`buildchain release --dry-run` explains the release-line state machine before a
|
|
115
153
|
maintainer opens or merges a channel PR:
|
|
116
154
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Release Passport and Binary Distribution
|
|
2
|
+
|
|
3
|
+
Buildchain v2.2 adds a GitHub-native release passport protocol for binary and
|
|
4
|
+
multi-artifact products. The protocol is meant for projects that want release
|
|
5
|
+
evidence without migrating their whole CI/CD system to Buildchain.
|
|
6
|
+
|
|
7
|
+
## Contract
|
|
8
|
+
|
|
9
|
+
The release passport surface is a welded contract. Additive fields are allowed;
|
|
10
|
+
breaking semantic changes require a new major line.
|
|
11
|
+
|
|
12
|
+
P0 protocol artifacts:
|
|
13
|
+
|
|
14
|
+
- `product-mechanism.json`
|
|
15
|
+
- `buildchain.release.json`
|
|
16
|
+
- `artifact-evidence.json`
|
|
17
|
+
- `impact.json`
|
|
18
|
+
- `agent-index.json`
|
|
19
|
+
- `llms.txt`
|
|
20
|
+
|
|
21
|
+
`buildchain.release.json` is the first file an agent should read. It points to
|
|
22
|
+
artifact evidence, impact, recovery, product mechanism, and agent index facts.
|
|
23
|
+
Buildchain's own binary lane also publishes observability artifacts generated by
|
|
24
|
+
the Buildchain logging API and CLI:
|
|
25
|
+
|
|
26
|
+
- `buildchain-log-events-<platform>.jsonl`
|
|
27
|
+
- `buildchain-log-summary-<platform>.json`
|
|
28
|
+
- `buildchain-log-events-passport.jsonl`
|
|
29
|
+
- `buildchain-log-summary-passport.json`
|
|
30
|
+
|
|
31
|
+
## Runner Policy
|
|
32
|
+
|
|
33
|
+
Production binary distribution should use GitHub-hosted runners by default:
|
|
34
|
+
|
|
35
|
+
- `ubuntu-24.04`
|
|
36
|
+
- `macos-latest`
|
|
37
|
+
- `windows-2022`
|
|
38
|
+
|
|
39
|
+
This keeps the public release path easy for other projects to reproduce.
|
|
40
|
+
Self-hosted runners remain compatibility fixtures: they prove that the protocol
|
|
41
|
+
does not depend on GitHub-hosted images, but they are not the default public
|
|
42
|
+
distribution lane.
|
|
43
|
+
|
|
44
|
+
The protocol records runner facts in `artifact-evidence.json`; it does not
|
|
45
|
+
require a specific runner class.
|
|
46
|
+
|
|
47
|
+
## CLI
|
|
48
|
+
|
|
49
|
+
Generate a local release passport bundle from release assets:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
buildchain collect github-release \
|
|
53
|
+
--tag v2.2.0 \
|
|
54
|
+
--repository kungfu-systems/buildchain \
|
|
55
|
+
--assets-dir dist \
|
|
56
|
+
--output-dir .buildchain/release-passport
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Verify a release passport:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
buildchain verify release-passport .buildchain/release-passport/buildchain.release.json
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Explain a release to an agent:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
buildchain explain release \
|
|
69
|
+
--passport .buildchain/release-passport/buildchain.release.json \
|
|
70
|
+
--for agent \
|
|
71
|
+
--json
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The verifier fails closed when a passport omits artifacts, omits evidence, has
|
|
75
|
+
digest mismatches, or misses required protocol files.
|
|
76
|
+
|
|
77
|
+
## Binary Distribution
|
|
78
|
+
|
|
79
|
+
Initial binary distribution stays lightweight:
|
|
80
|
+
|
|
81
|
+
- GitHub Release assets.
|
|
82
|
+
- `checksums.txt`.
|
|
83
|
+
- release passport artifacts.
|
|
84
|
+
- install scripts and Homebrew tap fixtures after the passport is reliable.
|
|
85
|
+
|
|
86
|
+
Heavy package manager channels such as apt, yum, winget, choco, Scoop, mise, or
|
|
87
|
+
asdf are out of the P0/P1 scope until there is real external demand.
|
|
88
|
+
|
|
89
|
+
Standalone binaries are a distribution shape, not a second implementation. The
|
|
90
|
+
source of truth remains the Node/ESM CLI and core library.
|
|
91
|
+
|
|
92
|
+
The standalone binary builder imports `@kungfu-tech/buildchain/logging` directly
|
|
93
|
+
and records setup, SEA blob generation, injection, signing, archiving, manifest,
|
|
94
|
+
and evidence phases. The GitHub workflow wraps the same build and passport
|
|
95
|
+
steps with `buildchain mark`, `buildchain span`,
|
|
96
|
+
`buildchain verify observability-log`, and `buildchain log summary`. Logging is
|
|
97
|
+
a hard release gate: missing events, error events, or missing required phases
|
|
98
|
+
fail the job before assets are uploaded. The verified logs are release assets
|
|
99
|
+
and are covered by the release passport digest checks.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Buildchain Versioning
|
|
2
|
+
|
|
3
|
+
Buildchain uses semantic version lines to describe public contracts, not only
|
|
4
|
+
code size. A release can be small in diff size and still open a new minor line
|
|
5
|
+
when it adds a durable surface that consumers, workflows, or agents can depend
|
|
6
|
+
on.
|
|
7
|
+
|
|
8
|
+
## Lines
|
|
9
|
+
|
|
10
|
+
| Line | Meaning |
|
|
11
|
+
| --- | --- |
|
|
12
|
+
| Patch | Compatible fix, hardening, documentation correction, or implementation repair inside an existing surface. |
|
|
13
|
+
| Minor | New compatible welded surface: reusable workflow output, CLI command family, config protocol, published subpath, evidence file, runner contract, or agent-readable artifact. |
|
|
14
|
+
| Major | Breaking semantic change, removed stable surface, changed branch/tag governance, or incompatible protocol rewrite. |
|
|
15
|
+
|
|
16
|
+
Kungfu minor lines are long-lived trains. `v2.0`, `v2.1`, and `v2.2` can each
|
|
17
|
+
receive many patch releases. The major ref, such as `v2`, points at the
|
|
18
|
+
selected stable major entrypoint; the minor ref, such as `v2.2`, points at the
|
|
19
|
+
latest stable production patch for that minor line.
|
|
20
|
+
|
|
21
|
+
## Welded Surfaces
|
|
22
|
+
|
|
23
|
+
These surfaces require at least a minor bump when first introduced:
|
|
24
|
+
|
|
25
|
+
- reusable workflow inputs, outputs, and artifact contracts;
|
|
26
|
+
- public CLI command families and their machine-readable JSON shapes;
|
|
27
|
+
- public npm exports such as `@kungfu-tech/buildchain/logging`;
|
|
28
|
+
- config protocols such as `buildchain.toml`;
|
|
29
|
+
- release governance state machines and protected ref semantics;
|
|
30
|
+
- release evidence contracts such as passport, artifact evidence, impact
|
|
31
|
+
ledger, and agent index files;
|
|
32
|
+
- binary distribution shapes that users can install or automate against.
|
|
33
|
+
|
|
34
|
+
Additive fields inside an existing welded surface can be patch releases when
|
|
35
|
+
old consumers continue to work and validation remains stricter, not looser.
|
|
36
|
+
Removing fields, changing meanings, weakening trust gates, or changing the
|
|
37
|
+
expected ref flow requires a major bump.
|
|
38
|
+
|
|
39
|
+
## Decision Log
|
|
40
|
+
|
|
41
|
+
| Date | Decision | Line | Reason |
|
|
42
|
+
| --- | --- | --- | --- |
|
|
43
|
+
| 2026-07-02 | Buildchain toolkit observability is a minor surface. | `v2.1` | It adds the public logging SDK, CLI observability commands, and package subpaths that consumers can import. |
|
|
44
|
+
| 2026-07-02 | Release passport and binary distribution are a minor surface. | `v2.2` | They add agent-readable release passport files, artifact evidence, impact ledger, agent index, GitHub Release collection and verification commands, and standalone binary assets. |
|
|
45
|
+
|
|
46
|
+
## Runner Policy
|
|
47
|
+
|
|
48
|
+
The `v2.2` binary distribution lane uses GitHub-hosted runners for production
|
|
49
|
+
assets because that is the easiest release path for external users to reproduce:
|
|
50
|
+
|
|
51
|
+
- `ubuntu-24.04`
|
|
52
|
+
- `macos-latest`
|
|
53
|
+
- `windows-2022`
|
|
54
|
+
|
|
55
|
+
Self-hosted runners remain compatibility fixtures. They prove Buildchain's
|
|
56
|
+
protocol does not depend on GitHub-hosted images, but they do not define the
|
|
57
|
+
public binary distribution path.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0-alpha.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Kungfu Buildchain reusable workflows, release governance, and CLI tooling.",
|
|
6
6
|
"repository": "https://github.com/kungfu-systems/buildchain",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
".": "./packages/core/index.js",
|
|
15
15
|
"./core": "./packages/core/index.js",
|
|
16
16
|
"./logging": "./packages/core/logging.js",
|
|
17
|
+
"./release-passport": "./packages/core/release-passport.js",
|
|
17
18
|
"./package.json": "./package.json"
|
|
18
19
|
},
|
|
19
20
|
"files": [
|
|
@@ -31,9 +32,11 @@
|
|
|
31
32
|
"docs/migration-inventory.md",
|
|
32
33
|
"docs/ownership.md",
|
|
33
34
|
"docs/publish-transaction.md",
|
|
35
|
+
"docs/release-passport.md",
|
|
34
36
|
"docs/release-flow.md",
|
|
35
37
|
"docs/release-governance.md",
|
|
36
38
|
"docs/reusable-build-surface.md",
|
|
39
|
+
"docs/versioning.md",
|
|
37
40
|
"docs/web-surface-deployments.md",
|
|
38
41
|
"actions/*/README.md",
|
|
39
42
|
"fixtures/*/README.md"
|
package/packages/core/index.js
CHANGED
|
@@ -50,4 +50,25 @@ export {
|
|
|
50
50
|
readBuildchainLogEvents,
|
|
51
51
|
redactBuildchainLogAttributes,
|
|
52
52
|
summarizeBuildchainLogEvents,
|
|
53
|
+
verifyBuildchainLogEvents,
|
|
53
54
|
} from "./logging.js";
|
|
55
|
+
|
|
56
|
+
export {
|
|
57
|
+
AGENT_INDEX_CONTRACT,
|
|
58
|
+
ARTIFACT_EVIDENCE_CONTRACT,
|
|
59
|
+
IMPACT_LEDGER_CONTRACT,
|
|
60
|
+
PRODUCT_MECHANISM_CONTRACT,
|
|
61
|
+
RELEASE_CHECK_REPORT_CONTRACT,
|
|
62
|
+
RELEASE_PASSPORT_CONTRACT,
|
|
63
|
+
collectGitHubReleasePassport,
|
|
64
|
+
createArtifactEvidence,
|
|
65
|
+
createReleaseCheckReport,
|
|
66
|
+
createReleasePassport,
|
|
67
|
+
explainReleasePassport,
|
|
68
|
+
makeReleasePassportFixtureAssets,
|
|
69
|
+
readJsonFromLocation,
|
|
70
|
+
sha256File,
|
|
71
|
+
sha256Text,
|
|
72
|
+
validateKnownReleasePassportContracts,
|
|
73
|
+
verifyReleasePassport,
|
|
74
|
+
} from "./release-passport.js";
|
package/packages/core/logging.js
CHANGED
|
@@ -111,6 +111,90 @@ export function summarizeBuildchainLogEvents(input = {}) {
|
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
function listOption(value) {
|
|
115
|
+
if (Array.isArray(value)) {
|
|
116
|
+
return value.flatMap((entry) => listOption(entry));
|
|
117
|
+
}
|
|
118
|
+
if (typeof value !== "string") {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
return value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function issue(level, code, message) {
|
|
125
|
+
return { level, code, message };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function verifyBuildchainLogEvents({
|
|
129
|
+
path: filePath = "",
|
|
130
|
+
events: inputEvents = undefined,
|
|
131
|
+
minEvents = 1,
|
|
132
|
+
allowErrors = false,
|
|
133
|
+
requirePhases = [],
|
|
134
|
+
requireComponents = [],
|
|
135
|
+
requireEvents = [],
|
|
136
|
+
} = {}) {
|
|
137
|
+
const issues = [];
|
|
138
|
+
let events = [];
|
|
139
|
+
try {
|
|
140
|
+
events = Array.isArray(inputEvents) ? inputEvents : readBuildchainLogEvents(filePath);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
issues.push(issue("error", "log.read.failed", error.message));
|
|
143
|
+
}
|
|
144
|
+
if (!events.length) {
|
|
145
|
+
issues.push(issue("error", "log.events.empty", `no Buildchain log events found at ${filePath || "<memory>"}`));
|
|
146
|
+
}
|
|
147
|
+
for (const [index, event] of events.entries()) {
|
|
148
|
+
if (event?.contract !== BUILDCHAIN_LOG_EVENT_CONTRACT) {
|
|
149
|
+
issues.push(issue("error", "log.contract.invalid", `event ${index} has invalid contract`));
|
|
150
|
+
}
|
|
151
|
+
for (const key of ["timestamp", "level", "source", "component", "event"]) {
|
|
152
|
+
if (!event?.[key]) {
|
|
153
|
+
issues.push(issue("error", "log.field.missing", `event ${index} is missing ${key}`));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const summary = summarizeBuildchainLogEvents(events);
|
|
158
|
+
const requiredEventCount = Number(minEvents || 0);
|
|
159
|
+
if (summary.eventCount < requiredEventCount) {
|
|
160
|
+
issues.push(issue("error", "log.events.too_few", `expected at least ${requiredEventCount} events, found ${summary.eventCount}`));
|
|
161
|
+
}
|
|
162
|
+
if (!allowErrors && summary.errorCount > 0) {
|
|
163
|
+
issues.push(issue("error", "log.errors.present", `expected no error events, found ${summary.errorCount}`));
|
|
164
|
+
}
|
|
165
|
+
for (const phase of listOption(requirePhases)) {
|
|
166
|
+
if (!summary.phases[phase]) {
|
|
167
|
+
issues.push(issue("error", "log.phase.missing", `required phase missing: ${phase}`));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
for (const component of listOption(requireComponents)) {
|
|
171
|
+
if (!summary.components[component]) {
|
|
172
|
+
issues.push(issue("error", "log.component.missing", `required component missing: ${component}`));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const eventNames = new Set(events.map((event) => event.event).filter(Boolean));
|
|
176
|
+
for (const eventName of listOption(requireEvents)) {
|
|
177
|
+
if (!eventNames.has(eventName)) {
|
|
178
|
+
issues.push(issue("error", "log.event.missing", `required event missing: ${eventName}`));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
schemaVersion: 1,
|
|
183
|
+
contract: "kungfu-buildchain-log-verification",
|
|
184
|
+
ok: !issues.some((entry) => entry.level === "error"),
|
|
185
|
+
path: filePath,
|
|
186
|
+
summary,
|
|
187
|
+
requirements: {
|
|
188
|
+
minEvents: requiredEventCount,
|
|
189
|
+
allowErrors,
|
|
190
|
+
phases: listOption(requirePhases),
|
|
191
|
+
components: listOption(requireComponents),
|
|
192
|
+
events: listOption(requireEvents),
|
|
193
|
+
},
|
|
194
|
+
issues,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
114
198
|
export function createBuildchainLogger(options = {}) {
|
|
115
199
|
const cwd = path.resolve(options.cwd || process.cwd());
|
|
116
200
|
const runningInActions = process.env.GITHUB_ACTIONS === "true";
|