@kungfu-tech/buildchain 4.0.2-alpha.9 → 4.0.2

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.
Files changed (157) hide show
  1. package/AGENTS.md +10 -1
  2. package/CONTRIBUTING.md +24 -0
  3. package/README.md +2 -2
  4. package/actions/promote-buildchain-ref/README.md +1 -1
  5. package/actions/v4-release-candidate-promote/README.md +28 -0
  6. package/architecture/agent-change-map.md +64 -7
  7. package/architecture/ci-lane-change-budget.json +366 -0
  8. package/architecture/internal-capabilities.json +53 -8
  9. package/architecture/maintainability-debt.json +147 -93
  10. package/architecture/maintainability-policy.json +32 -28
  11. package/architecture/release-tail-contract-inventory.json +6 -6
  12. package/architecture/v4-architecture-constitution.md +23 -20
  13. package/architecture/v4-cross-platform-adopter-qualification.json +1 -1
  14. package/architecture/v4-delivery-authority-parity.json +2 -2
  15. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.json +1 -1
  16. package/architecture/v4-floating-consumer-policy.json +2 -2
  17. package/architecture/v4-next-development-parity.json +5 -5
  18. package/architecture/v4-partial-mutation-recovery-qualification.json +4 -1
  19. package/architecture/v4-platform-stage-checkpoints.json +1 -1
  20. package/architecture/v4-provider-operation-journal-contract.json +5 -2
  21. package/architecture/v4-publication-facade-patches.json +64 -0
  22. package/architecture/v4-release-activation-shadow-domain.json +5 -2
  23. package/architecture/v4-release-topology.json +74 -39
  24. package/architecture/v4-rust-wasm-production-authority.json +58 -0
  25. package/architecture/v4-stable-publication-fence.json +5 -2
  26. package/architecture/v4-stage-capsule-qualification.json +2 -2
  27. package/architecture/v4-tail-reseal-parity.json +8 -6
  28. package/architecture/v4-universal-workflow-bootstrap.json +171 -0
  29. package/architecture/v4-universal-workflow-fault-campaign.json +72 -0
  30. package/architecture/v4-universal-workflow-train-admission.json +40 -0
  31. package/architecture/workflow-taxonomy.json +1451 -0
  32. package/bin/buildchain.mjs +7 -7
  33. package/contracts/buildchain-v2-residuals-v1.json +57 -48
  34. package/contracts/dev-delivery-authority-v2.schema.json +16 -2
  35. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  36. package/contracts/v4-canonical-contracts-v1.schema.json +1 -0
  37. package/dist/site/buildchain-contract.json +93 -48
  38. package/dist/site/buildchain-site.json +222 -69
  39. package/dist/site/capability-registry.json +4 -4
  40. package/dist/site/controller-registry.json +50 -13
  41. package/dist/site/kfd-claims.json +2585 -1000
  42. package/dist/site/kfd-upstream-aggregate.json +1 -1
  43. package/dist/site/manual-registry.json +13 -13
  44. package/dist/site/node-api-registry.json +120 -115
  45. package/dist/site/page-registry.json +204 -51
  46. package/dist/site/public-surface-audit.json +3205 -1219
  47. package/dist/site/publication-authority-registry.json +1124 -362
  48. package/dist/site/publication-registry.json +4 -4
  49. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +16 -2
  50. package/dist/site/site-manifest.json +17 -17
  51. package/dist/site/workflow-registry.json +5061 -1659
  52. package/docs/MAP.md +1 -0
  53. package/docs/auditable-demo.md +1 -1
  54. package/docs/cli.md +21 -8
  55. package/docs/dev-delivery-warrant.md +1 -1
  56. package/docs/engineering-housekeeper.md +3 -3
  57. package/docs/kfd-support.md +26 -2
  58. package/docs/next-development-transition.md +2 -2
  59. package/docs/node-api-reference.md +190 -190
  60. package/docs/publication-authority.md +2 -2
  61. package/docs/release-candidate.md +3 -3
  62. package/docs/release-flow.md +67 -19
  63. package/docs/release-governance.md +8 -8
  64. package/docs/release-passport.md +27 -2
  65. package/docs/reusable-build-surface.md +16 -0
  66. package/docs/v4-adopter-delivery.md +2 -2
  67. package/docs/v4-production-release.md +71 -8
  68. package/docs/v4-rust-wasm-production-authority.md +58 -0
  69. package/docs/v4-stage-capsule.md +2 -2
  70. package/docs/v4-tail-reseal.md +1 -1
  71. package/docs/workflow-catalog.md +154 -0
  72. package/docs/workflow-path-migration.md +109 -0
  73. package/package.json +10 -6
  74. package/packages/core/artifact-verification-envelope.js +5 -5
  75. package/packages/core/buildchain-contract.js +1 -1
  76. package/packages/core/buildchain-kfd-claims.js +2 -2
  77. package/packages/core/buildchain-publication-authority.js +42 -26
  78. package/packages/core/buildchain-v4-domain.wasm +0 -0
  79. package/packages/core/ci-lane-change-budget.js +14 -1
  80. package/packages/core/controller-evidence.js +4 -1
  81. package/packages/core/dev-delivery-candidate-identity.js +40 -6
  82. package/packages/core/dev-delivery-warrant-state.js +3 -4
  83. package/packages/core/dev-delivery-warrant.js +12 -2
  84. package/packages/core/github-governance-authority.js +4 -4
  85. package/packages/core/kfd-adopter-manifest.js +46 -47
  86. package/packages/core/next-development-projection.js +2 -2
  87. package/packages/core/paper-agent-entry.js +30 -4
  88. package/packages/core/paper-fleet.js +1 -1
  89. package/packages/core/paper-runtime-channels.js +348 -0
  90. package/packages/core/paper-scaffold-content.js +10 -0
  91. package/packages/core/paper.js +37 -71
  92. package/packages/core/release-line-dry-run.js +1 -1
  93. package/packages/core/release-passport-contract.js +5 -5
  94. package/packages/core/release-passport.js +5 -4
  95. package/packages/core/release-tail-provider-plane.js +68 -1048
  96. package/packages/core/v4-canonical-contracts.js +10 -75
  97. package/packages/core/v4-domain-wasm-artifact.js +7 -0
  98. package/packages/core/v4-domain-wasm.js +216 -0
  99. package/packages/core/v4-floating-consumer-evidence.js +1 -1
  100. package/packages/core/v4-floating-consumer-policy.js +1 -1
  101. package/packages/core/v4-partial-mutation-recovery-qualification.js +9 -386
  102. package/packages/core/v4-product-publication.js +19 -349
  103. package/packages/core/v4-protected-publication-source.js +19 -21
  104. package/packages/core/v4-provider-operation-journal.js +18 -487
  105. package/packages/core/v4-provider-readback-idempotency.js +12 -97
  106. package/packages/core/v4-release-activation-shadow.js +13 -500
  107. package/packages/core/v4-release-invocation.js +28 -389
  108. package/packages/core/v4-stable-publication-fence.js +13 -342
  109. package/packages/core/v4-universal-workflow-bootstrap.js +586 -0
  110. package/scripts/binary-publication-evidence.mjs +135 -0
  111. package/scripts/build-contract-core.mjs +1 -14
  112. package/scripts/build-v4-domain-wasm.mjs +189 -0
  113. package/scripts/check-action-bundles.mjs +12 -2
  114. package/scripts/check-ci-lane-change-budget.mjs +4 -1
  115. package/scripts/check-core-mechanism-inventory.mjs +4 -3
  116. package/scripts/check-inventory.mjs +35 -44
  117. package/scripts/check-maintainability.mjs +16 -113
  118. package/scripts/check-v4-floating-consumer-policy-contract.mjs +2 -2
  119. package/scripts/check-v4-public-dogfood-contract.mjs +22 -9
  120. package/scripts/check-v4-release-topology.mjs +90 -33
  121. package/scripts/check-v4-runtime-semantic-closure.mjs +4 -1
  122. package/scripts/check-v4-universal-workflow-bootstrap.mjs +266 -0
  123. package/scripts/check-workflow-taxonomy.mjs +6 -0
  124. package/scripts/copy-v4-domain-wasm.mjs +14 -0
  125. package/scripts/dev-delivery-authority-command-adapters.mjs +1 -1
  126. package/scripts/dev-delivery-authority.mjs +8 -8
  127. package/scripts/dev-delivery-source-proof-reuse.mjs +2 -1
  128. package/scripts/dev-delivery-warrant-options.mjs +14 -9
  129. package/scripts/dev-delivery-warrant-store.mjs +73 -7
  130. package/scripts/dev-delivery-warrant-transition.mjs +109 -0
  131. package/scripts/dev-delivery-warrant.mjs +76 -99
  132. package/scripts/dev-pr-auto-merge.mjs +15 -15
  133. package/scripts/generate-channel-build-workflow.mjs +7 -2
  134. package/scripts/generate-channel-promotion-workflow.mjs +7 -5
  135. package/scripts/generate-site-bundle.mjs +11 -11
  136. package/scripts/generate-v4-universal-workflow-facades.mjs +458 -0
  137. package/scripts/generate-workflow-taxonomy.mjs +25 -0
  138. package/scripts/github-output.mjs +31 -0
  139. package/scripts/maintainability-metrics.mjs +16 -5
  140. package/scripts/maintainability-public-surface.mjs +115 -0
  141. package/scripts/next-development-review.mjs +307 -0
  142. package/scripts/paper-work-fleet-cli.mjs +2 -0
  143. package/scripts/release-asset-client.mjs +91 -0
  144. package/scripts/release-candidate-resolver.mjs +9 -9
  145. package/scripts/source-verification-evidence.mjs +344 -0
  146. package/scripts/stable-candidate-qualification.mjs +1 -1
  147. package/scripts/universal-facade-maintainability.mjs +82 -0
  148. package/scripts/v4-product-publication-intent.mjs +13 -6
  149. package/scripts/v4-publication-settlement.mjs +211 -0
  150. package/scripts/v4-universal-workflow-backflow.mjs +212 -0
  151. package/scripts/v4-universal-workflow-engine.mjs +618 -0
  152. package/scripts/v4-universal-workflow-self-dogfood.mjs +205 -0
  153. package/scripts/verify-version-state-delta.mjs +173 -0
  154. package/scripts/workflow-taxonomy.mjs +507 -0
  155. package/templates/native-dev-delivery.yml +4 -7
  156. package/templates/universal-buildchain-bootstrap-recovery.yml +282 -0
  157. package/templates/universal-buildchain-bootstrap.yml +28 -0
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env node
2
+ import crypto from "node:crypto";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import {
7
+ V4_UNIVERSAL_WORKFLOW_REQUEST,
8
+ validateV4UniversalWorkflowRequest,
9
+ v4UniversalWorkflowAdmissionRoot,
10
+ } from "../packages/core/v4-universal-workflow-bootstrap.js";
11
+
12
+ const SHA = /^[0-9a-f]{40}$/u;
13
+
14
+ function fail(message) {
15
+ throw new Error(message);
16
+ }
17
+
18
+ function canonical(value) {
19
+ if (Array.isArray(value)) return value.map(canonical);
20
+ if (value && typeof value === "object")
21
+ return Object.fromEntries(
22
+ Object.keys(value)
23
+ .sort((left, right) => Buffer.from(left).compare(Buffer.from(right)))
24
+ .map((key) => [key, canonical(value[key])]),
25
+ );
26
+ return value;
27
+ }
28
+
29
+ function contentRoot(domain, value) {
30
+ const hash = crypto.createHash("sha256");
31
+ hash.update(domain, "utf8");
32
+ hash.update(Buffer.from([0]));
33
+ hash.update(`${JSON.stringify(canonical(value))}\n`, "utf8");
34
+ return `sha256:${hash.digest("hex")}`;
35
+ }
36
+
37
+ function defaultPolicy() {
38
+ return JSON.parse(
39
+ fs.readFileSync(
40
+ new URL(
41
+ "../architecture/v4-universal-workflow-train-admission.json",
42
+ import.meta.url,
43
+ ),
44
+ ),
45
+ );
46
+ }
47
+
48
+ function exactSha(value, label) {
49
+ const normalized = String(value || "").toLowerCase();
50
+ if (!SHA.test(normalized)) fail(`${label} must be an exact Git SHA`);
51
+ return normalized;
52
+ }
53
+
54
+ export function createV4UniversalSelfDogfoodRequest({
55
+ candidateSha,
56
+ consumerSha,
57
+ pullRequest,
58
+ channel,
59
+ policy = defaultPolicy(),
60
+ }) {
61
+ const expectedSha = exactSha(candidateSha, "candidateSha");
62
+ const sourceSha = exactSha(consumerSha, "consumerSha");
63
+ if (!Number.isSafeInteger(pullRequest) || pullRequest < 1)
64
+ fail("pullRequest must be a positive integer");
65
+ if (!["alpha", "stable", "conformance"].includes(channel))
66
+ fail("channel must be alpha, stable, or conformance");
67
+ const capability =
68
+ channel === "conformance"
69
+ ? {
70
+ id: "bootstrap-conformance",
71
+ payload: {
72
+ schema: "kungfu-buildchain-v4-universal-bootstrap-conformance/v1",
73
+ expectedGovernedWorkflowCount: 40,
74
+ },
75
+ }
76
+ : {
77
+ id: "release-candidate-promote",
78
+ payload: {
79
+ schema: "kungfu-buildchain-v4-universal-release-promotion/v1",
80
+ inputs: {
81
+ channel,
82
+ "dry-run": true,
83
+ "target-ref":
84
+ channel === "alpha" ? "alpha/v4/v4.0" : "release/v4/v4.0",
85
+ "target-sha": sourceSha,
86
+ },
87
+ dryRunObservation: {
88
+ observedSha: sourceSha,
89
+ comparisonStatus: "identical",
90
+ },
91
+ },
92
+ };
93
+ return validateV4UniversalWorkflowRequest({
94
+ schema: V4_UNIVERSAL_WORKFLOW_REQUEST,
95
+ mode: "train",
96
+ candidate: {
97
+ repository: "kungfu-systems/buildchain",
98
+ discoveryRef: "train/v4/v4.0/universal-reusable-workflow-bootstrap",
99
+ expectedSha,
100
+ admissionRoot: v4UniversalWorkflowAdmissionRoot(policy),
101
+ reviewPullRequest: pullRequest,
102
+ },
103
+ consumer: {
104
+ repository: "kungfu-systems/buildchain",
105
+ workflow: ".github/workflows/self-ops-bootstrap-dogfood.yml",
106
+ sourceSha,
107
+ },
108
+ capability: {
109
+ id: capability.id,
110
+ contractRoots: policy.contractRoots,
111
+ permissions: { contents: "read" },
112
+ },
113
+ payload: capability.payload,
114
+ });
115
+ }
116
+
117
+ function exactResult(value, label, expectedSha, expectedChannel) {
118
+ if (
119
+ value?.schema !== "kungfu-buildchain-v4-universal-workflow-result/v1" ||
120
+ value.status !== "succeeded" ||
121
+ value.runtime?.repository !== "kungfu-systems/buildchain" ||
122
+ value.runtime?.sha !== expectedSha
123
+ )
124
+ fail(`${label} is not a successful exact-Train result`);
125
+ if (expectedChannel === "conformance") {
126
+ if (
127
+ value.output?.schema !==
128
+ "kungfu-buildchain-v4-universal-bootstrap-conformance-result/v1" ||
129
+ value.output?.status !== "candidate-engine-executed"
130
+ )
131
+ fail(`${label} did not execute the candidate conformance engine`);
132
+ } else if (
133
+ value.output?.dryRun !== true ||
134
+ value.output?.route?.decision !== "Fresh" ||
135
+ value.output?.route?.channel !== expectedChannel
136
+ ) {
137
+ fail(
138
+ `${label} did not execute the ${expectedChannel}-shaped release route`,
139
+ );
140
+ }
141
+ return value;
142
+ }
143
+
144
+ export function verifyV4UniversalSelfDogfoodPair({
145
+ primary,
146
+ recovery,
147
+ expectedSha,
148
+ channel,
149
+ }) {
150
+ const sha = exactSha(expectedSha, "expectedSha");
151
+ const left = exactResult(primary, "primary result", sha, channel);
152
+ const right = exactResult(recovery, "recovery result", sha, channel);
153
+ for (const field of ["requestRoot", "capabilityRoot", "resultRoot"]) {
154
+ if (left[field] !== right[field])
155
+ fail(`primary and recovery ${field} differ`);
156
+ }
157
+ const body = {
158
+ schema: "kungfu-buildchain-v4-universal-self-dogfood-pair/v1",
159
+ channel,
160
+ runtimeSha: sha,
161
+ requestRoot: left.requestRoot,
162
+ capabilityRoot: left.capabilityRoot,
163
+ resultRoot: left.resultRoot,
164
+ equivalent: true,
165
+ };
166
+ return {
167
+ ...body,
168
+ pairRoot: contentRoot("universal-self-dogfood-pair", body),
169
+ };
170
+ }
171
+
172
+ function option(name) {
173
+ const index = process.argv.indexOf(`--${name}`);
174
+ return index >= 0 ? process.argv[index + 1] : "";
175
+ }
176
+
177
+ async function main() {
178
+ const command = process.argv[2];
179
+ if (command === "request") {
180
+ const request = createV4UniversalSelfDogfoodRequest({
181
+ candidateSha: option("candidate-sha"),
182
+ consumerSha: option("consumer-sha"),
183
+ pullRequest: Number(option("pull-request")),
184
+ channel: option("channel"),
185
+ });
186
+ process.stdout.write(`${JSON.stringify(request)}\n`);
187
+ return;
188
+ }
189
+ if (command === "verify-pair") {
190
+ const result = verifyV4UniversalSelfDogfoodPair({
191
+ primary: JSON.parse(process.env.BUILDCHAIN_PRIMARY_RESULT_JSON || ""),
192
+ recovery: JSON.parse(process.env.BUILDCHAIN_RECOVERY_RESULT_JSON || ""),
193
+ expectedSha: option("candidate-sha"),
194
+ channel: option("channel"),
195
+ });
196
+ process.stdout.write(`${JSON.stringify(result)}\n`);
197
+ return;
198
+ }
199
+ fail("command must be request or verify-pair");
200
+ }
201
+
202
+ const isMain =
203
+ process.argv[1] &&
204
+ fileURLToPath(import.meta.url) === path.resolve(process.argv[1]);
205
+ if (isMain) await main();
@@ -0,0 +1,173 @@
1
+ import fs from "node:fs";
2
+ import crypto from "node:crypto";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { execFileSync } from "node:child_process";
6
+ import { pathToFileURL } from "node:url";
7
+ import { invokeV4DomainWasm } from "../packages/core/v4-domain-wasm.js";
8
+
9
+ const execute = (command, args, options = {}) =>
10
+ execFileSync(command, args, {
11
+ maxBuffer: 64 * 1024 * 1024,
12
+ ...options,
13
+ });
14
+
15
+ // Reconstruct with the protected base's generator. No candidate command runs.
16
+ export function verifyVersionStateDelta({
17
+ baseSha,
18
+ headSha,
19
+ cwd = process.cwd(),
20
+ nodeModules = path.resolve("node_modules"),
21
+ }) {
22
+ if (![baseSha, headSha].every((value) => /^[a-f0-9]{40}$/u.test(value || "")))
23
+ throw new Error("version-state verification requires exact commits");
24
+ const git = (...args) => execute("git", args, { cwd }).toString().trim();
25
+ const bytes = (revision, file) =>
26
+ execute("git", ["show", `${revision}:${file}`], { cwd });
27
+ git("merge-base", "--is-ancestor", baseSha, headSha);
28
+ const changes = git("diff", "--raw", "--no-renames", baseSha, headSha)
29
+ .split("\n")
30
+ .filter(Boolean);
31
+ if (!changes.length) throw new Error("no version-state delta");
32
+ const temporary = fs.mkdtempSync(
33
+ path.join(os.tmpdir(), "buildchain-version-state-"),
34
+ );
35
+ const source = path.join(temporary, "source");
36
+ try {
37
+ fs.mkdirSync(source);
38
+ const archive = path.join(temporary, "base.tar");
39
+ git("-c", "core.autocrlf=false", "-c", "core.eol=lf", "archive", "--format=tar", `--output=${archive}`, baseSha);
40
+ execute("tar", ["-xf", "base.tar", "-C", "source"], { cwd: temporary });
41
+ fs.symlinkSync(nodeModules, path.join(source, "node_modules"), "junction");
42
+ const planSource = `
43
+ import fs from 'node:fs';
44
+ import { loadBuildchainConfig, discoverConfiguredVersionStateFiles,
45
+ updateConfiguredVersionStateContents } from './packages/core/buildchain-config.js';
46
+ const config = loadBuildchainConfig(process.cwd());
47
+ const files = discoverConfiguredVersionStateFiles(process.cwd(), config);
48
+ const allowed = [...new Set([...files.map(f => f.path), ...config.config.version.derivedFiles])];
49
+ if (JSON.stringify(config.config.lifecycle['version-state'].commands) !== JSON.stringify(['node scripts/generate-site-bundle.mjs']))
50
+ throw new Error('unsupported version-state generator');
51
+ if (process.env.BUILDCHAIN_DELTA_VERSION)
52
+ for (const f of updateConfiguredVersionStateContents(files, process.env.BUILDCHAIN_DELTA_VERSION))
53
+ fs.writeFileSync(f.path, f.content);
54
+ console.log(JSON.stringify(allowed));
55
+ `;
56
+ const plan = (version = "") =>
57
+ JSON.parse(
58
+ execute(process.execPath, ["--input-type=module", "-e", planSource], {
59
+ cwd: source,
60
+ env: { ...process.env, BUILDCHAIN_DELTA_VERSION: version },
61
+ }),
62
+ );
63
+ const allowed = plan();
64
+ for (const file of allowed)
65
+ if (path.isAbsolute(file) || file.split(/[\\/]/u).includes(".."))
66
+ throw new Error("unsafe declared version-state path");
67
+ for (const change of changes) {
68
+ const match = change.match(
69
+ /^:100644 100644 [a-f0-9]+ [a-f0-9]+ M\t(.+)$/u,
70
+ );
71
+ if (!match || !allowed.includes(match[1]))
72
+ throw new Error(
73
+ "delta includes undeclared source, lock, config or file mode changes",
74
+ );
75
+ }
76
+ const version = JSON.parse(bytes(headSha, "package.json")).version;
77
+ invokeV4DomainWasm("source-version-projection", {
78
+ baseVersion: JSON.parse(bytes(baseSha, "package.json")).version,
79
+ version,
80
+ });
81
+ const metadata = JSON.parse(bytes(headSha, "dist/site/site-manifest.json"));
82
+ if (
83
+ metadata.sourceRevision !== baseSha ||
84
+ metadata.package.version !== version ||
85
+ !Number.isFinite(Date.parse(metadata.generatedAt)) ||
86
+ metadata.generatedAt !== metadata.publishedAt
87
+ )
88
+ throw new Error(
89
+ "version-state projection source metadata is not bound to base",
90
+ );
91
+ plan(version);
92
+ execute(process.execPath, ["scripts/generate-site-bundle.mjs"], {
93
+ cwd: source,
94
+ env: {
95
+ ...process.env,
96
+ BUILDCHAIN_SOURCE_SHA: baseSha,
97
+ BUILDCHAIN_SITE_GENERATED_AT: metadata.generatedAt,
98
+ BUILDCHAIN_SITE_PUBLISHED_AT: metadata.publishedAt,
99
+ BUILDCHAIN_SITE_TIMESTAMP_POLICY: "ci-injected",
100
+ BUILDCHAIN_SURFACE_GENERATED_AT: metadata.generatedAt,
101
+ BUILDCHAIN_SURFACE_PUBLISHED_AT: metadata.publishedAt,
102
+ SOURCE_DATE_EPOCH: "0",
103
+ },
104
+ });
105
+ // Git blob hashes compare all tracked bytes without executing candidate code.
106
+ const expected = new Map(
107
+ git("ls-tree", "-r", headSha)
108
+ .split("\n")
109
+ .map((line) => {
110
+ const [meta, file] = line.split("\t");
111
+ return [file, meta.split(" ")[2]];
112
+ }),
113
+ );
114
+ for (const [file, digest] of expected) {
115
+ const generated = path.join(source, file);
116
+ const bytes = fs.lstatSync(generated).isSymbolicLink()
117
+ ? Buffer.from(fs.readlinkSync(generated))
118
+ : fs.readFileSync(generated);
119
+ const actual = crypto
120
+ .createHash("sha1")
121
+ .update(`blob ${bytes.length}\0`)
122
+ .update(bytes)
123
+ .digest("hex");
124
+ if (actual !== digest)
125
+ throw new Error(`version-state regeneration mismatch: ${file}`);
126
+ }
127
+ const walk = (directory) => {
128
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
129
+ const absolute = path.join(directory, entry.name);
130
+ if (absolute === path.join(source, "node_modules")) continue;
131
+ if (entry.isDirectory()) walk(absolute);
132
+ else if (
133
+ !expected.has(
134
+ path.relative(source, absolute).split(path.sep).join("/"),
135
+ )
136
+ )
137
+ throw new Error("version-state generator created undeclared output");
138
+ }
139
+ };
140
+ walk(source);
141
+ return {
142
+ validationKind: "version-state-projection",
143
+ baseSha,
144
+ headSha,
145
+ sourceTree: git("rev-parse", `${headSha}^{tree}`),
146
+ version,
147
+ changedPaths: changes.map((change) => change.split("\t")[1]),
148
+ generator: "scripts/generate-site-bundle.mjs",
149
+ generatedAt: metadata.generatedAt,
150
+ };
151
+ } finally {
152
+ fs.rmSync(temporary, { recursive: true, force: true });
153
+ }
154
+ }
155
+
156
+ if (
157
+ process.argv[1] &&
158
+ import.meta.url === pathToFileURL(process.argv[1]).href
159
+ ) {
160
+ try {
161
+ console.log(
162
+ JSON.stringify(
163
+ verifyVersionStateDelta({
164
+ baseSha: process.argv[2],
165
+ headSha: process.argv[3],
166
+ }),
167
+ ),
168
+ );
169
+ } catch (error) {
170
+ console.error(error.message);
171
+ process.exitCode = 1;
172
+ }
173
+ }