@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.
- package/AGENTS.md +10 -1
- package/CONTRIBUTING.md +24 -0
- package/README.md +2 -2
- package/actions/promote-buildchain-ref/README.md +1 -1
- package/actions/v4-release-candidate-promote/README.md +28 -0
- package/architecture/agent-change-map.md +64 -7
- package/architecture/ci-lane-change-budget.json +366 -0
- package/architecture/internal-capabilities.json +53 -8
- package/architecture/maintainability-debt.json +147 -93
- package/architecture/maintainability-policy.json +32 -28
- package/architecture/release-tail-contract-inventory.json +6 -6
- package/architecture/v4-architecture-constitution.md +23 -20
- package/architecture/v4-cross-platform-adopter-qualification.json +1 -1
- package/architecture/v4-delivery-authority-parity.json +2 -2
- package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.json +1 -1
- package/architecture/v4-floating-consumer-policy.json +2 -2
- package/architecture/v4-next-development-parity.json +5 -5
- package/architecture/v4-partial-mutation-recovery-qualification.json +4 -1
- package/architecture/v4-platform-stage-checkpoints.json +1 -1
- package/architecture/v4-provider-operation-journal-contract.json +5 -2
- package/architecture/v4-publication-facade-patches.json +64 -0
- package/architecture/v4-release-activation-shadow-domain.json +5 -2
- package/architecture/v4-release-topology.json +74 -39
- package/architecture/v4-rust-wasm-production-authority.json +58 -0
- package/architecture/v4-stable-publication-fence.json +5 -2
- package/architecture/v4-stage-capsule-qualification.json +2 -2
- package/architecture/v4-tail-reseal-parity.json +8 -6
- package/architecture/v4-universal-workflow-bootstrap.json +171 -0
- package/architecture/v4-universal-workflow-fault-campaign.json +72 -0
- package/architecture/v4-universal-workflow-train-admission.json +40 -0
- package/architecture/workflow-taxonomy.json +1451 -0
- package/bin/buildchain.mjs +7 -7
- package/contracts/buildchain-v2-residuals-v1.json +57 -48
- package/contracts/dev-delivery-authority-v2.schema.json +16 -2
- package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
- package/contracts/v4-canonical-contracts-v1.schema.json +1 -0
- package/dist/site/buildchain-contract.json +93 -48
- package/dist/site/buildchain-site.json +222 -69
- package/dist/site/capability-registry.json +4 -4
- package/dist/site/controller-registry.json +50 -13
- package/dist/site/kfd-claims.json +2585 -1000
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +13 -13
- package/dist/site/node-api-registry.json +120 -115
- package/dist/site/page-registry.json +204 -51
- package/dist/site/public-surface-audit.json +3205 -1219
- package/dist/site/publication-authority-registry.json +1124 -362
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/schemas/dev-delivery-authority-v2.schema.json +16 -2
- package/dist/site/site-manifest.json +17 -17
- package/dist/site/workflow-registry.json +5061 -1659
- package/docs/MAP.md +1 -0
- package/docs/auditable-demo.md +1 -1
- package/docs/cli.md +21 -8
- package/docs/dev-delivery-warrant.md +1 -1
- package/docs/engineering-housekeeper.md +3 -3
- package/docs/kfd-support.md +26 -2
- package/docs/next-development-transition.md +2 -2
- package/docs/node-api-reference.md +190 -190
- package/docs/publication-authority.md +2 -2
- package/docs/release-candidate.md +3 -3
- package/docs/release-flow.md +67 -19
- package/docs/release-governance.md +8 -8
- package/docs/release-passport.md +27 -2
- package/docs/reusable-build-surface.md +16 -0
- package/docs/v4-adopter-delivery.md +2 -2
- package/docs/v4-production-release.md +71 -8
- package/docs/v4-rust-wasm-production-authority.md +58 -0
- package/docs/v4-stage-capsule.md +2 -2
- package/docs/v4-tail-reseal.md +1 -1
- package/docs/workflow-catalog.md +154 -0
- package/docs/workflow-path-migration.md +109 -0
- package/package.json +10 -6
- package/packages/core/artifact-verification-envelope.js +5 -5
- package/packages/core/buildchain-contract.js +1 -1
- package/packages/core/buildchain-kfd-claims.js +2 -2
- package/packages/core/buildchain-publication-authority.js +42 -26
- package/packages/core/buildchain-v4-domain.wasm +0 -0
- package/packages/core/ci-lane-change-budget.js +14 -1
- package/packages/core/controller-evidence.js +4 -1
- package/packages/core/dev-delivery-candidate-identity.js +40 -6
- package/packages/core/dev-delivery-warrant-state.js +3 -4
- package/packages/core/dev-delivery-warrant.js +12 -2
- package/packages/core/github-governance-authority.js +4 -4
- package/packages/core/kfd-adopter-manifest.js +46 -47
- package/packages/core/next-development-projection.js +2 -2
- package/packages/core/paper-agent-entry.js +30 -4
- package/packages/core/paper-fleet.js +1 -1
- package/packages/core/paper-runtime-channels.js +348 -0
- package/packages/core/paper-scaffold-content.js +10 -0
- package/packages/core/paper.js +37 -71
- package/packages/core/release-line-dry-run.js +1 -1
- package/packages/core/release-passport-contract.js +5 -5
- package/packages/core/release-passport.js +5 -4
- package/packages/core/release-tail-provider-plane.js +68 -1048
- package/packages/core/v4-canonical-contracts.js +10 -75
- package/packages/core/v4-domain-wasm-artifact.js +7 -0
- package/packages/core/v4-domain-wasm.js +216 -0
- package/packages/core/v4-floating-consumer-evidence.js +1 -1
- package/packages/core/v4-floating-consumer-policy.js +1 -1
- package/packages/core/v4-partial-mutation-recovery-qualification.js +9 -386
- package/packages/core/v4-product-publication.js +19 -349
- package/packages/core/v4-protected-publication-source.js +19 -21
- package/packages/core/v4-provider-operation-journal.js +18 -487
- package/packages/core/v4-provider-readback-idempotency.js +12 -97
- package/packages/core/v4-release-activation-shadow.js +13 -500
- package/packages/core/v4-release-invocation.js +28 -389
- package/packages/core/v4-stable-publication-fence.js +13 -342
- package/packages/core/v4-universal-workflow-bootstrap.js +586 -0
- package/scripts/binary-publication-evidence.mjs +135 -0
- package/scripts/build-contract-core.mjs +1 -14
- package/scripts/build-v4-domain-wasm.mjs +189 -0
- package/scripts/check-action-bundles.mjs +12 -2
- package/scripts/check-ci-lane-change-budget.mjs +4 -1
- package/scripts/check-core-mechanism-inventory.mjs +4 -3
- package/scripts/check-inventory.mjs +35 -44
- package/scripts/check-maintainability.mjs +16 -113
- package/scripts/check-v4-floating-consumer-policy-contract.mjs +2 -2
- package/scripts/check-v4-public-dogfood-contract.mjs +22 -9
- package/scripts/check-v4-release-topology.mjs +90 -33
- package/scripts/check-v4-runtime-semantic-closure.mjs +4 -1
- package/scripts/check-v4-universal-workflow-bootstrap.mjs +266 -0
- package/scripts/check-workflow-taxonomy.mjs +6 -0
- package/scripts/copy-v4-domain-wasm.mjs +14 -0
- package/scripts/dev-delivery-authority-command-adapters.mjs +1 -1
- package/scripts/dev-delivery-authority.mjs +8 -8
- package/scripts/dev-delivery-source-proof-reuse.mjs +2 -1
- package/scripts/dev-delivery-warrant-options.mjs +14 -9
- package/scripts/dev-delivery-warrant-store.mjs +73 -7
- package/scripts/dev-delivery-warrant-transition.mjs +109 -0
- package/scripts/dev-delivery-warrant.mjs +76 -99
- package/scripts/dev-pr-auto-merge.mjs +15 -15
- package/scripts/generate-channel-build-workflow.mjs +7 -2
- package/scripts/generate-channel-promotion-workflow.mjs +7 -5
- package/scripts/generate-site-bundle.mjs +11 -11
- package/scripts/generate-v4-universal-workflow-facades.mjs +458 -0
- package/scripts/generate-workflow-taxonomy.mjs +25 -0
- package/scripts/github-output.mjs +31 -0
- package/scripts/maintainability-metrics.mjs +16 -5
- package/scripts/maintainability-public-surface.mjs +115 -0
- package/scripts/next-development-review.mjs +307 -0
- package/scripts/paper-work-fleet-cli.mjs +2 -0
- package/scripts/release-asset-client.mjs +91 -0
- package/scripts/release-candidate-resolver.mjs +9 -9
- package/scripts/source-verification-evidence.mjs +344 -0
- package/scripts/stable-candidate-qualification.mjs +1 -1
- package/scripts/universal-facade-maintainability.mjs +82 -0
- package/scripts/v4-product-publication-intent.mjs +13 -6
- package/scripts/v4-publication-settlement.mjs +211 -0
- package/scripts/v4-universal-workflow-backflow.mjs +212 -0
- package/scripts/v4-universal-workflow-engine.mjs +618 -0
- package/scripts/v4-universal-workflow-self-dogfood.mjs +205 -0
- package/scripts/verify-version-state-delta.mjs +173 -0
- package/scripts/workflow-taxonomy.mjs +507 -0
- package/templates/native-dev-delivery.yml +4 -7
- package/templates/universal-buildchain-bootstrap-recovery.yml +282 -0
- package/templates/universal-buildchain-bootstrap.yml +28 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { invokeV4DomainWasm } from "../packages/core/v4-domain-wasm.js";
|
|
7
|
+
|
|
8
|
+
const workflow = ".github/workflows/self-build-verify.yml";
|
|
9
|
+
const proofPath = ".buildchain/source-verification/evidence.json";
|
|
10
|
+
const hash = (bytes) =>
|
|
11
|
+
`sha256:${crypto.createHash("sha256").update(bytes).digest("hex")}`;
|
|
12
|
+
const execute = (command, args, encoding) =>
|
|
13
|
+
execFileSync(command, args, {
|
|
14
|
+
encoding,
|
|
15
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
16
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
17
|
+
});
|
|
18
|
+
const run = (command, args) => execute(command, args, "utf8").trim();
|
|
19
|
+
const git = (...args) => run("git", args);
|
|
20
|
+
const write = (file, value) => {
|
|
21
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
22
|
+
fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function verificationIdentity({
|
|
26
|
+
env = process.env,
|
|
27
|
+
command = run,
|
|
28
|
+
revision = "HEAD",
|
|
29
|
+
} = {}) {
|
|
30
|
+
const sourceSha = command("git", ["rev-parse", revision]);
|
|
31
|
+
const sourceTree = command("git", ["rev-parse", `${revision}^{tree}`]);
|
|
32
|
+
const bytes = (file) => execute("git", ["show", `${sourceSha}:${file}`]);
|
|
33
|
+
const named = (files) =>
|
|
34
|
+
hash(JSON.stringify(files.map((file) => [file, hash(bytes(file))])));
|
|
35
|
+
for (const key of ["GITHUB_REPOSITORY", "ImageOS", "ImageVersion"])
|
|
36
|
+
if (!env[key]) throw new Error(`verification identity requires ${key}`);
|
|
37
|
+
return {
|
|
38
|
+
repository: env.GITHUB_REPOSITORY,
|
|
39
|
+
sourceSha,
|
|
40
|
+
sourceTree,
|
|
41
|
+
workflowRoot: hash(bytes(workflow)),
|
|
42
|
+
checkDefinitionRoot: named([
|
|
43
|
+
"package.json",
|
|
44
|
+
".buildchain/buildchain.toml",
|
|
45
|
+
"scripts/source-verification-evidence.mjs",
|
|
46
|
+
"scripts/verify-version-state-delta.mjs",
|
|
47
|
+
]),
|
|
48
|
+
runtimeRoot: named([
|
|
49
|
+
"packages/core/buildchain-v4-domain.wasm",
|
|
50
|
+
"packages/core/v4-domain-wasm-artifact.js",
|
|
51
|
+
]),
|
|
52
|
+
dependencyRoot: named([
|
|
53
|
+
"pnpm-lock.yaml",
|
|
54
|
+
"pnpm-workspace.yaml",
|
|
55
|
+
"crates/buildchain-v4-contracts/Cargo.lock",
|
|
56
|
+
"crates/buildchain-v4-bridge/Cargo.lock",
|
|
57
|
+
]),
|
|
58
|
+
toolchainRoot: hash(
|
|
59
|
+
JSON.stringify([
|
|
60
|
+
process.version,
|
|
61
|
+
command("corepack", ["pnpm", "--version"]),
|
|
62
|
+
command("go", ["version"]),
|
|
63
|
+
command("rustc", ["+1.96.0", "--version"]),
|
|
64
|
+
command("cargo", ["+1.96.0", "--version"]),
|
|
65
|
+
command("rustc", ["--version"]),
|
|
66
|
+
command("cargo", ["--version"]),
|
|
67
|
+
]),
|
|
68
|
+
),
|
|
69
|
+
environmentRoot: hash(
|
|
70
|
+
JSON.stringify([
|
|
71
|
+
env.ImageOS,
|
|
72
|
+
env.ImageVersion,
|
|
73
|
+
process.platform,
|
|
74
|
+
process.arch,
|
|
75
|
+
...[
|
|
76
|
+
"CI",
|
|
77
|
+
"NODE_ENV",
|
|
78
|
+
"TZ",
|
|
79
|
+
"LANG",
|
|
80
|
+
"LC_ALL",
|
|
81
|
+
"SOURCE_DATE_EPOCH",
|
|
82
|
+
"BUILDCHAIN_SITE_GENERATED_AT",
|
|
83
|
+
"BUILDCHAIN_SITE_PUBLISHED_AT",
|
|
84
|
+
"BUILDCHAIN_SITE_TIMESTAMP_POLICY",
|
|
85
|
+
"BUILDCHAIN_SOURCE_SHA",
|
|
86
|
+
"RUSTUP_TOOLCHAIN",
|
|
87
|
+
"RUSTFLAGS",
|
|
88
|
+
"CARGO_ENCODED_RUSTFLAGS",
|
|
89
|
+
"NODE_OPTIONS",
|
|
90
|
+
"GOFLAGS",
|
|
91
|
+
"GOTOOLCHAIN",
|
|
92
|
+
].map((key) => env[key] || ""),
|
|
93
|
+
]),
|
|
94
|
+
),
|
|
95
|
+
platform:
|
|
96
|
+
{ linux: "linux", darwin: "macos", win32: "windows" }[process.platform] +
|
|
97
|
+
`-${process.arch}`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function planVerification(input) {
|
|
102
|
+
return invokeV4DomainWasm("source-verification-plan", input);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function sealVerification(input) {
|
|
106
|
+
return invokeV4DomainWasm("source-verification-seal", input);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const api = (endpoint, binary = false) =>
|
|
110
|
+
execute("gh", ["api", endpoint], binary ? undefined : "utf8");
|
|
111
|
+
|
|
112
|
+
// Only the one bounded proof file is read; archive paths are never extracted.
|
|
113
|
+
export function readProofArchive(archive, digest) {
|
|
114
|
+
if (hash(archive) !== digest)
|
|
115
|
+
throw new Error("artifact archive digest mismatch");
|
|
116
|
+
const source = [
|
|
117
|
+
"import io,json,sys,zipfile",
|
|
118
|
+
"z=zipfile.ZipFile(io.BytesIO(sys.stdin.buffer.read()))",
|
|
119
|
+
"entries=[e for e in z.infolist() if e.filename=='evidence.json']",
|
|
120
|
+
"assert len(entries)==1 and entries[0].file_size <= 1048576, 'invalid proof archive'",
|
|
121
|
+
"sys.stdout.buffer.write(z.read(entries[0]))",
|
|
122
|
+
].join("\n");
|
|
123
|
+
return JSON.parse(
|
|
124
|
+
execFileSync(
|
|
125
|
+
process.platform === "win32" ? "python" : "python3",
|
|
126
|
+
["-c", source],
|
|
127
|
+
{
|
|
128
|
+
input: archive,
|
|
129
|
+
maxBuffer: 1048576,
|
|
130
|
+
encoding: "utf8",
|
|
131
|
+
},
|
|
132
|
+
),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function discoverVerification({
|
|
137
|
+
expected,
|
|
138
|
+
evaluatedAtMs,
|
|
139
|
+
fetchJson,
|
|
140
|
+
fetchArchive,
|
|
141
|
+
}) {
|
|
142
|
+
const prefix = `repos/${expected.repository}/actions`;
|
|
143
|
+
const response = await fetchJson(
|
|
144
|
+
`${prefix}/workflows/self-build-verify.yml/runs?event=merge_group&status=success&head_sha=${expected.sourceSha}&per_page=10`,
|
|
145
|
+
);
|
|
146
|
+
const reasons = [];
|
|
147
|
+
for (const listed of response.workflow_runs || []) {
|
|
148
|
+
try {
|
|
149
|
+
const run = await fetchJson(`${prefix}/runs/${listed.id}`);
|
|
150
|
+
if (
|
|
151
|
+
run.repository?.full_name !== expected.repository ||
|
|
152
|
+
run.head_sha !== expected.sourceSha ||
|
|
153
|
+
run.path !== workflow ||
|
|
154
|
+
run.event !== "merge_group" ||
|
|
155
|
+
run.conclusion !== "success" ||
|
|
156
|
+
run.status !== "completed"
|
|
157
|
+
)
|
|
158
|
+
throw new Error("untrusted evidence run");
|
|
159
|
+
const assets = await fetchJson(
|
|
160
|
+
`${prefix}/runs/${run.id}/artifacts?per_page=100`,
|
|
161
|
+
);
|
|
162
|
+
const matches = (assets.artifacts || []).filter(
|
|
163
|
+
(a) =>
|
|
164
|
+
a.name === `source-verification-${run.head_sha}-${run.run_attempt}`,
|
|
165
|
+
);
|
|
166
|
+
if (
|
|
167
|
+
matches.length !== 1 ||
|
|
168
|
+
matches[0].expired ||
|
|
169
|
+
matches[0].size_in_bytes > 1048576
|
|
170
|
+
)
|
|
171
|
+
throw new Error("missing or expired proof artifact");
|
|
172
|
+
const asset = matches[0];
|
|
173
|
+
const candidate = readProofArchive(
|
|
174
|
+
await fetchArchive(`${prefix}/artifacts/${asset.id}/zip`),
|
|
175
|
+
asset.digest,
|
|
176
|
+
);
|
|
177
|
+
const decision = planVerification({
|
|
178
|
+
expected,
|
|
179
|
+
evaluatedAtMs,
|
|
180
|
+
candidate,
|
|
181
|
+
provider: {
|
|
182
|
+
repository: run.repository.full_name,
|
|
183
|
+
runId: String(run.id),
|
|
184
|
+
runAttempt: run.run_attempt,
|
|
185
|
+
headSha: run.head_sha,
|
|
186
|
+
event: run.event,
|
|
187
|
+
status: run.status,
|
|
188
|
+
conclusion: run.conclusion,
|
|
189
|
+
workflowPath: run.path,
|
|
190
|
+
artifactDigest: asset.digest,
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
if (decision.decision === "reuse")
|
|
194
|
+
return { ...decision, sourceSha: expected.sourceSha };
|
|
195
|
+
reasons.push(decision.reason);
|
|
196
|
+
} catch {
|
|
197
|
+
// Provider/transport or evidence failures are cache misses, never a green check.
|
|
198
|
+
reasons.push("unavailable-or-invalid-evidence");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
decision: "execute",
|
|
203
|
+
reason: reasons.join(",") || "missing-evidence",
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function output(decision) {
|
|
208
|
+
write(".buildchain/source-verification/decision.json", decision);
|
|
209
|
+
if (process.env.GITHUB_OUTPUT)
|
|
210
|
+
fs.appendFileSync(
|
|
211
|
+
process.env.GITHUB_OUTPUT,
|
|
212
|
+
`decision=${decision.decision}\n`,
|
|
213
|
+
);
|
|
214
|
+
if (process.env.GITHUB_STEP_SUMMARY)
|
|
215
|
+
fs.appendFileSync(
|
|
216
|
+
process.env.GITHUB_STEP_SUMMARY,
|
|
217
|
+
`Source verification: **${decision.decision}** (${decision.reason}).${decision.runId ? ` Original full execution: run ${decision.runId}, attempt ${decision.runAttempt}, proof ${decision.evidenceRoot}.` : ""}\n`,
|
|
218
|
+
);
|
|
219
|
+
console.log(JSON.stringify(decision));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function providerDiscovery(expected) {
|
|
223
|
+
return discoverVerification({
|
|
224
|
+
expected,
|
|
225
|
+
evaluatedAtMs: Date.now(),
|
|
226
|
+
fetchJson: async (endpoint) => JSON.parse(api(endpoint)),
|
|
227
|
+
fetchArchive: async (endpoint) => api(endpoint, true),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export async function planVersionProjection({
|
|
232
|
+
baseSha,
|
|
233
|
+
headSha,
|
|
234
|
+
discover,
|
|
235
|
+
regenerate,
|
|
236
|
+
}) {
|
|
237
|
+
const proof = await discover(baseSha);
|
|
238
|
+
if (proof.decision !== "reuse")
|
|
239
|
+
return { decision: "execute", reason: "base-full-proof-unavailable" };
|
|
240
|
+
const projection = await regenerate(baseSha, headSha);
|
|
241
|
+
return {
|
|
242
|
+
...proof,
|
|
243
|
+
decision: "projection",
|
|
244
|
+
reason: "verified-base-and-exact-regenerated-version-delta",
|
|
245
|
+
projection,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async function planCurrentVerification() {
|
|
250
|
+
if (git("status", "--porcelain", "--untracked-files=no"))
|
|
251
|
+
throw new Error("dirty source");
|
|
252
|
+
const event = process.env.GITHUB_EVENT_NAME;
|
|
253
|
+
const headSha = git("rev-parse", "HEAD");
|
|
254
|
+
if (
|
|
255
|
+
event === "push" &&
|
|
256
|
+
process.env.GITHUB_REF?.startsWith("refs/heads/dev/")
|
|
257
|
+
) {
|
|
258
|
+
const exact = await providerDiscovery(verificationIdentity());
|
|
259
|
+
if (exact.decision === "reuse") return exact;
|
|
260
|
+
}
|
|
261
|
+
if (!["pull_request", "merge_group", "push"].includes(event))
|
|
262
|
+
return { decision: "execute", reason: "full-execution-required-for-event" };
|
|
263
|
+
const payload = JSON.parse(fs.readFileSync(process.env.GITHUB_EVENT_PATH));
|
|
264
|
+
const baseSha =
|
|
265
|
+
event === "merge_group"
|
|
266
|
+
? payload.merge_group?.base_sha
|
|
267
|
+
: event === "pull_request"
|
|
268
|
+
? payload.pull_request?.base.sha
|
|
269
|
+
: process.env.GITHUB_REF?.startsWith("refs/heads/dev/")
|
|
270
|
+
? payload.before
|
|
271
|
+
: null;
|
|
272
|
+
if (!/^[a-f0-9]{40}$/u.test(baseSha || ""))
|
|
273
|
+
throw new Error("no exact protected base");
|
|
274
|
+
return planVersionProjection({
|
|
275
|
+
baseSha,
|
|
276
|
+
headSha,
|
|
277
|
+
discover: (revision) =>
|
|
278
|
+
providerDiscovery(verificationIdentity({ revision })),
|
|
279
|
+
regenerate: async (baseSha, headSha) => {
|
|
280
|
+
const { verifyVersionStateDelta } =
|
|
281
|
+
await import("./verify-version-state-delta.mjs");
|
|
282
|
+
return verifyVersionStateDelta({
|
|
283
|
+
baseSha,
|
|
284
|
+
headSha,
|
|
285
|
+
nodeModules: path.resolve(".buildchain/runtime/node_modules"),
|
|
286
|
+
});
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export async function main(mode) {
|
|
292
|
+
if (mode === "plan") {
|
|
293
|
+
try {
|
|
294
|
+
output(await planCurrentVerification());
|
|
295
|
+
} catch {
|
|
296
|
+
output({
|
|
297
|
+
decision: "execute",
|
|
298
|
+
reason: "identity-provider-or-version-delta-unavailable",
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
} else if (mode === "start") {
|
|
302
|
+
write(".buildchain/source-verification/start.json", {
|
|
303
|
+
identity: verificationIdentity(),
|
|
304
|
+
startedAtMs: Date.now(),
|
|
305
|
+
});
|
|
306
|
+
} else if (mode === "seal") {
|
|
307
|
+
if (process.env.GITHUB_EVENT_NAME !== "merge_group")
|
|
308
|
+
throw new Error(
|
|
309
|
+
"only merge-group full executions produce reusable proof",
|
|
310
|
+
);
|
|
311
|
+
const started = JSON.parse(
|
|
312
|
+
fs.readFileSync(".buildchain/source-verification/start.json"),
|
|
313
|
+
);
|
|
314
|
+
if (
|
|
315
|
+
git("status", "--porcelain", "--untracked-files=no") ||
|
|
316
|
+
JSON.stringify(started.identity) !==
|
|
317
|
+
JSON.stringify(verificationIdentity())
|
|
318
|
+
)
|
|
319
|
+
throw new Error(
|
|
320
|
+
"source or verification identity changed during execution",
|
|
321
|
+
);
|
|
322
|
+
const completedAtMs = Date.now();
|
|
323
|
+
write(
|
|
324
|
+
proofPath,
|
|
325
|
+
sealVerification({
|
|
326
|
+
schema: "buildchain-v4-source-verification-evidence/v1",
|
|
327
|
+
...started,
|
|
328
|
+
runId: process.env.GITHUB_RUN_ID,
|
|
329
|
+
runAttempt: Number(process.env.GITHUB_RUN_ATTEMPT),
|
|
330
|
+
event: process.env.GITHUB_EVENT_NAME,
|
|
331
|
+
validationKind: "full-source",
|
|
332
|
+
exitCode: 0,
|
|
333
|
+
completedAtMs,
|
|
334
|
+
expiresAtMs: completedAtMs + 6 * 60 * 60 * 1000,
|
|
335
|
+
}),
|
|
336
|
+
);
|
|
337
|
+
} else throw new Error(`unknown verification evidence mode: ${mode}`);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
|
|
341
|
+
main(process.argv[2]).catch((error) => {
|
|
342
|
+
console.error(error.message);
|
|
343
|
+
process.exitCode = 1;
|
|
344
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const DEFAULTS = {
|
|
4
|
-
buildWorkflowFile: "build-
|
|
4
|
+
buildWorkflowFile: "self-build-fixture.yml",
|
|
5
5
|
buildWorkflowName: "Build Surface Fixture",
|
|
6
6
|
canaryRepository: "kungfu-systems/site-libkungfu-dev",
|
|
7
7
|
canaryWorkflowFile: "buildchain-stable-canary.yml",
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
function loadUniversalFacadeMigration(root) {
|
|
6
|
+
const contract = JSON.parse(
|
|
7
|
+
fs.readFileSync(
|
|
8
|
+
path.join(root, "architecture/v4-universal-workflow-bootstrap.json"),
|
|
9
|
+
"utf8",
|
|
10
|
+
),
|
|
11
|
+
);
|
|
12
|
+
const retired = new Set(contract.retiredWorkflowSurfaces || []);
|
|
13
|
+
const paths = (contract.bootstrapGovernedWorkflows || []).filter(
|
|
14
|
+
(entry) =>
|
|
15
|
+
entry !== contract.bootstrap?.publicWorkflow && !retired.has(entry),
|
|
16
|
+
);
|
|
17
|
+
const sourceRevision = contract.migration?.facadeSourceRevision || "";
|
|
18
|
+
if (!/^[0-9a-f]{40}$/u.test(sourceRevision)) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
"universal facade migration requires an exact facadeSourceRevision",
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return { paths: new Set(paths), sourceRevision };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function verifyUniversalFacadeMigration(root) {
|
|
27
|
+
execFileSync(
|
|
28
|
+
process.execPath,
|
|
29
|
+
["scripts/generate-v4-universal-workflow-facades.mjs", "--check"],
|
|
30
|
+
{ cwd: root, stdio: "pipe" },
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function governUniversalFacadeWorkflowMetrics({
|
|
35
|
+
root,
|
|
36
|
+
current,
|
|
37
|
+
workflowMetricsAtRevision,
|
|
38
|
+
}) {
|
|
39
|
+
const migration = loadUniversalFacadeMigration(root);
|
|
40
|
+
verifyUniversalFacadeMigration(root);
|
|
41
|
+
const frozen = workflowMetricsAtRevision(root, migration.sourceRevision);
|
|
42
|
+
const governed = { ...current, workflows: { ...current.workflows } };
|
|
43
|
+
for (const file of migration.paths) {
|
|
44
|
+
if (!frozen[file])
|
|
45
|
+
throw new Error(`frozen universal facade source is missing ${file}`);
|
|
46
|
+
governed.workflows[file] = frozen[file];
|
|
47
|
+
}
|
|
48
|
+
return { governed, migration };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isGeneratedFacadePublicTransition({
|
|
52
|
+
entry,
|
|
53
|
+
kind,
|
|
54
|
+
migration,
|
|
55
|
+
baseContract,
|
|
56
|
+
currentContract,
|
|
57
|
+
}) {
|
|
58
|
+
if (kind !== "workflow" || !migration?.paths.has(entry.path) || !baseContract)
|
|
59
|
+
return false;
|
|
60
|
+
const { inputs: currentInputs, ...currentRest } = currentContract;
|
|
61
|
+
const { inputs: previousInputs, ...previousRest } = baseContract;
|
|
62
|
+
if (JSON.stringify(currentRest) !== JSON.stringify(previousRest))
|
|
63
|
+
return false;
|
|
64
|
+
const added = currentInputs.filter(
|
|
65
|
+
(input) => !previousInputs.includes(input),
|
|
66
|
+
);
|
|
67
|
+
const removed = previousInputs.filter(
|
|
68
|
+
(input) => !currentInputs.includes(input),
|
|
69
|
+
);
|
|
70
|
+
return (
|
|
71
|
+
removed.length === 0 &&
|
|
72
|
+
added.length === 1 &&
|
|
73
|
+
added[0] === "universal-request-json"
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
governUniversalFacadeWorkflowMetrics,
|
|
79
|
+
isGeneratedFacadePublicTransition,
|
|
80
|
+
loadUniversalFacadeMigration,
|
|
81
|
+
verifyUniversalFacadeMigration,
|
|
82
|
+
};
|
|
@@ -18,7 +18,8 @@ function readJson(file) {
|
|
|
18
18
|
return JSON.parse(fs.readFileSync(path.resolve(file), "utf8"));
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function packageName() {
|
|
21
|
+
function packageName(artifactKind) {
|
|
22
|
+
if (artifactKind !== "npm") return "";
|
|
22
23
|
const declared = env("BUILDCHAIN_PUBLISH_PACKAGE_MAIN");
|
|
23
24
|
if (declared) return declared;
|
|
24
25
|
const manifestPath = env("BUILDCHAIN_SEALED_BUNDLE_MANIFEST", true);
|
|
@@ -59,9 +60,14 @@ function writeOutput(name, value) {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
export function resolveV4ProductPublicationIntent() {
|
|
62
|
-
const
|
|
63
|
+
const artifactKind = env("BUILDCHAIN_PUBLISH_ARTIFACT_KIND") || "npm";
|
|
64
|
+
const name = packageName(artifactKind);
|
|
63
65
|
const sourceSha = env("BUILDCHAIN_SOURCE_SHA", true);
|
|
64
|
-
const
|
|
66
|
+
const manifestPath = env(
|
|
67
|
+
"BUILDCHAIN_SEALED_BUNDLE_MANIFEST",
|
|
68
|
+
artifactKind === "npm",
|
|
69
|
+
);
|
|
70
|
+
const manifest = manifestPath ? readJson(manifestPath) : null;
|
|
65
71
|
const requiredArtifacts = readJson(
|
|
66
72
|
env("BUILDCHAIN_REQUIRED_ARTIFACTS_PATH", true),
|
|
67
73
|
);
|
|
@@ -72,18 +78,19 @@ export function resolveV4ProductPublicationIntent() {
|
|
|
72
78
|
sourceSha,
|
|
73
79
|
sourceTimestamp: env("BUILDCHAIN_SOURCE_TIMESTAMP", true),
|
|
74
80
|
repository: env("BUILDCHAIN_REPOSITORY", true),
|
|
81
|
+
artifactKind,
|
|
75
82
|
packageName: name,
|
|
76
83
|
distTag:
|
|
77
84
|
env("BUILDCHAIN_PUBLISH_DIST_TAG") ||
|
|
78
85
|
(channel === "alpha" ? "alpha" : "latest"),
|
|
79
|
-
sealedBundleRoot: manifest
|
|
86
|
+
sealedBundleRoot: manifest?.root,
|
|
80
87
|
requiredArtifactsRoot: v4ContentRoot(
|
|
81
88
|
"v4-product-required-artifacts",
|
|
82
89
|
requiredArtifacts,
|
|
83
90
|
),
|
|
84
91
|
candidateVersion: env("BUILDCHAIN_CANDIDATE_VERSION", true),
|
|
85
92
|
recoveredVersion: env("BUILDCHAIN_RECOVERED_PUBLICATION_VERSION"),
|
|
86
|
-
observedVersions: observedVersions(name),
|
|
93
|
+
observedVersions: artifactKind === "npm" ? observedVersions(name) : [],
|
|
87
94
|
});
|
|
88
95
|
const outputPath = path.resolve(
|
|
89
96
|
env("BUILDCHAIN_PRODUCT_PUBLICATION_INTENT_PATH") ||
|
|
@@ -95,7 +102,7 @@ export function resolveV4ProductPublicationIntent() {
|
|
|
95
102
|
writeOutput("exact-tag", intent.exactTag);
|
|
96
103
|
writeOutput("intent-path", outputPath);
|
|
97
104
|
writeOutput("intent-root", intent.intentRoot);
|
|
98
|
-
return { name, intent, outputPath };
|
|
105
|
+
return { name: name || artifactKind, intent, outputPath };
|
|
99
106
|
}
|
|
100
107
|
|
|
101
108
|
if (
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import {
|
|
5
|
+
createV4ReleaseInvocation,
|
|
6
|
+
createV4ReleaseReceipt,
|
|
7
|
+
createV4ReleaseTransaction,
|
|
8
|
+
} from "../packages/core/v4-release-invocation.js";
|
|
9
|
+
import {
|
|
10
|
+
releaseTailRoot,
|
|
11
|
+
validateReleaseTailTransaction,
|
|
12
|
+
} from "../packages/core/release-tail-provider-plane.js";
|
|
13
|
+
|
|
14
|
+
export const SETTLEMENT_ASSET = "buildchain-publication-settlement.json";
|
|
15
|
+
|
|
16
|
+
export function verifyPublicationSettlement(
|
|
17
|
+
documents,
|
|
18
|
+
{ repository, tag, sourceSha, publicPassport, candidateSha } = {},
|
|
19
|
+
) {
|
|
20
|
+
const {
|
|
21
|
+
invocation,
|
|
22
|
+
transaction,
|
|
23
|
+
receipt,
|
|
24
|
+
passport,
|
|
25
|
+
product,
|
|
26
|
+
providerState,
|
|
27
|
+
productState,
|
|
28
|
+
} = documents;
|
|
29
|
+
const projected = createV4ReleaseInvocation(invocation);
|
|
30
|
+
const roots = createV4ReleaseTransaction({
|
|
31
|
+
invocationRoot: projected.roots.invocationRoot,
|
|
32
|
+
publisherRoot: projected.roots.publisherRoot,
|
|
33
|
+
runtimeRoot: projected.roots.runtimeRoot,
|
|
34
|
+
providerRoot: projected.roots.providerRoot,
|
|
35
|
+
parentRoot: projected.roots.parentRoot,
|
|
36
|
+
});
|
|
37
|
+
const { receiptRoot, ...receiptBody } = receipt;
|
|
38
|
+
const { passportRoot, ...passportBody } = passport;
|
|
39
|
+
const { root: productRoot, ...productBody } = product;
|
|
40
|
+
if (
|
|
41
|
+
!validateReleaseTailTransaction(providerState).valid ||
|
|
42
|
+
!validateReleaseTailTransaction(productState).valid
|
|
43
|
+
)
|
|
44
|
+
throw new Error("invalid provider transaction state");
|
|
45
|
+
if (
|
|
46
|
+
releaseTailRoot(transaction) !==
|
|
47
|
+
releaseTailRoot({
|
|
48
|
+
...roots.transaction,
|
|
49
|
+
transactionRoot: roots.transactionRoot,
|
|
50
|
+
}) ||
|
|
51
|
+
createV4ReleaseReceipt(receiptBody).receiptRoot !== receiptRoot ||
|
|
52
|
+
receipt.transactionRoot !== roots.transactionRoot ||
|
|
53
|
+
receipt.outcome !== "complete" ||
|
|
54
|
+
passportRoot !== releaseTailRoot(passportBody) ||
|
|
55
|
+
receipt.releasePassportRoot !== passportRoot ||
|
|
56
|
+
productRoot !== releaseTailRoot(productBody) ||
|
|
57
|
+
!receipt.providerReceiptRoots.includes(productRoot) ||
|
|
58
|
+
receipt.providerTransactionRoot !== providerState.transactionRoot ||
|
|
59
|
+
receipt.providerStateRoot !== providerState.stateRoot ||
|
|
60
|
+
providerState.state !== "complete" ||
|
|
61
|
+
productState.state !== "complete" ||
|
|
62
|
+
product.transaction.stateRoot !== productState.stateRoot ||
|
|
63
|
+
product.transaction.transactionRoot !== productState.transactionRoot ||
|
|
64
|
+
productState.transactionRoot !== roots.transactionRoot ||
|
|
65
|
+
providerState.receipts.some(
|
|
66
|
+
({ receiptRoot: root }) => !receipt.providerReceiptRoots.includes(root),
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
throw new Error("terminal publication receipt lineage does not verify");
|
|
70
|
+
verifyPublicationCoordinates(documents, {
|
|
71
|
+
repository,
|
|
72
|
+
tag,
|
|
73
|
+
sourceSha,
|
|
74
|
+
publicPassport,
|
|
75
|
+
candidateSha,
|
|
76
|
+
});
|
|
77
|
+
return { publication: "complete", receiptRoot, sourceSha, tag, repository };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function verifyPublicationCoordinates(
|
|
81
|
+
{ invocation, passport, product },
|
|
82
|
+
{ repository, tag, sourceSha, publicPassport, candidateSha },
|
|
83
|
+
) {
|
|
84
|
+
if (
|
|
85
|
+
invocation.candidate.repository !== repository ||
|
|
86
|
+
(candidateSha && invocation.candidate.commit !== candidateSha) ||
|
|
87
|
+
passport.repository !== repository ||
|
|
88
|
+
invocation.provider.repository !== repository ||
|
|
89
|
+
invocation.target.tag !== tag ||
|
|
90
|
+
passport.release.tag !== tag ||
|
|
91
|
+
passport.release.version !== tag.replace(/^v/u, "") ||
|
|
92
|
+
product.publication.exactTag !== tag ||
|
|
93
|
+
product.publication.version !== passport.release.version ||
|
|
94
|
+
product.publication.releaseSha !== sourceSha ||
|
|
95
|
+
product.publication.state !== "complete" ||
|
|
96
|
+
product.publication.finalizationNeeded ||
|
|
97
|
+
passport.source.headSha !== invocation.candidate.commit ||
|
|
98
|
+
passport.source.treeHash !== invocation.candidate.tree ||
|
|
99
|
+
passport.policyDigest !== invocation.authority.policyRoot ||
|
|
100
|
+
passport.publicationQualificationRoot !==
|
|
101
|
+
invocation.authority.qualificationRoot ||
|
|
102
|
+
(publicPassport &&
|
|
103
|
+
releaseTailRoot(publicPassport) !== releaseTailRoot(passport))
|
|
104
|
+
)
|
|
105
|
+
throw new Error(
|
|
106
|
+
"publication evidence does not match exact provider release coordinates",
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function readApplyDocuments(base) {
|
|
111
|
+
const read = (relative) =>
|
|
112
|
+
JSON.parse(fs.readFileSync(path.join(base, relative)));
|
|
113
|
+
return {
|
|
114
|
+
invocation: read("release-tail/release-invocation.json"),
|
|
115
|
+
transaction: read("release-tail/release-transaction.json"),
|
|
116
|
+
receipt: read("release-tail/release-receipt.json"),
|
|
117
|
+
product: read("release-tail/product-provider-result.json"),
|
|
118
|
+
providerState: read("release-tail/state.json"),
|
|
119
|
+
productState: read("release-tail/product-provider-transaction.json"),
|
|
120
|
+
passport: read("release-passport/buildchain.release.json"),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function settlePublication({
|
|
125
|
+
base,
|
|
126
|
+
client,
|
|
127
|
+
applyOutcome,
|
|
128
|
+
repository,
|
|
129
|
+
candidateSha,
|
|
130
|
+
}) {
|
|
131
|
+
const documents = readApplyDocuments(base);
|
|
132
|
+
if (
|
|
133
|
+
documents.invocation.candidate.repository !== repository ||
|
|
134
|
+
(candidateSha && documents.invocation.candidate.commit !== candidateSha)
|
|
135
|
+
)
|
|
136
|
+
throw new Error(
|
|
137
|
+
"publication settlement does not match the admitted repository and candidate",
|
|
138
|
+
);
|
|
139
|
+
const tag = documents.invocation.target.tag;
|
|
140
|
+
const release = client.release(tag);
|
|
141
|
+
const sourceSha = client.json(
|
|
142
|
+
`repos/${repository}/commits/${encodeURIComponent(tag)}`,
|
|
143
|
+
).sha;
|
|
144
|
+
const matches = release.assets.filter(
|
|
145
|
+
({ name }) => name === "buildchain.release.json",
|
|
146
|
+
);
|
|
147
|
+
if (matches.length !== 1)
|
|
148
|
+
throw new Error(
|
|
149
|
+
"exact public publication passport is missing or ambiguous",
|
|
150
|
+
);
|
|
151
|
+
const publicPassport = JSON.parse(client.assetBytes(matches[0]));
|
|
152
|
+
const status = verifyPublicationSettlement(documents, {
|
|
153
|
+
repository,
|
|
154
|
+
tag,
|
|
155
|
+
sourceSha,
|
|
156
|
+
publicPassport,
|
|
157
|
+
});
|
|
158
|
+
// Publication completion is immutable; next-development and distribution progress stay separate.
|
|
159
|
+
const settlement = {
|
|
160
|
+
schemaVersion: 1,
|
|
161
|
+
contract: "buildchain-v4-publication-settlement/v1",
|
|
162
|
+
id: "v4-publication",
|
|
163
|
+
release: { sourceSha, tag, channel: documents.invocation.target.channel },
|
|
164
|
+
documents,
|
|
165
|
+
};
|
|
166
|
+
const file = path.join(base, "release-tail", SETTLEMENT_ASSET);
|
|
167
|
+
client.write(file, settlement);
|
|
168
|
+
await client.publish(release, [file]);
|
|
169
|
+
const summary = {
|
|
170
|
+
...status,
|
|
171
|
+
nextDevelopment: applyOutcome === "success" ? "advanced" : "incomplete",
|
|
172
|
+
binaryDistribution: "pending-provider-readback",
|
|
173
|
+
};
|
|
174
|
+
client.write(
|
|
175
|
+
path.join(base, "release-tail", "delivery-summary.json"),
|
|
176
|
+
summary,
|
|
177
|
+
);
|
|
178
|
+
return { summary, receipt: documents.receipt };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function main() {
|
|
182
|
+
const { releaseAssetClient } = await import("./release-asset-client.mjs");
|
|
183
|
+
const repository = process.env.GITHUB_REPOSITORY;
|
|
184
|
+
const result = await settlePublication({
|
|
185
|
+
base: process.argv[2],
|
|
186
|
+
client: releaseAssetClient(repository),
|
|
187
|
+
repository,
|
|
188
|
+
candidateSha: process.env.CANDIDATE_SHA,
|
|
189
|
+
applyOutcome: process.env.APPLY_OUTCOME,
|
|
190
|
+
});
|
|
191
|
+
if (process.env.GITHUB_OUTPUT)
|
|
192
|
+
fs.appendFileSync(
|
|
193
|
+
process.env.GITHUB_OUTPUT,
|
|
194
|
+
`receipt-json=${JSON.stringify(result.receipt)}\nreceipt-root=${result.receipt.receiptRoot}\nstatus=complete\n`,
|
|
195
|
+
);
|
|
196
|
+
if (process.env.GITHUB_STEP_SUMMARY)
|
|
197
|
+
fs.appendFileSync(
|
|
198
|
+
process.env.GITHUB_STEP_SUMMARY,
|
|
199
|
+
`Publication: **complete**, receipt \`${result.receipt.receiptRoot}\`. Next development: **${result.summary.nextDevelopment}**. Binary distribution: **pending provider readback**.\n`,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (
|
|
204
|
+
process.argv[1] &&
|
|
205
|
+
path.basename(process.argv[1]) === "v4-publication-settlement.mjs" &&
|
|
206
|
+
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
207
|
+
)
|
|
208
|
+
main().catch((error) => {
|
|
209
|
+
console.error(error.message);
|
|
210
|
+
process.exitCode = 1;
|
|
211
|
+
});
|