@kungfu-tech/buildchain 2.2.1-alpha.0 → 2.2.1-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.
@@ -0,0 +1,70 @@
1
+ # Site Bundle Contract
2
+
3
+ `@kungfu-tech/buildchain` publishes `dist/site/` as the package-owned fact
4
+ source for `buildchain.libkungfu.dev` and other documentation surfaces.
5
+
6
+ The website may design navigation, visual hierarchy, examples, and explanatory
7
+ copy around these facts. It should not hand-write the current Buildchain
8
+ release mechanics, command registry, workflow registry, or artifact schema.
9
+
10
+ ## Files
11
+
12
+ ```text
13
+ dist/site/
14
+ buildchain-site.json
15
+ site-manifest.json
16
+ cli-registry.json
17
+ workflow-registry.json
18
+ release-model.json
19
+ artifact-schemas.json
20
+ product-mechanism.json
21
+ release-provenance.json
22
+ agent-index.json
23
+ ```
24
+
25
+ `buildchain-site.json` is the top-level bundle entrypoint.
26
+
27
+ ## npm Consumption
28
+
29
+ ```bash
30
+ npm install @kungfu-tech/buildchain
31
+ ```
32
+
33
+ Then read files from:
34
+
35
+ ```text
36
+ node_modules/@kungfu-tech/buildchain/dist/site/
37
+ ```
38
+
39
+ Package exports are also provided for direct JSON-aware consumers:
40
+
41
+ ```js
42
+ import siteManifest from "@kungfu-tech/buildchain/site/site-manifest.json" with { type: "json" };
43
+ ```
44
+
45
+ ## Generation
46
+
47
+ ```bash
48
+ pnpm run generate:site
49
+ pnpm run check:site
50
+ ```
51
+
52
+ `check:site` fails when generated files are stale. `pnpm run check` includes
53
+ this gate, so release candidates cannot publish an out-of-date site bundle.
54
+
55
+ ## Scope
56
+
57
+ The P0 bundle includes:
58
+
59
+ - site manifest;
60
+ - CLI command registry;
61
+ - workflow/action registry;
62
+ - release model facts;
63
+ - artifact and evidence schema index;
64
+ - product mechanism manifest;
65
+ - release provenance;
66
+ - agent read order.
67
+
68
+ Future minor lines can add examples, recipes, fixture indexes, and richer
69
+ schema metadata without breaking existing consumers.
70
+
@@ -0,0 +1,70 @@
1
+ # Toolkit Observability
2
+
3
+ Buildchain ships a small logging toolkit for repository workflows and project
4
+ scripts. The goal is to separate time spent in Buildchain's framework from time
5
+ spent in the consumer's own build, test, packaging, and publish steps.
6
+
7
+ ## CLI Logging
8
+
9
+ ```bash
10
+ buildchain mark \
11
+ --event native.configure \
12
+ --phase configure \
13
+ --component cmake \
14
+ --attribute preset=release
15
+
16
+ buildchain span \
17
+ --event native.build \
18
+ --phase build \
19
+ --component cmake \
20
+ -- cmake --build build --config Release
21
+
22
+ buildchain log summary --json
23
+ buildchain verify observability-log .buildchain/logs/events.jsonl --min-events 4
24
+ ```
25
+
26
+ Every event records a timestamp. `span` records duration and preserves the
27
+ wrapped command's exit code.
28
+
29
+ ## Library API
30
+
31
+ ```js
32
+ import { createBuildchainLogger } from "@kungfu-tech/buildchain/logging";
33
+
34
+ const logger = createBuildchainLogger({
35
+ source: "consumer",
36
+ component: "native-build",
37
+ phase: "compile",
38
+ });
39
+
40
+ const span = logger.span("native.compile", { attributes: { target: "release" } });
41
+ try {
42
+ await compile();
43
+ span.end();
44
+ } catch (error) {
45
+ span.error(error);
46
+ throw error;
47
+ }
48
+ ```
49
+
50
+ Use the API when a build script has internal stages that are invisible to the
51
+ outer workflow. Keep secret values out of attributes; known sensitive keys are
52
+ redacted, but callers should still avoid logging private material.
53
+
54
+ ## Release Gate
55
+
56
+ Buildchain's own binary distribution lane verifies required log events before
57
+ uploading release assets. Consumers can apply the same pattern:
58
+
59
+ ```bash
60
+ buildchain verify observability-log .buildchain/logs/events.jsonl \
61
+ --require-phase build \
62
+ --require-phase package \
63
+ --require-component workflow \
64
+ --require-event native.build.start \
65
+ --require-event native.build.end
66
+ ```
67
+
68
+ This makes missing instrumentation a release failure instead of a dashboard
69
+ afterthought.
70
+
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@kungfu-tech/buildchain",
3
- "version": "2.2.1-alpha.0",
3
+ "version": "2.2.1-alpha.1",
4
4
  "private": false,
5
- "description": "Kungfu Buildchain reusable workflows, release governance, and CLI tooling.",
5
+ "description": "Buildchain Release Passport, release governance, CLI toolkit, and site facts.",
6
6
  "repository": "https://github.com/kungfu-systems/buildchain",
7
7
  "author": "Keren Dong",
8
8
  "license": "Apache-2.0",
@@ -15,6 +15,15 @@
15
15
  "./core": "./packages/core/index.js",
16
16
  "./logging": "./packages/core/logging.js",
17
17
  "./release-passport": "./packages/core/release-passport.js",
18
+ "./site/buildchain-site.json": "./dist/site/buildchain-site.json",
19
+ "./site/site-manifest.json": "./dist/site/site-manifest.json",
20
+ "./site/cli-registry.json": "./dist/site/cli-registry.json",
21
+ "./site/workflow-registry.json": "./dist/site/workflow-registry.json",
22
+ "./site/release-model.json": "./dist/site/release-model.json",
23
+ "./site/artifact-schemas.json": "./dist/site/artifact-schemas.json",
24
+ "./site/product-mechanism.json": "./dist/site/product-mechanism.json",
25
+ "./site/release-provenance.json": "./dist/site/release-provenance.json",
26
+ "./site/agent-index.json": "./dist/site/agent-index.json",
18
27
  "./package.json": "./package.json"
19
28
  },
20
29
  "files": [
@@ -24,18 +33,24 @@
24
33
  "LICENSE-POLICY.md",
25
34
  "scripts/*.mjs",
26
35
  "packages/core/",
36
+ "dist/site/",
27
37
  "README.md",
28
38
  "SECURITY.md",
29
39
  "docs/MAP.md",
40
+ "docs/binary-distribution.md",
30
41
  "docs/cli.md",
42
+ "docs/install.md",
31
43
  "docs/lifecycle-protocol.md",
32
44
  "docs/migration-inventory.md",
33
45
  "docs/ownership.md",
46
+ "docs/product-mechanism.md",
34
47
  "docs/publish-transaction.md",
35
48
  "docs/release-passport.md",
36
49
  "docs/release-flow.md",
37
50
  "docs/release-governance.md",
38
51
  "docs/reusable-build-surface.md",
52
+ "docs/site-bundle-contract.md",
53
+ "docs/toolkit-observability.md",
39
54
  "docs/versioning.md",
40
55
  "docs/web-surface-deployments.md",
41
56
  "actions/*/README.md",
@@ -50,8 +65,10 @@
50
65
  },
51
66
  "packageManager": "pnpm@11.7.0",
52
67
  "scripts": {
53
- "check": "node scripts/check-inventory.mjs && pnpm run check:workflows && pnpm run test:unit && pnpm -r --filter \"./actions/**\" build",
68
+ "check": "node scripts/check-inventory.mjs && pnpm run check:site && pnpm run check:workflows && pnpm run test:unit && pnpm -r --filter \"./actions/**\" build",
54
69
  "check:workflows": "bash scripts/check-workflows.sh",
70
+ "generate:site": "node scripts/generate-site-bundle.mjs",
71
+ "check:site": "node scripts/generate-site-bundle.mjs --check",
55
72
  "test:unit": "node --test tests/*.test.mjs",
56
73
  "build": "pnpm -r --filter \"./actions/**\" build",
57
74
  "package": "npm pack --dry-run --json --registry=https://registry.npmjs.org/",
@@ -15,11 +15,18 @@ const requiredPaths = [
15
15
  ".github/pull_request_template.md",
16
16
  "bin/buildchain.mjs",
17
17
  "docs/MAP.md",
18
+ "docs/binary-distribution.md",
18
19
  "docs/cli.md",
20
+ "docs/install.md",
21
+ "docs/product-mechanism.md",
19
22
  "docs/release-passport.md",
23
+ "docs/site-bundle-contract.md",
24
+ "docs/toolkit-observability.md",
20
25
  "docs/versioning.md",
21
26
  "scripts/release-line-dry-run.mjs",
22
27
  "scripts/build-standalone-binary.mjs",
28
+ "scripts/create-release-bundle.mjs",
29
+ "scripts/generate-site-bundle.mjs",
23
30
  "scripts/npm-publish-dry-run.mjs",
24
31
  "scripts/npm-publish-transaction.mjs",
25
32
  "docs/migration-inventory.md",
@@ -76,6 +83,11 @@ for (const expectedFile of ["bin/", "scripts/*.mjs", "packages/core/", "docs/MAP
76
83
  throw new Error(`root package files must include ${expectedFile}`);
77
84
  }
78
85
  }
86
+ for (const expectedFile of ["dist/site/", "docs/install.md", "docs/binary-distribution.md", "docs/site-bundle-contract.md"]) {
87
+ if (!rootPackage.files?.includes(expectedFile)) {
88
+ throw new Error(`root package files must include ${expectedFile}`);
89
+ }
90
+ }
79
91
  const cliSource = fs.readFileSync(path.join(root, "bin/buildchain.mjs"), "utf8");
80
92
  const coreIndexSource = fs.readFileSync(path.join(root, "packages/core/index.js"), "utf8");
81
93
  const versioningDoc = fs.readFileSync(path.join(root, "docs/versioning.md"), "utf8");
@@ -206,6 +218,8 @@ for (const requiredSnippet of [
206
218
  "bin/buildchain.mjs log summary",
207
219
  "collect github-release",
208
220
  "verify release-passport",
221
+ "scripts/create-release-bundle.mjs",
222
+ "buildchain-release-bundle",
209
223
  "gh release upload",
210
224
  ]) {
211
225
  if (!binaryDistributionWorkflow.includes(requiredSnippet)) {
@@ -247,12 +261,27 @@ if (inventory.safety?.releasePassport?.binaryDistribution?.productionRunnerDefau
247
261
  if (inventory.safety?.releasePassport?.binaryDistribution?.selfHostedRole !== "compatibility-fixture") {
248
262
  throw new Error("self-hosted runners must remain release passport compatibility fixtures");
249
263
  }
250
- for (const artifact of ["buildchain.release.json", "artifact-evidence.json", "impact.json", "agent-index.json", "llms.txt"]) {
264
+ for (const artifact of [
265
+ "buildchain.release.json",
266
+ "artifact-evidence.json",
267
+ "impact.json",
268
+ "agent-index.json",
269
+ "check-report.json",
270
+ "llms.txt",
271
+ "buildchain-release-bundle.json",
272
+ "buildchain-release-bundle.tar.gz",
273
+ ]) {
251
274
  if (!inventory.safety?.releasePassport?.protocolArtifacts?.includes(artifact)) {
252
275
  throw new Error(`release passport inventory missing protocol artifact ${artifact}`);
253
276
  }
254
277
  }
255
278
 
279
+ for (const siteFile of ["buildchain-site.json", "site-manifest.json", "cli-registry.json", "release-model.json"]) {
280
+ if (!fs.existsSync(path.join(root, "dist", "site", siteFile))) {
281
+ throw new Error(`site bundle missing ${siteFile}`);
282
+ }
283
+ }
284
+
256
285
  if (!Array.isArray(inventory.workflowSources) || inventory.workflowSources.length < 1) {
257
286
  throw new Error("workflowSources must include the migrated workflows repository");
258
287
  }
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from "node:child_process";
3
+ import crypto from "node:crypto";
4
+ import fs from "node:fs";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ import { pathToFileURL } from "node:url";
8
+
9
+ const BUNDLE_CONTRACT = "kungfu-buildchain-release-evidence-bundle";
10
+ const BUNDLE_BASE = "buildchain-release-bundle";
11
+
12
+ function readArg(name, fallback = "") {
13
+ const index = process.argv.indexOf(`--${name}`);
14
+ if (index === -1) {
15
+ return fallback;
16
+ }
17
+ return process.argv[index + 1] || "";
18
+ }
19
+
20
+ function sha256File(filePath) {
21
+ return crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
22
+ }
23
+
24
+ function listFiles(dir) {
25
+ if (!dir || !fs.existsSync(dir)) {
26
+ return [];
27
+ }
28
+ const results = [];
29
+ const walk = (current) => {
30
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
31
+ const entryPath = path.join(current, entry.name);
32
+ if (entry.isDirectory()) {
33
+ walk(entryPath);
34
+ } else if (entry.isFile()) {
35
+ results.push(entryPath);
36
+ }
37
+ }
38
+ };
39
+ walk(dir);
40
+ return results.sort((a, b) => a.localeCompare(b));
41
+ }
42
+
43
+ function copyFile(source, destination) {
44
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
45
+ fs.copyFileSync(source, destination);
46
+ }
47
+
48
+ function relative(root, target) {
49
+ return path.relative(root, target).split(path.sep).join("/");
50
+ }
51
+
52
+ function isBundleOutput(filePath) {
53
+ const name = path.basename(filePath);
54
+ return name === `${BUNDLE_BASE}.tar.gz` || name === `${BUNDLE_BASE}.json`;
55
+ }
56
+
57
+ export function createReleaseEvidenceBundle({
58
+ cwd = process.cwd(),
59
+ assetsDir = "dist/binary",
60
+ passportDir = ".buildchain/release-passport",
61
+ outputDir = ".buildchain/release-passport",
62
+ tag = "",
63
+ sourceSha = "",
64
+ } = {}) {
65
+ const resolvedAssetsDir = path.resolve(cwd, assetsDir);
66
+ const resolvedPassportDir = path.resolve(cwd, passportDir);
67
+ const resolvedOutputDir = path.resolve(cwd, outputDir);
68
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "buildchain-release-bundle-"));
69
+ const bundleRoot = path.join(tempDir, BUNDLE_BASE);
70
+ const sourceSets = [
71
+ { id: "release-assets", dir: resolvedAssetsDir, bundlePrefix: "release-assets" },
72
+ { id: "release-passport", dir: resolvedPassportDir, bundlePrefix: "release-passport" },
73
+ ];
74
+ const included = [];
75
+ for (const sourceSet of sourceSets) {
76
+ for (const sourcePath of listFiles(sourceSet.dir)) {
77
+ if (isBundleOutput(sourcePath)) {
78
+ continue;
79
+ }
80
+ const sourceRelative = relative(sourceSet.dir, sourcePath);
81
+ const bundlePath = path.join(bundleRoot, sourceSet.bundlePrefix, sourceRelative);
82
+ copyFile(sourcePath, bundlePath);
83
+ included.push({
84
+ source: sourceSet.id,
85
+ sourcePath: relative(cwd, sourcePath),
86
+ bundlePath: relative(bundleRoot, bundlePath),
87
+ size: fs.statSync(sourcePath).size,
88
+ sha256: sha256File(sourcePath),
89
+ });
90
+ }
91
+ }
92
+ if (included.length === 0) {
93
+ throw new Error("release evidence bundle requires at least one input file");
94
+ }
95
+
96
+ const index = {
97
+ schemaVersion: 1,
98
+ contract: BUNDLE_CONTRACT,
99
+ release: {
100
+ tag,
101
+ sourceSha,
102
+ },
103
+ entrypoints: {
104
+ releasePassport: "release-passport/buildchain.release.json",
105
+ checksums: "release-assets/checksums.txt",
106
+ agentIndex: "release-passport/agent-index.json",
107
+ llms: "release-passport/llms.txt",
108
+ },
109
+ files: included,
110
+ };
111
+ fs.writeFileSync(path.join(bundleRoot, `${BUNDLE_BASE}.index.json`), `${JSON.stringify(index, null, 2)}\n`);
112
+
113
+ fs.mkdirSync(resolvedOutputDir, { recursive: true });
114
+ const archivePath = path.join(resolvedOutputDir, `${BUNDLE_BASE}.tar.gz`);
115
+ const tar = spawnSync("tar", ["-czf", archivePath, "-C", tempDir, BUNDLE_BASE], {
116
+ cwd,
117
+ stdio: "inherit",
118
+ });
119
+ if (tar.error) {
120
+ throw tar.error;
121
+ }
122
+ if (tar.status !== 0) {
123
+ throw new Error(`tar exited with ${tar.status}`);
124
+ }
125
+ const manifest = {
126
+ ...index,
127
+ bundle: {
128
+ name: path.basename(archivePath),
129
+ path: relative(cwd, archivePath),
130
+ size: fs.statSync(archivePath).size,
131
+ sha256: sha256File(archivePath),
132
+ },
133
+ };
134
+ const manifestPath = path.join(resolvedOutputDir, `${BUNDLE_BASE}.json`);
135
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
136
+ return {
137
+ outputDir: resolvedOutputDir,
138
+ archivePath,
139
+ manifestPath,
140
+ manifest,
141
+ };
142
+ }
143
+
144
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
145
+ try {
146
+ const result = createReleaseEvidenceBundle({
147
+ cwd: path.resolve(readArg("cwd", process.cwd())),
148
+ assetsDir: readArg("assets-dir", "dist/binary"),
149
+ passportDir: readArg("passport-dir", ".buildchain/release-passport"),
150
+ outputDir: readArg("output-dir", ".buildchain/release-passport"),
151
+ tag: readArg("tag", process.env.RELEASE_TAG || ""),
152
+ sourceSha: readArg("source-sha", process.env.GITHUB_SHA || ""),
153
+ });
154
+ process.stdout.write(`${JSON.stringify({
155
+ contract: BUNDLE_CONTRACT,
156
+ archive: result.manifest.bundle,
157
+ fileCount: result.manifest.files.length,
158
+ }, null, 2)}\n`);
159
+ } catch (error) {
160
+ console.error(`buildchain release bundle: ${error.message}`);
161
+ process.exitCode = 1;
162
+ }
163
+ }