@kungfu-tech/buildchain 2.10.9 → 2.10.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/buildchain.mjs +11 -1
- package/dist/site/buildchain-contract.json +6 -6
- package/dist/site/buildchain-site.json +79 -16
- package/dist/site/capability-registry.json +5 -5
- package/dist/site/cli-registry.json +25 -1
- package/dist/site/kfd-claims.json +114 -13
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +3 -3
- package/dist/site/node-api-registry.json +17 -4
- package/dist/site/page-registry.json +72 -9
- package/dist/site/public-surface-audit.json +63 -9
- package/dist/site/release-provenance.json +1 -0
- package/dist/site/site-manifest.json +7 -7
- package/dist/site/workflow-registry.json +25 -1
- package/docs/MAP.md +6 -1
- package/docs/cli.md +19 -2
- package/docs/publication-artifacts.md +126 -0
- package/docs/web-surface-deployments.md +8 -2
- package/fixtures/publication-artifact-shaped/README.md +11 -0
- package/package.json +2 -1
- package/packages/core/README.md +11 -0
- package/packages/core/buildchain-config.js +33 -2
- package/packages/core/index.js +8 -0
- package/packages/core/public-surface-audit.js +4 -0
- package/packages/core/publication-artifact.js +245 -0
- package/scripts/generate-site-bundle.mjs +5 -1
- package/scripts/init-repo.mjs +80 -6
- package/scripts/publication-artifact.mjs +45 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { loadBuildchainConfig } from "./buildchain-config.js";
|
|
6
|
+
|
|
7
|
+
export const PUBLICATION_ARTIFACT_MANIFEST_CONTRACT = "kungfu-buildchain-publication-artifact-manifest";
|
|
8
|
+
export const PUBLICATION_ARTIFACT_PASSPORT_CONTRACT = "kungfu-buildchain-publication-artifact-passport";
|
|
9
|
+
|
|
10
|
+
function toPosix(value) {
|
|
11
|
+
return String(value || "").split(path.sep).join("/");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function sha256Buffer(buffer) {
|
|
15
|
+
return crypto.createHash("sha256").update(buffer).digest("hex");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function sha256FilePath(filePath) {
|
|
19
|
+
return sha256Buffer(fs.readFileSync(filePath));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function repoRelative(cwd, filePath) {
|
|
23
|
+
const relative = path.relative(cwd, filePath);
|
|
24
|
+
return relative && !relative.startsWith("..") && !path.isAbsolute(relative)
|
|
25
|
+
? toPosix(relative)
|
|
26
|
+
: toPosix(path.resolve(filePath));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function listFiles(cwd, relPath) {
|
|
30
|
+
const target = path.resolve(cwd, relPath);
|
|
31
|
+
if (!fs.existsSync(target)) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const stat = fs.statSync(target);
|
|
35
|
+
if (stat.isFile()) {
|
|
36
|
+
return [target];
|
|
37
|
+
}
|
|
38
|
+
if (!stat.isDirectory()) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
return fs
|
|
42
|
+
.readdirSync(target, { withFileTypes: true })
|
|
43
|
+
.flatMap((entry) => listFiles(cwd, path.join(repoRelative(cwd, target), entry.name)));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function collectFiles(cwd, paths = []) {
|
|
47
|
+
const files = new Map();
|
|
48
|
+
for (const relPath of paths) {
|
|
49
|
+
for (const filePath of listFiles(cwd, relPath)) {
|
|
50
|
+
const relative = repoRelative(cwd, filePath);
|
|
51
|
+
files.set(relative, {
|
|
52
|
+
path: relative,
|
|
53
|
+
bytes: fs.statSync(filePath).size,
|
|
54
|
+
sha256: sha256FilePath(filePath),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return [...files.values()].sort((left, right) => left.path.localeCompare(right.path));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function gitValue(cwd, args, fallback = "") {
|
|
62
|
+
try {
|
|
63
|
+
return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
64
|
+
} catch {
|
|
65
|
+
return fallback;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function createdAt(now = new Date()) {
|
|
70
|
+
const sourceDateEpoch = process.env.SOURCE_DATE_EPOCH || "";
|
|
71
|
+
if (/^\d+$/.test(sourceDateEpoch)) {
|
|
72
|
+
return new Date(Number(sourceDateEpoch) * 1000).toISOString();
|
|
73
|
+
}
|
|
74
|
+
return now.toISOString();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function createPublicationSourceBundle({
|
|
78
|
+
cwd = process.cwd(),
|
|
79
|
+
sourcePaths = [],
|
|
80
|
+
output = ".buildchain/publication/source.tar.gz",
|
|
81
|
+
} = {}) {
|
|
82
|
+
const outputPath = path.resolve(cwd, output);
|
|
83
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
84
|
+
const paths = sourcePaths.length > 0 ? sourcePaths : ["."];
|
|
85
|
+
execFileSync("git", [
|
|
86
|
+
"archive",
|
|
87
|
+
"--format=tar.gz",
|
|
88
|
+
`--output=${outputPath}`,
|
|
89
|
+
"HEAD",
|
|
90
|
+
...paths,
|
|
91
|
+
], { cwd, stdio: "pipe" });
|
|
92
|
+
return {
|
|
93
|
+
path: toPosix(path.relative(cwd, outputPath)),
|
|
94
|
+
bytes: fs.statSync(outputPath).size,
|
|
95
|
+
sha256: sha256FilePath(outputPath),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function collectPublicationArtifact({
|
|
100
|
+
cwd = process.cwd(),
|
|
101
|
+
sourceSha = "",
|
|
102
|
+
sourceBundle = true,
|
|
103
|
+
sourceBundlePath = "",
|
|
104
|
+
generatedAt = "",
|
|
105
|
+
} = {}) {
|
|
106
|
+
const loaded = loadBuildchainConfig(cwd);
|
|
107
|
+
if (!loaded?.config?.publication) {
|
|
108
|
+
throw new Error("publication artifact manifest requires [publication] in .buildchain/buildchain.toml");
|
|
109
|
+
}
|
|
110
|
+
if (loaded.config.project?.type !== "publication-artifact") {
|
|
111
|
+
throw new Error('publication artifact manifest requires project.type = "publication-artifact"');
|
|
112
|
+
}
|
|
113
|
+
const publication = loaded.config.publication;
|
|
114
|
+
const artifactPaths = [publication.primaryArtifact, ...publication.artifactPaths]
|
|
115
|
+
.filter(Boolean)
|
|
116
|
+
.filter((value, index, list) => list.indexOf(value) === index);
|
|
117
|
+
const artifacts = collectFiles(cwd, artifactPaths);
|
|
118
|
+
if (!artifacts.find((artifact) => artifact.path === publication.primaryArtifact)) {
|
|
119
|
+
throw new Error(`publication primary artifact is missing: ${publication.primaryArtifact}`);
|
|
120
|
+
}
|
|
121
|
+
const metadata = collectFiles(cwd, publication.metadataPaths);
|
|
122
|
+
const sourceFiles = collectFiles(cwd, publication.sourcePaths);
|
|
123
|
+
const bundle = sourceBundle
|
|
124
|
+
? createPublicationSourceBundle({
|
|
125
|
+
cwd,
|
|
126
|
+
sourcePaths: publication.sourcePaths,
|
|
127
|
+
output: sourceBundlePath || publication.sourceBundlePath,
|
|
128
|
+
})
|
|
129
|
+
: undefined;
|
|
130
|
+
const resolvedSourceSha = sourceSha || gitValue(cwd, ["rev-parse", "HEAD"]);
|
|
131
|
+
const sourceTreeSha = gitValue(cwd, ["rev-parse", `${resolvedSourceSha}^{tree}`]);
|
|
132
|
+
const timestamp = generatedAt || createdAt();
|
|
133
|
+
const manifest = {
|
|
134
|
+
schemaVersion: 1,
|
|
135
|
+
contract: PUBLICATION_ARTIFACT_MANIFEST_CONTRACT,
|
|
136
|
+
project: {
|
|
137
|
+
name: loaded.config.project?.name || path.basename(path.resolve(cwd)),
|
|
138
|
+
type: "publication-artifact",
|
|
139
|
+
},
|
|
140
|
+
publication: {
|
|
141
|
+
kind: publication.kind,
|
|
142
|
+
title: publication.title,
|
|
143
|
+
version: publication.version,
|
|
144
|
+
abstract: publication.abstract,
|
|
145
|
+
authors: publication.authors,
|
|
146
|
+
primaryArtifact: publication.primaryArtifact,
|
|
147
|
+
siteConsumers: publication.siteConsumers,
|
|
148
|
+
},
|
|
149
|
+
source: {
|
|
150
|
+
repository: gitValue(cwd, ["config", "--get", "remote.origin.url"]),
|
|
151
|
+
sha: resolvedSourceSha,
|
|
152
|
+
treeSha: sourceTreeSha,
|
|
153
|
+
sourcePaths: publication.sourcePaths,
|
|
154
|
+
sourceFiles,
|
|
155
|
+
sourceBundle: bundle,
|
|
156
|
+
},
|
|
157
|
+
artifacts,
|
|
158
|
+
metadata,
|
|
159
|
+
generatedAt: timestamp,
|
|
160
|
+
publishedAt: timestamp,
|
|
161
|
+
reproducible: true,
|
|
162
|
+
timestampPolicy: "ci-injected",
|
|
163
|
+
deterministicInputs: [
|
|
164
|
+
"publication source paths",
|
|
165
|
+
"publication metadata paths",
|
|
166
|
+
"publication primary artifact",
|
|
167
|
+
"Buildchain publication contract",
|
|
168
|
+
"source SHA",
|
|
169
|
+
"source tree SHA",
|
|
170
|
+
],
|
|
171
|
+
timestampPolicyDetails: {
|
|
172
|
+
contract: "kungfu-buildchain-surface-timestamp-policy",
|
|
173
|
+
timestampFields: ["generatedAt", "publishedAt"],
|
|
174
|
+
timestampFieldsParticipateInArtifactDigest: false,
|
|
175
|
+
artifactDigestScope: "publication artifact digests cover files and source bundle, not manifest timestamps",
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
return {
|
|
179
|
+
manifest,
|
|
180
|
+
passport: {
|
|
181
|
+
schemaVersion: 1,
|
|
182
|
+
contract: PUBLICATION_ARTIFACT_PASSPORT_CONTRACT,
|
|
183
|
+
status: "passed",
|
|
184
|
+
manifestDigest: `sha256:${sha256Buffer(Buffer.from(JSON.stringify(manifest, null, 2)))}`,
|
|
185
|
+
source: manifest.source,
|
|
186
|
+
artifacts: manifest.artifacts,
|
|
187
|
+
responsibility: {
|
|
188
|
+
producer: "publication repository",
|
|
189
|
+
renderer: "site repository",
|
|
190
|
+
buildchain: "artifact contract, manifest, source bundle, and evidence generation",
|
|
191
|
+
},
|
|
192
|
+
auditBoundary: {
|
|
193
|
+
machineVerified: [
|
|
194
|
+
"declared publication artifact files exist",
|
|
195
|
+
"declared publication metadata files exist",
|
|
196
|
+
"declared publication source files are hashed",
|
|
197
|
+
"source bundle digest is recorded",
|
|
198
|
+
],
|
|
199
|
+
outsideBoundary: [
|
|
200
|
+
"paper scientific claims",
|
|
201
|
+
"bibliography quality",
|
|
202
|
+
"human review of publication content",
|
|
203
|
+
],
|
|
204
|
+
},
|
|
205
|
+
residualRisk: [
|
|
206
|
+
{
|
|
207
|
+
riskType: "natural-language-semantic-risk",
|
|
208
|
+
trustImpact: "downgrade-warning",
|
|
209
|
+
machineProvability: "not-machine-verifiable",
|
|
210
|
+
agentAction: "semantic-review-required",
|
|
211
|
+
note: "Buildchain verifies declared files and hashes; it does not peer-review paper claims.",
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function writePublicationArtifact({
|
|
219
|
+
cwd = process.cwd(),
|
|
220
|
+
output = "",
|
|
221
|
+
passportOutput = "",
|
|
222
|
+
sourceSha = "",
|
|
223
|
+
sourceBundle = true,
|
|
224
|
+
sourceBundlePath = "",
|
|
225
|
+
generatedAt = "",
|
|
226
|
+
} = {}) {
|
|
227
|
+
const collected = collectPublicationArtifact({
|
|
228
|
+
cwd,
|
|
229
|
+
sourceSha,
|
|
230
|
+
sourceBundle,
|
|
231
|
+
sourceBundlePath,
|
|
232
|
+
generatedAt,
|
|
233
|
+
});
|
|
234
|
+
const manifestOutput = output || loadBuildchainConfig(cwd).config.publication.manifestPath;
|
|
235
|
+
const passportPath = passportOutput || ".buildchain/publication/publication-artifact-passport.json";
|
|
236
|
+
fs.mkdirSync(path.dirname(path.resolve(cwd, manifestOutput)), { recursive: true });
|
|
237
|
+
fs.mkdirSync(path.dirname(path.resolve(cwd, passportPath)), { recursive: true });
|
|
238
|
+
fs.writeFileSync(path.resolve(cwd, manifestOutput), `${JSON.stringify(collected.manifest, null, 2)}\n`);
|
|
239
|
+
fs.writeFileSync(path.resolve(cwd, passportPath), `${JSON.stringify(collected.passport, null, 2)}\n`);
|
|
240
|
+
return {
|
|
241
|
+
...collected,
|
|
242
|
+
manifestPath: toPosix(manifestOutput),
|
|
243
|
+
passportPath: toPosix(passportPath),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
@@ -325,6 +325,7 @@ const manualMetaById = new Map(Object.entries({
|
|
|
325
325
|
"site-bundle-contract": { capabilityGroup: "site-and-propagation", audience: ["site", "agent"], maturity: "stable", order: 400 },
|
|
326
326
|
"web-surface-deployments": { capabilityGroup: "site-and-propagation", audience: ["site", "release-operator"], maturity: "stable", order: 410 },
|
|
327
327
|
"release-propagation": { capabilityGroup: "site-and-propagation", audience: ["release-operator", "agent"], maturity: "preview", order: 420 },
|
|
328
|
+
"publication-artifacts": { capabilityGroup: "reusable-build", audience: ["consumer", "site", "agent"], maturity: "stable", order: 430 },
|
|
328
329
|
"readme-badges": { capabilityGroup: "distribution-indexes", audience: ["consumer", "site"], maturity: "stable", order: 500 },
|
|
329
330
|
homebrew: { capabilityGroup: "distribution-indexes", audience: ["consumer", "release-operator"], maturity: "stable", order: 510 },
|
|
330
331
|
"build-facts": { capabilityGroup: "observability-diagnostics", audience: ["maintainer", "agent"], maturity: "stable", order: 600 },
|
|
@@ -427,6 +428,8 @@ function cliCommandMeta(id) {
|
|
|
427
428
|
npm: { group: "release-passport-trust", purpose: "Inspect npm publishing command families." },
|
|
428
429
|
"npm-dry-run": { group: "release-passport-trust", purpose: "Verify npm publish shape before a release transaction." },
|
|
429
430
|
"publish-source": { group: "release-passport-trust", purpose: "Create, inspect, or verify publish-gate source-lock refs." },
|
|
431
|
+
"publication-artifact": { group: "reusable-build", purpose: "Generate publication artifact manifests, passports, and source bundles for paper/report repositories." },
|
|
432
|
+
"publication-artifact-manifest": { group: "reusable-build", purpose: "Write a site-consumable publication artifact manifest, publication passport, and source bundle." },
|
|
430
433
|
"release-dry-run": { group: "governance-versioning", purpose: "Explain what a channel merge would publish before the PR is merged." },
|
|
431
434
|
"release-line-open": { group: "governance-versioning", purpose: "Plan or write the initial version-state commit for a new minor release line." },
|
|
432
435
|
"release-propagation": { group: "site-and-propagation", purpose: "Plan channel-preserving downstream release PRs and write exact upstream release locks." },
|
|
@@ -461,6 +464,7 @@ function nodeApiMeta(exportName) {
|
|
|
461
464
|
"./build-facts": { group: "observability-diagnostics", summary: "Git source, version, module output, product artifact, and legacy Kungfu build fact APIs." },
|
|
462
465
|
"./diagnostics": { group: "observability-diagnostics", summary: "Native diagnostics collection, summarization, cache, compiler, and process-sampler APIs." },
|
|
463
466
|
"./logging": { group: "observability-diagnostics", summary: "Buildchain JSONL logging, span, summary, and verification APIs." },
|
|
467
|
+
"./publication-artifact": { group: "reusable-build", summary: "Publication artifact manifest, source bundle, and publication passport APIs." },
|
|
464
468
|
"./artifact-passport": { group: "release-passport-trust", summary: "Artifact passport digest and evidence helper APIs." },
|
|
465
469
|
"./release-passport": { group: "release-passport-trust", summary: "Release passport collection, verification, explanation, and evidence APIs." },
|
|
466
470
|
"./release-candidate": { group: "reusable-build", summary: "PR-stage release-candidate artifact, passport, and promote-only resolver APIs." },
|
|
@@ -531,7 +535,7 @@ function buildCapabilityRegistry({ docs, pages, cliRegistry, manualRegistry, nod
|
|
|
531
535
|
|
|
532
536
|
function workflowCapabilityGroup(entry) {
|
|
533
537
|
if (["web-surface", "release-propagation"].includes(entry.id)) return capabilityGroup("site-and-propagation");
|
|
534
|
-
if (["build", "release-candidate-promote"].includes(entry.id)) return capabilityGroup("reusable-build");
|
|
538
|
+
if (["build", "release-candidate-promote", "publication-artifact"].includes(entry.id)) return capabilityGroup("reusable-build");
|
|
535
539
|
if (["buildchain-ref-promotion", "release-line-bootstrap"].includes(entry.id)) return capabilityGroup("release-passport-trust");
|
|
536
540
|
if (entry.id.includes("patrol") || entry.id.includes("dev-pr-auto-merge")) return capabilityGroup("governance-versioning");
|
|
537
541
|
if (entry.status === "repository-internal" || entry.status === "compatibility-fixture") return capabilityGroup("api-cli-reference");
|
package/scripts/init-repo.mjs
CHANGED
|
@@ -239,6 +239,33 @@ command = "buildchain infra-contract --mode ci"
|
|
|
239
239
|
`;
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
function publicationArtifactToml(cwd) {
|
|
243
|
+
const name = repoName(cwd);
|
|
244
|
+
return `schema = 1
|
|
245
|
+
|
|
246
|
+
[project]
|
|
247
|
+
type = "publication-artifact"
|
|
248
|
+
name = "${name}"
|
|
249
|
+
|
|
250
|
+
[publication]
|
|
251
|
+
kind = "paper"
|
|
252
|
+
title = "${name}"
|
|
253
|
+
primary_artifact = "_build/main.pdf"
|
|
254
|
+
artifact_paths = ["_build/main.pdf"]
|
|
255
|
+
metadata_paths = ["README.md", "docs/MAP.md"]
|
|
256
|
+
source_paths = ["paper", "README.md", "LICENSE", "Makefile"]
|
|
257
|
+
site_consumers = ["papers-site"]
|
|
258
|
+
manifest_path = ".buildchain/publication/publication-artifact.json"
|
|
259
|
+
source_bundle_path = ".buildchain/publication/source.tar.gz"
|
|
260
|
+
|
|
261
|
+
[lifecycle.build]
|
|
262
|
+
command = "make pdf"
|
|
263
|
+
|
|
264
|
+
[lifecycle.verify]
|
|
265
|
+
command = "make check"
|
|
266
|
+
`;
|
|
267
|
+
}
|
|
268
|
+
|
|
242
269
|
function infraContractDesiredJson(cwd) {
|
|
243
270
|
return `${JSON.stringify({
|
|
244
271
|
service: repoName(cwd),
|
|
@@ -280,10 +307,48 @@ function workflowArtifactPaths(type) {
|
|
|
280
307
|
.buildchain/infra-contract-evidence-bundle.json
|
|
281
308
|
.buildchain/infra-contract-evidence-verification.json`;
|
|
282
309
|
}
|
|
310
|
+
if (type === "publication-artifact") {
|
|
311
|
+
return `_build/main.pdf
|
|
312
|
+
.buildchain/publication/publication-artifact.json
|
|
313
|
+
.buildchain/publication/publication-artifact-passport.json
|
|
314
|
+
.buildchain/publication/source.tar.gz`;
|
|
315
|
+
}
|
|
283
316
|
return `dist
|
|
284
317
|
build/stage`;
|
|
285
318
|
}
|
|
286
319
|
|
|
320
|
+
function publicationWorkflowYaml() {
|
|
321
|
+
return `name: Build
|
|
322
|
+
|
|
323
|
+
on:
|
|
324
|
+
workflow_dispatch:
|
|
325
|
+
inputs:
|
|
326
|
+
buildchain-ref:
|
|
327
|
+
description: "Temporary Buildchain runtime ref for trusted manual validation"
|
|
328
|
+
required: false
|
|
329
|
+
default: ""
|
|
330
|
+
pull_request:
|
|
331
|
+
push:
|
|
332
|
+
branches:
|
|
333
|
+
- "dev/**"
|
|
334
|
+
- "alpha/**"
|
|
335
|
+
- "release/**"
|
|
336
|
+
|
|
337
|
+
permissions:
|
|
338
|
+
contents: read
|
|
339
|
+
issues: write
|
|
340
|
+
|
|
341
|
+
jobs:
|
|
342
|
+
publication:
|
|
343
|
+
uses: kungfu-systems/buildchain/.github/workflows/publication-artifact.yml@v2
|
|
344
|
+
with:
|
|
345
|
+
buildchain-ref: \${{ inputs.buildchain-ref || '' }}
|
|
346
|
+
build-command: make pdf
|
|
347
|
+
verify-command: make check
|
|
348
|
+
artifact-name: publication-artifact
|
|
349
|
+
`;
|
|
350
|
+
}
|
|
351
|
+
|
|
287
352
|
function workflowYaml({ type, runnerPreset, artifactName }) {
|
|
288
353
|
return `name: Build
|
|
289
354
|
|
|
@@ -350,19 +415,28 @@ export function initBuildchainRepo({
|
|
|
350
415
|
if (type === "infra-contract") {
|
|
351
416
|
return infraContractToml(resolvedCwd);
|
|
352
417
|
}
|
|
418
|
+
if (type === "publication-artifact") {
|
|
419
|
+
return publicationArtifactToml(resolvedCwd);
|
|
420
|
+
}
|
|
353
421
|
if (type === "anchored-package") {
|
|
354
422
|
return anchoredPackageToml(resolvedCwd, manager);
|
|
355
423
|
}
|
|
356
|
-
throw new Error("init --type must be one of package, native, web-surface, infra-contract, or anchored-package");
|
|
424
|
+
throw new Error("init --type must be one of package, native, web-surface, infra-contract, publication-artifact, or anchored-package");
|
|
357
425
|
})();
|
|
358
426
|
|
|
359
427
|
const written = [
|
|
360
428
|
writeIfAllowed(path.join(resolvedCwd, BUILDCHAIN_CONFIG_PATH), toml, { force }),
|
|
361
|
-
writeIfAllowed(
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
429
|
+
writeIfAllowed(
|
|
430
|
+
path.join(resolvedCwd, ".github", "workflows", "build.yml"),
|
|
431
|
+
type === "publication-artifact"
|
|
432
|
+
? publicationWorkflowYaml()
|
|
433
|
+
: workflowYaml({
|
|
434
|
+
type,
|
|
435
|
+
runnerPreset,
|
|
436
|
+
artifactName,
|
|
437
|
+
}),
|
|
438
|
+
{ force },
|
|
439
|
+
),
|
|
366
440
|
];
|
|
367
441
|
|
|
368
442
|
if (type === "anchored-package" && !fs.existsSync(path.join(resolvedCwd, "release.json"))) {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import { writePublicationArtifact } from "../packages/core/publication-artifact.js";
|
|
4
|
+
|
|
5
|
+
function readFlag(args, name, fallback = "") {
|
|
6
|
+
const index = args.indexOf(`--${name}`);
|
|
7
|
+
return index === -1 ? fallback : args[index + 1] || "";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function readBooleanFlag(args, name) {
|
|
11
|
+
return args.includes(`--${name}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function runPublicationArtifactCli(args = process.argv.slice(2)) {
|
|
15
|
+
const [mode = "manifest"] = args;
|
|
16
|
+
if (!["manifest", "collect"].includes(mode)) {
|
|
17
|
+
throw new Error("usage: buildchain publication-artifact manifest [--cwd <dir>] [--source-sha <sha>] [--output <file>] [--passport-output <file>] [--source-bundle <file>] [--no-source-bundle] [--json]");
|
|
18
|
+
}
|
|
19
|
+
const result = writePublicationArtifact({
|
|
20
|
+
cwd: readFlag(args, "cwd", process.cwd()),
|
|
21
|
+
output: readFlag(args, "output", ""),
|
|
22
|
+
passportOutput: readFlag(args, "passport-output", ""),
|
|
23
|
+
sourceSha: readFlag(args, "source-sha", ""),
|
|
24
|
+
sourceBundlePath: readFlag(args, "source-bundle", ""),
|
|
25
|
+
sourceBundle: !readBooleanFlag(args, "no-source-bundle"),
|
|
26
|
+
generatedAt: readFlag(args, "generated-at", ""),
|
|
27
|
+
});
|
|
28
|
+
if (readBooleanFlag(args, "json")) {
|
|
29
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
30
|
+
} else {
|
|
31
|
+
process.stdout.write(`publication-manifest=${result.manifestPath}\n`);
|
|
32
|
+
process.stdout.write(`publication-passport=${result.passportPath}\n`);
|
|
33
|
+
process.stdout.write(`publication-primary-artifact=${result.manifest.publication.primaryArtifact}\n`);
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
39
|
+
try {
|
|
40
|
+
runPublicationArtifactCli();
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error(`publication-artifact: ${error.message}`);
|
|
43
|
+
process.exitCode = 1;
|
|
44
|
+
}
|
|
45
|
+
}
|