bulletin-deploy 0.7.27 → 0.7.28-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/assets/environments.json +2 -1
- package/bin/bulletin-deploy +136 -7
- package/dist/bug-report.js +4 -4
- package/dist/{chunk-I7YT2IAO.js → chunk-5DKLSM7C.js} +5 -2
- package/dist/{chunk-AXIJLJZK.js → chunk-FOPTSHK2.js} +1 -1
- package/dist/chunk-GQAKFSMZ.js +164 -0
- package/dist/chunk-GZD2UFLR.js +8 -0
- package/dist/{chunk-TILLNN33.js → chunk-KP64NWAS.js} +372 -30
- package/dist/chunk-LZJMVPYW.js +156 -0
- package/dist/{chunk-W4R7HNRX.js → chunk-MEC7GWLJ.js} +1 -1
- package/dist/chunk-MMAZFJDG.js +91 -0
- package/dist/{chunk-MGU5I7H5.js → chunk-OITUIM2E.js} +2 -1
- package/dist/{chunk-6TIWKKDS.js → chunk-PJCHSHMU.js} +4 -3
- package/dist/chunk-RI3ZLNPN.js +71 -0
- package/dist/{chunk-FW4HEUMH.js → chunk-SJKPP7KA.js} +2 -2
- package/dist/{chunk-JIKWTM5P.js → chunk-VUHKPUWQ.js} +66 -8
- package/dist/chunk-probe.js +3 -3
- package/dist/deploy.d.ts +23 -5
- package/dist/deploy.js +12 -10
- package/dist/dotns.d.ts +130 -1
- package/dist/dotns.js +14 -4
- package/dist/environments.js +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +42 -8
- package/dist/manifest/byte-budget.d.ts +46 -0
- package/dist/manifest/byte-budget.js +14 -0
- package/dist/manifest/config-load.d.ts +36 -0
- package/dist/manifest/config-load.js +10 -0
- package/dist/manifest/publish.d.ts +54 -0
- package/dist/manifest/publish.js +23 -0
- package/dist/manifest/schema.d.ts +29 -0
- package/dist/manifest/schema.js +10 -0
- package/dist/manifest/types.d.ts +90 -0
- package/dist/manifest/types.js +6 -0
- package/dist/memory-report.js +2 -2
- package/dist/merkle.js +8 -8
- package/dist/personhood/bind-paid-alias.js +2 -2
- package/dist/personhood/bind-personal-id.js +2 -2
- package/dist/personhood/bootstrap.js +12 -12
- package/dist/personhood/claim-pgas.js +2 -2
- package/dist/personhood/member-key.js +2 -2
- package/dist/personhood/people-client.js +4 -4
- package/dist/personhood/reprove.js +5 -5
- package/dist/run-state.js +1 -1
- package/dist/telemetry.js +2 -2
- package/dist/version-check.js +3 -3
- package/package.json +4 -3
- package/dist/{chunk-74ETPOKH.js → chunk-2VAUMZB2.js} +5 -5
- package/dist/{chunk-QHOZEY5X.js → chunk-5VZQ2KSU.js} +7 -7
- package/dist/{chunk-EJ5TNGAY.js → chunk-BMAEWZYV.js} +3 -3
- package/dist/{chunk-A5IQ5MKO.js → chunk-IDYGYIMH.js} +3 -3
package/README.md
CHANGED
|
@@ -215,6 +215,16 @@ await deploy("./dist", "my-app00.dot", { jsMerkle: true });
|
|
|
215
215
|
| `IPFS CLI not installed` | Install Kubo or switch to `--js-merkle`. |
|
|
216
216
|
| Previous deploy did not exit cleanly / OOM hint | Retry with a larger Node heap, for example `NODE_OPTIONS='--max-old-space-size=8192'`. |
|
|
217
217
|
|
|
218
|
+
## Contributing
|
|
219
|
+
|
|
220
|
+
New to the codebase? Start with **[ONBOARDING.md](ONBOARDING.md)** — it covers install, the mental model, the first task, and the working conventions for this repo (worktree-per-branch, squash-merge policy, never-delete-tests, where the per-directory rules live).
|
|
221
|
+
|
|
222
|
+
The team uses Claude Code as a primary tool. The repo ships team-shared Claude configuration: `.claude/skills/` (project-specific commands like `/e2e-local`, `/dotns-diagnose`, `/sentry-query`), `.claude/settings.json` (Bash allowlist), and per-directory `CLAUDE.md` files in `src/`, `sentry/`, `test/`, `tools/` that load on demand. Running `claude` inside this repo picks all of that up automatically.
|
|
223
|
+
|
|
224
|
+
Already have Claude Code installed? Clone, `npm install`, open `ONBOARDING.md`. New to Claude Code itself? The same doc covers install.
|
|
225
|
+
|
|
226
|
+
For maintainers and engineers familiar with the release flow, the canonical procedures live in the root [`CLAUDE.md`](CLAUDE.md): change workflow, dual-stage RC → stable release, post-release Sentry monitoring, and the squash-merge convention that satisfies branch protection without per-commit GPG signing.
|
|
227
|
+
|
|
218
228
|
## More Docs
|
|
219
229
|
|
|
220
230
|
- [Bootstrap and operator setup](docs/bootstrap.md)
|
package/assets/environments.json
CHANGED
|
@@ -80,7 +80,8 @@
|
|
|
80
80
|
"POP_RULES": "0x2002C1c15b88632Ad01c7770f6EbE1Ca05c8472E",
|
|
81
81
|
"STORE_FACTORY": "0x0DE5De70d61cc6b44B45d6595afDe8dB9b55bc31",
|
|
82
82
|
"LABEL_STORE_BEACON": "0xD033F7Ada687E8BC776928AB239505F9f0479Ce7",
|
|
83
|
-
"USER_STORE_BEACON": "0x7eD9b7D137Fa535965048F93b3B0248fEd2fcd32"
|
|
83
|
+
"USER_STORE_BEACON": "0x7eD9b7D137Fa535965048F93b3B0248fEd2fcd32",
|
|
84
|
+
"PUBLISHER": "0x1307fc02d308f879a16b1ae3a49b4927aed53649"
|
|
84
85
|
}
|
|
85
86
|
},
|
|
86
87
|
{
|
package/bin/bulletin-deploy
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { deploy, DEFAULT_BULLETIN_RPC, DEFAULT_POOL_SIZE, NonRetryableError, EXIT_CODE_NO_RETRY, isConnectionError } from "../dist/deploy.js";
|
|
3
|
+
import { deploy, DEFAULT_BULLETIN_RPC, DEFAULT_POOL_SIZE, NonRetryableError, EXIT_CODE_NO_RETRY, isConnectionError, unpublish } from "../dist/deploy.js";
|
|
4
4
|
import { VERSION, setDeployAttribute, captureWarning, closeTelemetry, setRunStateActive, markRelaunchOomHintShown } from "../dist/telemetry.js";
|
|
5
5
|
import { handleFailedDeploy, handlePreflightVersionCheck, fetchVersionInfo, preReleaseWarning, checkNodeVersion } from "../dist/version-check.js";
|
|
6
6
|
import { setDeployContext, installLogCapture, buildCliFlagsSummary } from "../dist/bug-report.js";
|
|
@@ -36,19 +36,26 @@ for (let i = 0; i < args.length; i++) {
|
|
|
36
36
|
else if (args[i] === "--js-merkle") { flags.jsMerkle = true; }
|
|
37
37
|
else if (args[i] === "--input-car") { flags.inputCar = args[++i]; }
|
|
38
38
|
else if (args[i] === "--tag") { flags.tag = args[++i]; }
|
|
39
|
-
else if (args[i] === "--
|
|
40
|
-
else if (args[i] === "--description") { flags.description = args[++i]; }
|
|
39
|
+
else if (args[i] === "--config") { flags.config = args[++i]; }
|
|
41
40
|
else if (args[i] === "--gh-pages-mirror") { flags.ghPagesMirror = true; }
|
|
42
41
|
else if (args[i] === "--allow-large-deploy") { flags.allowLargeDeploy = true; }
|
|
43
42
|
else if (args[i] === "--reproducible") { flags.reproducibleSource = "commit"; }
|
|
44
43
|
else if (args[i].startsWith("--reproducible=")) { flags.reproducibleSource = args[i].slice("--reproducible=".length); }
|
|
45
44
|
else if (args[i] === "--dump-car") { flags.dumpCar = true; }
|
|
46
45
|
else if (args[i].startsWith("--dump-car=")) { flags.dumpCar = args[i].slice("--dump-car=".length); }
|
|
46
|
+
else if (args[i] === "--publish") { flags.publish = true; }
|
|
47
|
+
else if (args[i] === "--unpublish") { flags.unpublish = true; }
|
|
48
|
+
else if (args[i] === "--fail-on-publish-error") { flags.failOnPublishError = true; }
|
|
47
49
|
else if (args[i] === "--version" || args[i] === "-V") { flags.version = true; }
|
|
48
50
|
else if (args[i] === "--help" || args[i] === "-h") { flags.help = true; }
|
|
49
51
|
else { positional.push(args[i]); }
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
if (flags.publish && flags.unpublish) {
|
|
55
|
+
console.error("Error: --publish and --unpublish are mutually exclusive.");
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
52
59
|
if (flags.version) {
|
|
53
60
|
console.log(`bulletin-deploy v${VERSION}`);
|
|
54
61
|
process.exit(0);
|
|
@@ -72,7 +79,74 @@ if (flags.listEnvironments) {
|
|
|
72
79
|
}
|
|
73
80
|
}
|
|
74
81
|
|
|
82
|
+
if (flags.unpublish) {
|
|
83
|
+
if (!flags.mnemonic && !process.env.MNEMONIC) {
|
|
84
|
+
console.error("Error: --unpublish requires --mnemonic (or MNEMONIC env var).");
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
const [domain] = positional;
|
|
88
|
+
if (!domain) {
|
|
89
|
+
console.error("Error: --unpublish requires a domain (e.g. my-app.dot)");
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const result = await unpublish(domain, {
|
|
94
|
+
mnemonic: flags.mnemonic,
|
|
95
|
+
derivationPath: flags.derivationPath,
|
|
96
|
+
rpc: flags.rpc,
|
|
97
|
+
env: flags.env,
|
|
98
|
+
});
|
|
99
|
+
console.log(`Domain: ${result.domainName}`);
|
|
100
|
+
console.log(`Status: ${result.status}`);
|
|
101
|
+
process.exit(0);
|
|
102
|
+
} catch (e) {
|
|
103
|
+
console.error(`Unpublish failed:`, e?.message ?? e);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
75
107
|
|
|
108
|
+
// `product` subcommand. Only `validate` is wired today. `publish` and `resolve` arrive in later phases.
|
|
109
|
+
if (positional[0] === "product") {
|
|
110
|
+
const verb = positional[1];
|
|
111
|
+
if (verb === "validate") {
|
|
112
|
+
try {
|
|
113
|
+
const { loadProductConfig, pessimisticSizePreflight, getTextRecordBudgetBytes } =
|
|
114
|
+
await import("../dist/index.js");
|
|
115
|
+
const explicitPath = positional[2];
|
|
116
|
+
const { config, sourcePath } = explicitPath
|
|
117
|
+
? await loadProductConfig({ path: explicitPath })
|
|
118
|
+
: await loadProductConfig();
|
|
119
|
+
console.log(`✓ Loaded ${sourcePath}`);
|
|
120
|
+
console.log(`✓ Schema ${config.domain} (${config.executables.length} executable${config.executables.length === 1 ? "" : "s"})`);
|
|
121
|
+
const budget = getTextRecordBudgetBytes();
|
|
122
|
+
const report = pessimisticSizePreflight(config, budget);
|
|
123
|
+
for (const check of report.checks) {
|
|
124
|
+
const tag = check.ok ? "✓" : "✗";
|
|
125
|
+
console.log(`${tag} ${check.key.padEnd(40)} ${check.bytes} B / ${check.budget} B`);
|
|
126
|
+
}
|
|
127
|
+
if (!report.ok) {
|
|
128
|
+
console.error("Pessimistic size preflight failed — at least one manifest exceeds the dotNS text-record budget.");
|
|
129
|
+
process.exit(EXIT_CODE_NO_RETRY);
|
|
130
|
+
}
|
|
131
|
+
console.log("Validate complete.");
|
|
132
|
+
process.exit(0);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
console.error(`Error: ${err?.message ?? err}`);
|
|
135
|
+
process.exit(err instanceof NonRetryableError ? EXIT_CODE_NO_RETRY : 1);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (verb === "publish" || verb === "resolve") {
|
|
139
|
+
console.error(`'product ${verb}' is not implemented yet (planned for a later phase).`);
|
|
140
|
+
process.exit(2);
|
|
141
|
+
}
|
|
142
|
+
console.error(`Unknown product subcommand: ${verb ?? "(none)"}. Try: bulletin-deploy product validate [<config-path>]`);
|
|
143
|
+
process.exit(2);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (flags.publish && !flags.mnemonic && !process.env.MNEMONIC) {
|
|
147
|
+
console.error("Error: --publish requires --mnemonic (or MNEMONIC env var).");
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
76
150
|
|
|
77
151
|
if (flags.help || positional.length === 0) {
|
|
78
152
|
console.log(`bulletin-deploy v${VERSION}
|
|
@@ -97,10 +171,23 @@ Options:
|
|
|
97
171
|
--dump-car[=<path>] Save the pre-upload CAR file to disk. Default path: <buildDir>.bulletin.car.
|
|
98
172
|
Override path with =<path>. Also settable via BULLETIN_DEPLOY_DUMP_CAR env var.
|
|
99
173
|
--tag "..." Label deploy in telemetry (or set DEPLOY_TAG env var); see Telemetry in README
|
|
100
|
-
--
|
|
101
|
-
|
|
174
|
+
--config <path> Explicit path to a bulletin-deploy.config.ts (default: walk up from
|
|
175
|
+
<build-dir> looking for bulletin-deploy.config.{ts,js,mjs}). When a
|
|
176
|
+
config is found, deploy ALSO writes the manifest + executable text
|
|
177
|
+
records on <domain> and its app/widget/worker subnames.
|
|
102
178
|
--gh-pages-mirror After deploy, push the CAR to the current repo's gh-pages branch
|
|
103
179
|
at bulletin/<domain>.dot.car (opt-in; also set GH_PAGES_MIRROR=1)
|
|
180
|
+
--publish After deploy, list <domain> in the on-chain Publisher
|
|
181
|
+
registry (Publisher.publish). Only takes effect on envs
|
|
182
|
+
with a deployed Publisher (currently: paseo-next-v2).
|
|
183
|
+
Requires --mnemonic (the signer must own the label).
|
|
184
|
+
--unpublish Standalone mode: removes <domain> from the Publisher
|
|
185
|
+
registry (Publisher.unpublish). Skips the deploy.
|
|
186
|
+
Usage: bulletin-deploy --unpublish <domain.dot>
|
|
187
|
+
Requires --mnemonic. Mutually exclusive with --publish.
|
|
188
|
+
--fail-on-publish-error
|
|
189
|
+
Exit non-zero if --publish fails after a successful deploy.
|
|
190
|
+
Default: non-fatal (warning logged, exit 0).
|
|
104
191
|
--version Show version
|
|
105
192
|
--help Show this help`);
|
|
106
193
|
process.exit(0);
|
|
@@ -269,11 +356,11 @@ try {
|
|
|
269
356
|
inputCar: flags.inputCar,
|
|
270
357
|
tag: flags.tag,
|
|
271
358
|
ghPagesMirror: flags.ghPagesMirror,
|
|
272
|
-
name: flags.name,
|
|
273
|
-
description: flags.description,
|
|
274
359
|
allowLargeDeploy: flags.allowLargeDeploy,
|
|
275
360
|
reproducibleSource: flags.reproducibleSource,
|
|
276
361
|
dumpCar: flags.dumpCar,
|
|
362
|
+
publish: flags.publish,
|
|
363
|
+
failOnPublishError: flags.failOnPublishError,
|
|
277
364
|
});
|
|
278
365
|
|
|
279
366
|
const output = process.env.GITHUB_OUTPUT;
|
|
@@ -285,6 +372,48 @@ try {
|
|
|
285
372
|
console.log(`CID: ${result.cid}`);
|
|
286
373
|
console.log(`Domain: ${result.domainName}`);
|
|
287
374
|
|
|
375
|
+
// Opt-in manifest publish on top of the legacy contenthash deploy.
|
|
376
|
+
//
|
|
377
|
+
// Walks up from <build-dir> (or honours --config) looking for
|
|
378
|
+
// bulletin-deploy.config.{ts,js,mjs}. When found, writes the root + per-
|
|
379
|
+
// executable text records, otherwise the legacy flow returns unchanged.
|
|
380
|
+
{
|
|
381
|
+
const { tryLoadProductConfig, publishManifest } = await import("../dist/index.js");
|
|
382
|
+
const path = await import("node:path");
|
|
383
|
+
const buildDirAbs = path.resolve(buildDir);
|
|
384
|
+
const loaded = await tryLoadProductConfig(
|
|
385
|
+
flags.config ? { path: flags.config } : { cwd: buildDirAbs, walkUp: true },
|
|
386
|
+
);
|
|
387
|
+
if (loaded) {
|
|
388
|
+
try {
|
|
389
|
+
await publishManifest({
|
|
390
|
+
loaded,
|
|
391
|
+
domain,
|
|
392
|
+
buildDirCid: { absPath: buildDirAbs, cid: result.cid },
|
|
393
|
+
env: flags.env,
|
|
394
|
+
rpc: flags.rpc,
|
|
395
|
+
mnemonic: flags.mnemonic,
|
|
396
|
+
derivationPath: flags.derivationPath,
|
|
397
|
+
});
|
|
398
|
+
} catch (err) {
|
|
399
|
+
console.error(`Manifest publish failed: ${err?.message ?? err}`);
|
|
400
|
+
process.exit(err instanceof NonRetryableError ? EXIT_CODE_NO_RETRY : 1);
|
|
401
|
+
}
|
|
402
|
+
} else {
|
|
403
|
+
const where = flags.config
|
|
404
|
+
? `--config ${flags.config}`
|
|
405
|
+
: `bulletin-deploy.config.{ts,js,mjs} via walking up from ${buildDirAbs}`;
|
|
406
|
+
console.log("");
|
|
407
|
+
console.log(`⚠ No bulletin-deploy.config.ts found (${where}).`);
|
|
408
|
+
console.log(` ${domain} was published as legacy contenthash only.`);
|
|
409
|
+
console.log(" Add a bulletin-deploy.config.ts to enable the product manifest:");
|
|
410
|
+
console.log(" • product icon, displayName, description on the base name");
|
|
411
|
+
console.log(" • per-modality subnames (app.<id>.dot, widget.<id>.dot, worker.<id>.dot)");
|
|
412
|
+
console.log(" • each modality’s archive CID on its subname contenthash");
|
|
413
|
+
console.log(" See: https://github.com/paritytech/triangle-js-sdks (Product Manifest RFC)");
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
288
417
|
if (!flags.help && !flags.version) {
|
|
289
418
|
try { writeRunState({ status: "succeeded", endedAt: Date.now() }); } catch {}
|
|
290
419
|
}
|
package/dist/bug-report.js
CHANGED
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
offerBugReport,
|
|
10
10
|
scrubSecrets,
|
|
11
11
|
setDeployContext
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-5DKLSM7C.js";
|
|
13
|
+
import "./chunk-FOPTSHK2.js";
|
|
14
|
+
import "./chunk-SJKPP7KA.js";
|
|
15
|
+
import "./chunk-PJCHSHMU.js";
|
|
16
16
|
export {
|
|
17
17
|
buildCliFlagsSummary,
|
|
18
18
|
buildLabels,
|
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
classifyErrorArea,
|
|
3
3
|
isInteractive,
|
|
4
4
|
promptYesNo
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-FOPTSHK2.js";
|
|
6
6
|
import {
|
|
7
7
|
VERSION,
|
|
8
8
|
getCurrentSentryTraceId
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-SJKPP7KA.js";
|
|
10
10
|
|
|
11
11
|
// src/bug-report.ts
|
|
12
12
|
import { execSync, execFileSync } from "child_process";
|
|
@@ -76,6 +76,9 @@ function buildCliFlagsSummary(flags) {
|
|
|
76
76
|
const parts = [];
|
|
77
77
|
if (flags.jsMerkle) parts.push("--js-merkle");
|
|
78
78
|
if (flags.ghPagesMirror) parts.push("--gh-pages-mirror");
|
|
79
|
+
if (flags.publish) parts.push("--publish");
|
|
80
|
+
if (flags.unpublish) parts.push("--unpublish");
|
|
81
|
+
if (flags.failOnPublishError) parts.push("--fail-on-publish-error");
|
|
79
82
|
if (flags.poolSize != null) parts.push(`--pool-size ${String(flags.poolSize)}`);
|
|
80
83
|
if (typeof flags.tag === "string" && flags.tag) parts.push(`--tag ${flags.tag}`);
|
|
81
84
|
if (flags.mnemonic) parts.push("--mnemonic <set>");
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import {
|
|
2
|
+
pessimisticSizePreflight
|
|
3
|
+
} from "./chunk-RI3ZLNPN.js";
|
|
4
|
+
import {
|
|
5
|
+
encodeContenthash,
|
|
6
|
+
resolveDotnsConnectOptions,
|
|
7
|
+
storeDirectory,
|
|
8
|
+
storeFile
|
|
9
|
+
} from "./chunk-VUHKPUWQ.js";
|
|
10
|
+
import {
|
|
11
|
+
DotNS
|
|
12
|
+
} from "./chunk-KP64NWAS.js";
|
|
13
|
+
import {
|
|
14
|
+
getPopSelfServeConfig,
|
|
15
|
+
loadEnvironments,
|
|
16
|
+
resolveEndpoints
|
|
17
|
+
} from "./chunk-OITUIM2E.js";
|
|
18
|
+
import {
|
|
19
|
+
NonRetryableError
|
|
20
|
+
} from "./chunk-ZOC4GITL.js";
|
|
21
|
+
|
|
22
|
+
// src/manifest/publish.ts
|
|
23
|
+
import * as fs from "fs/promises";
|
|
24
|
+
import * as path from "path";
|
|
25
|
+
async function publishManifest(opts) {
|
|
26
|
+
const { config, sourcePath } = opts.loaded;
|
|
27
|
+
if (config.domain !== opts.domain) {
|
|
28
|
+
throw new NonRetryableError(
|
|
29
|
+
`Config domain '${config.domain}' (in ${sourcePath}) does not match deploy domain '${opts.domain}'. Either update the config or pass the matching <domain> argument.`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
const sizeReport = pessimisticSizePreflight(config);
|
|
33
|
+
if (!sizeReport.ok) {
|
|
34
|
+
const failing = sizeReport.checks.filter((c) => !c.ok).map((c) => `${c.key}: ${c.bytes}/${c.budget} B`).join(", ");
|
|
35
|
+
throw new NonRetryableError(
|
|
36
|
+
`Manifest size preflight failed: ${failing}. Shrink displayName / description / paths or override BULLETIN_TEXT_BUDGET.`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
const configDir = path.dirname(sourcePath);
|
|
40
|
+
const iconAbs = path.resolve(configDir, config.icon.path);
|
|
41
|
+
const iconBytes = await readFileOrThrow(iconAbs, "icon");
|
|
42
|
+
console.log(`
|
|
43
|
+
Manifest publish \u2014 ${config.domain}`);
|
|
44
|
+
console.log(` Loaded config: ${sourcePath}`);
|
|
45
|
+
console.log(` Uploading icon (${iconBytes.length} B)\u2026`);
|
|
46
|
+
const iconCid = await storeFile(iconBytes);
|
|
47
|
+
console.log(` Icon CID: ${iconCid}`);
|
|
48
|
+
const executableCids = {};
|
|
49
|
+
for (const exec of config.executables) {
|
|
50
|
+
const execAbs = path.resolve(configDir, exec.path);
|
|
51
|
+
if (opts.buildDirCid && path.resolve(opts.buildDirCid.absPath) === execAbs) {
|
|
52
|
+
console.log(` Executable [${exec.kind}] reused build-dir CID: ${opts.buildDirCid.cid}`);
|
|
53
|
+
executableCids[exec.kind] = opts.buildDirCid.cid;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
console.log(` Uploading executable [${exec.kind}] from ${execAbs}\u2026`);
|
|
57
|
+
const { storageCid } = await storeDirectory(execAbs, {}, void 0, true);
|
|
58
|
+
console.log(` Executable [${exec.kind}] CID: ${storageCid}`);
|
|
59
|
+
executableCids[exec.kind] = storageCid;
|
|
60
|
+
}
|
|
61
|
+
const dotns = await connectDotNS(opts);
|
|
62
|
+
try {
|
|
63
|
+
const baseLabel = stripDotSuffix(config.domain);
|
|
64
|
+
await dotns.ensureContentResolver(baseLabel);
|
|
65
|
+
const rootManifest = composeRoot(config, iconCid);
|
|
66
|
+
const rootJson = JSON.stringify(rootManifest);
|
|
67
|
+
console.log(` Writing root manifest text record on ${config.domain} (${Buffer.byteLength(rootJson, "utf8")} B)\u2026`);
|
|
68
|
+
await dotns.setTextRecord(baseLabel, "manifest", rootJson);
|
|
69
|
+
let textRecordsWritten = 1;
|
|
70
|
+
for (const exec of config.executables) {
|
|
71
|
+
const cid = executableCids[exec.kind];
|
|
72
|
+
if (!cid) throw new NonRetryableError(`Internal: missing CID for executable kind '${exec.kind}'`);
|
|
73
|
+
const ownership = await dotns.checkSubdomainOwnership(exec.kind, baseLabel);
|
|
74
|
+
if (!ownership.owned) {
|
|
75
|
+
if (ownership.owner) {
|
|
76
|
+
throw new NonRetryableError(
|
|
77
|
+
`Subname ${exec.kind}.${config.domain} is owned by ${ownership.owner}, not the publisher. Aborting.`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
console.log(` Registering subname ${exec.kind}.${config.domain}\u2026`);
|
|
81
|
+
await dotns.registerSubdomain(exec.kind, baseLabel);
|
|
82
|
+
}
|
|
83
|
+
await dotns.ensureContentResolver(`${exec.kind}.${baseLabel}`);
|
|
84
|
+
const subContenthash = `0x${encodeContenthash(cid)}`;
|
|
85
|
+
console.log(` Setting contenthash on ${exec.kind}.${config.domain} \u2192 ${cid}\u2026`);
|
|
86
|
+
await dotns.setContenthash(`${exec.kind}.${baseLabel}`, subContenthash);
|
|
87
|
+
const execManifest = composeExecutable(exec);
|
|
88
|
+
const execJson = JSON.stringify(execManifest);
|
|
89
|
+
console.log(` Writing executable manifest on ${exec.kind}.${config.domain} (${Buffer.byteLength(execJson, "utf8")} B)\u2026`);
|
|
90
|
+
await dotns.setTextRecord(`${exec.kind}.${baseLabel}`, "executable", execJson);
|
|
91
|
+
textRecordsWritten++;
|
|
92
|
+
}
|
|
93
|
+
console.log(` \u2713 ${textRecordsWritten} text record${textRecordsWritten === 1 ? "" : "s"} written.`);
|
|
94
|
+
return { iconCid, executableCids, textRecordsWritten };
|
|
95
|
+
} finally {
|
|
96
|
+
dotns.disconnect();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function readFileOrThrow(p, label) {
|
|
100
|
+
try {
|
|
101
|
+
return await fs.readFile(p);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
throw new NonRetryableError(`Cannot read ${label} at ${p}: ${err.message}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function connectDotNS(opts) {
|
|
107
|
+
const envId = opts.env ?? "paseo-next-v2";
|
|
108
|
+
const { doc } = await loadEnvironments();
|
|
109
|
+
const resolved = resolveEndpoints(doc, envId);
|
|
110
|
+
const popSelfServe = getPopSelfServeConfig(doc, envId);
|
|
111
|
+
const deployOptsShim = {
|
|
112
|
+
mnemonic: opts.mnemonic,
|
|
113
|
+
derivationPath: opts.derivationPath
|
|
114
|
+
};
|
|
115
|
+
const connectOpts = resolveDotnsConnectOptions(
|
|
116
|
+
deployOptsShim,
|
|
117
|
+
resolved.assetHub,
|
|
118
|
+
resolved.autoAccountMapping,
|
|
119
|
+
resolved.contracts,
|
|
120
|
+
resolved.nativeToEthRatio,
|
|
121
|
+
envId,
|
|
122
|
+
popSelfServe,
|
|
123
|
+
resolved.registerStorageDeposit
|
|
124
|
+
);
|
|
125
|
+
const dotns = new DotNS();
|
|
126
|
+
await dotns.connect(connectOpts);
|
|
127
|
+
return dotns;
|
|
128
|
+
}
|
|
129
|
+
function composeRoot(config, iconCid) {
|
|
130
|
+
return {
|
|
131
|
+
$v: 1,
|
|
132
|
+
displayName: config.displayName,
|
|
133
|
+
description: config.description,
|
|
134
|
+
icon: { cid: iconCid, format: config.icon.format }
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function composeExecutable(exec) {
|
|
138
|
+
if (exec.kind === "app") {
|
|
139
|
+
return { $v: 1, kind: "app", appVersion: exec.appVersion };
|
|
140
|
+
}
|
|
141
|
+
if (exec.kind === "widget") {
|
|
142
|
+
return {
|
|
143
|
+
$v: 1,
|
|
144
|
+
kind: "widget",
|
|
145
|
+
appVersion: exec.appVersion,
|
|
146
|
+
dimensions: exec.dimensions,
|
|
147
|
+
...exec.description !== void 0 ? { description: exec.description } : {}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
$v: 1,
|
|
152
|
+
kind: "worker",
|
|
153
|
+
appVersion: exec.appVersion,
|
|
154
|
+
entrypoint: exec.entrypoint,
|
|
155
|
+
includes: exec.includes
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function stripDotSuffix(domain) {
|
|
159
|
+
return domain.replace(/\.dot$/i, "");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export {
|
|
163
|
+
publishManifest
|
|
164
|
+
};
|