bulletin-deploy 0.7.15 → 0.7.16-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 +3 -0
- package/bin/bulletin-deploy +18 -4
- package/dist/bug-report.js +4 -4
- package/dist/{chunk-5TW653QP.js → chunk-FACUN37U.js} +12 -9
- package/dist/{chunk-NE5MN2M2.js → chunk-H7J5Y73C.js} +2 -2
- package/dist/{chunk-KMRYJR4E.js → chunk-QFSPFKE2.js} +16 -2
- package/dist/{chunk-Y2OSXJIZ.js → chunk-VTVRSJLE.js} +1 -1
- package/dist/{chunk-2VYG7NXN.js → chunk-X3F7WHSF.js} +1 -1
- package/dist/{chunk-AQUBKPSF.js → chunk-XNVUAMAJ.js} +2 -2
- package/dist/{chunk-ZGU6FOLO.js → chunk-ZKHTRUD7.js} +1 -1
- package/dist/{chunk-VQHM3R6N.js → chunk-ZLFQ2MK4.js} +69 -12
- package/dist/chunk-probe.js +3 -3
- package/dist/deploy.d.ts +8 -0
- package/dist/deploy.js +8 -8
- package/dist/dotns.d.ts +3 -2
- package/dist/dotns.js +5 -3
- package/dist/environments.d.ts +1 -1
- package/dist/environments.js +1 -1
- package/dist/index.js +8 -8
- package/dist/memory-report.d.ts +1 -1
- package/dist/memory-report.js +2 -2
- package/dist/merkle.js +8 -8
- package/dist/run-state.js +1 -1
- package/dist/telemetry.js +2 -2
- package/dist/version-check.js +3 -3
- package/docs/telemetry.md +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -103,6 +103,7 @@ The runtime uses a 24-hour cache at `${XDG_CACHE_HOME:-~/.cache}/bulletin-deploy
|
|
|
103
103
|
| `--js-merkle` | Use pure-JS merkleization instead of the Kubo binary. |
|
|
104
104
|
| `--tag "..."` | Attach a free-form telemetry label. Also readable from `DEPLOY_TAG`. |
|
|
105
105
|
| `--gh-pages-mirror` | After a successful deploy, push the generated CAR to the current repo's `gh-pages` branch as an HTTP mirror. |
|
|
106
|
+
| `--input-car <path>` | Deploy from a pre-built CAR file instead of a build directory. Skips merkleization; reads the root CID from the CAR header. Usage: `bulletin-deploy --input-car site.car my-app.dot` |
|
|
106
107
|
| `--version` | Print the CLI version. |
|
|
107
108
|
| `--help` | Show help. |
|
|
108
109
|
|
|
@@ -201,6 +202,8 @@ await deploy("./dist", "my-app00.dot", { jsMerkle: true });
|
|
|
201
202
|
| `DOTNS_STATUS` | `full` on testnet, `none` on mainnet | PoP level to self-grant before registration: `none`, `lite`, or `full` |
|
|
202
203
|
| `IPFS_CID` | unset | Skip storage and reuse an existing CID |
|
|
203
204
|
| `DEPLOY_TAG` | unset | Telemetry label equivalent to `--tag` |
|
|
205
|
+
| `BULLETIN_DEPLOY_HOST_APP` | unset | Name of the host app embedding bulletin-deploy (e.g. `playground-cli`). Sets `deploy.host_app` on telemetry spans. |
|
|
206
|
+
| `BULLETIN_DEPLOY_HOST_APP_VERSION` | unset | Version of the host app. Sets `deploy.host_app_version` on telemetry spans when `BULLETIN_DEPLOY_HOST_APP` is also set. |
|
|
204
207
|
|
|
205
208
|
## Troubleshooting
|
|
206
209
|
|
package/bin/bulletin-deploy
CHANGED
|
@@ -27,6 +27,7 @@ for (let i = 0; i < args.length; i++) {
|
|
|
27
27
|
else if (args[i] === "--refresh-environments") { flags.refreshEnvironments = true; }
|
|
28
28
|
else if (args[i] === "--password") { flags.password = args[++i]; }
|
|
29
29
|
else if (args[i] === "--js-merkle") { flags.jsMerkle = true; }
|
|
30
|
+
else if (args[i] === "--input-car") { flags.inputCar = args[++i]; }
|
|
30
31
|
else if (args[i] === "--tag") { flags.tag = args[++i]; }
|
|
31
32
|
else if (args[i] === "--name") { flags.name = args[++i]; }
|
|
32
33
|
else if (args[i] === "--description") { flags.description = args[++i]; }
|
|
@@ -95,6 +96,8 @@ Options:
|
|
|
95
96
|
--pool-size N Number of pool accounts (default: 10)
|
|
96
97
|
--password "..." Encrypt SPA content (users will be prompted to decrypt)
|
|
97
98
|
--js-merkle Use pure-JS merkleization (no IPFS Kubo binary required)
|
|
99
|
+
--input-car <path> Deploy a pre-built CAR file; skips directory scan and merkleization.
|
|
100
|
+
Usage: bulletin-deploy --input-car <file.car> <domain.dot>
|
|
98
101
|
--tag "..." Label deploy in telemetry (or set DEPLOY_TAG env var); see Telemetry in README
|
|
99
102
|
--name "..." Optional. Sets the "name" text record on the domain.
|
|
100
103
|
--description "..." Optional. Sets the "description" text record (≤100 chars recommended).
|
|
@@ -210,10 +213,20 @@ if (!flags.help && !flags.version) {
|
|
|
210
213
|
}
|
|
211
214
|
|
|
212
215
|
try {
|
|
213
|
-
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
|
|
216
|
+
let buildDir, domain;
|
|
217
|
+
if (flags.inputCar) {
|
|
218
|
+
// --input-car mode: positional[0] is the domain; no build directory needed.
|
|
219
|
+
[domain] = positional;
|
|
220
|
+
if (!flags.inputCar) { console.error("Error: --input-car requires a path argument"); process.exit(1); }
|
|
221
|
+
if (!domain) { console.error("Error: domain required (e.g. my-app.dot)"); process.exit(1); }
|
|
222
|
+
if (!fs.existsSync(flags.inputCar)) { console.error(`Error: ${flags.inputCar} does not exist`); process.exit(1); }
|
|
223
|
+
buildDir = flags.inputCar; // passed as `content` to deploy(); inputCar branch handles it
|
|
224
|
+
} else {
|
|
225
|
+
[buildDir, domain] = positional;
|
|
226
|
+
if (!buildDir) { console.error("Error: build directory required"); process.exit(1); }
|
|
227
|
+
if (!domain) { console.error("Error: domain required (e.g. my-app.dot)"); process.exit(1); }
|
|
228
|
+
if (!fs.existsSync(buildDir)) { console.error(`Error: ${buildDir} does not exist`); process.exit(1); }
|
|
229
|
+
}
|
|
217
230
|
|
|
218
231
|
// --refresh-environments composed with a deploy: bust the cache before
|
|
219
232
|
// deploy() loads it, so deploy() picks up fresh data on the next loadEnvironments().
|
|
@@ -254,6 +267,7 @@ try {
|
|
|
254
267
|
poolSize: flags.poolSize,
|
|
255
268
|
password: flags.password,
|
|
256
269
|
jsMerkle: flags.jsMerkle,
|
|
270
|
+
inputCar: flags.inputCar,
|
|
257
271
|
tag: flags.tag,
|
|
258
272
|
ghPagesMirror: flags.ghPagesMirror,
|
|
259
273
|
name: flags.name,
|
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-XNVUAMAJ.js";
|
|
13
|
+
import "./chunk-H7J5Y73C.js";
|
|
14
|
+
import "./chunk-QFSPFKE2.js";
|
|
15
|
+
import "./chunk-ZKHTRUD7.js";
|
|
16
16
|
import "./chunk-QGM4M3NI.js";
|
|
17
17
|
export {
|
|
18
18
|
buildCliFlagsSummary,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
captureWarning,
|
|
6
6
|
setDeployAttribute,
|
|
7
7
|
withSpan
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-QFSPFKE2.js";
|
|
9
9
|
|
|
10
10
|
// src/dotns.ts
|
|
11
11
|
import { spawn } from "child_process";
|
|
@@ -362,6 +362,9 @@ function isExplicitCommitmentBuffer(envValue) {
|
|
|
362
362
|
const parsed = Number(envValue);
|
|
363
363
|
return Number.isFinite(parsed) && parsed > 0;
|
|
364
364
|
}
|
|
365
|
+
function isLikelyCommitmentRace(msg) {
|
|
366
|
+
return /CommitmentTooNew/i.test(msg) || /Revive\.ContractReverted/i.test(msg);
|
|
367
|
+
}
|
|
365
368
|
function classifyDotnsLabel(label) {
|
|
366
369
|
const totalLength = label.length;
|
|
367
370
|
const trailingDigits = countTrailingDigits(label);
|
|
@@ -1518,7 +1521,7 @@ var DotNS = class {
|
|
|
1518
1521
|
}
|
|
1519
1522
|
return { ...candidate, signerFreeBalance: effectiveBalance, feeFloor, toppedUp };
|
|
1520
1523
|
}
|
|
1521
|
-
async register(label, options = {}) {
|
|
1524
|
+
async register(label, options = {}, _cli = runDotnsCli) {
|
|
1522
1525
|
return withSpan("deploy.dotns.register", `2a. register ${label}.dot`, {}, async () => {
|
|
1523
1526
|
if (!this.connected) await this.connect(options);
|
|
1524
1527
|
label = validateDomainLabel(label);
|
|
@@ -1580,7 +1583,7 @@ var DotNS = class {
|
|
|
1580
1583
|
}
|
|
1581
1584
|
const runRegister = async (bufferSeconds) => {
|
|
1582
1585
|
const env = bufferSeconds ? { ...authEnv, DOTNS_COMMITMENT_BUFFER: bufferSeconds } : authEnv;
|
|
1583
|
-
return await
|
|
1586
|
+
return await _cli(
|
|
1584
1587
|
["register", "domain", "-n", label, "--json", ...statusArgs, ...reverseArgs, ...rpcFlag(this.rpc)],
|
|
1585
1588
|
env
|
|
1586
1589
|
);
|
|
@@ -1590,17 +1593,16 @@ var DotNS = class {
|
|
|
1590
1593
|
result = await runRegister();
|
|
1591
1594
|
} catch (err) {
|
|
1592
1595
|
const msg = err.message;
|
|
1593
|
-
if (
|
|
1596
|
+
if (!isLikelyCommitmentRace(msg)) throw err;
|
|
1594
1597
|
const retryBuffer = userSetBuffer ? void 0 : "60";
|
|
1595
|
-
console.log(`
|
|
1598
|
+
console.log(` register reverted on first attempt \u2014 retrying once${retryBuffer ? ` with DOTNS_COMMITMENT_BUFFER=${retryBuffer}s` : ""} (commit-reveal race; block-time lag typically resolves within a block or two).`);
|
|
1596
1599
|
try {
|
|
1597
1600
|
result = await runRegister(retryBuffer);
|
|
1598
1601
|
} catch (retryErr) {
|
|
1599
1602
|
const retryMsg = retryErr.message;
|
|
1600
|
-
if (
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
);
|
|
1603
|
+
if (!isLikelyCommitmentRace(retryMsg)) throw retryErr;
|
|
1604
|
+
const likelyCause = /CommitmentTooNew/i.test(retryMsg) ? `the chain's block timestamps are lagging wall-clock by more than 30s across two register attempts. This is usually a transient block-production slowdown. Retry in a minute, or set DOTNS_COMMITMENT_BUFFER=60 (or higher) to absorb longer drift. Upstream fix tracked at paritytech/dotns-sdk#105.` : `the register tx reverted twice in a row. This is typically a commit-reveal timing race (block-production slowdown), but could also be a contract rejection (label already taken, PoP status mismatch). Try again in a minute; if it persists, verify the label is available. Upstream: paritytech/dotns-sdk#105.`;
|
|
1605
|
+
throw new Error(`DotNS register failed after retry: ${likelyCause} Underlying: ${retryMsg}`);
|
|
1604
1606
|
}
|
|
1605
1607
|
}
|
|
1606
1608
|
console.log(`
|
|
@@ -1651,6 +1653,7 @@ export {
|
|
|
1651
1653
|
validateDomainLabel,
|
|
1652
1654
|
isCommitmentMature,
|
|
1653
1655
|
isExplicitCommitmentBuffer,
|
|
1656
|
+
isLikelyCommitmentRace,
|
|
1654
1657
|
classifyDotnsLabel,
|
|
1655
1658
|
canRegister,
|
|
1656
1659
|
simulateUserStatus,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-QFSPFKE2.js";
|
|
4
4
|
|
|
5
5
|
// src/version-check.ts
|
|
6
6
|
import { execSync, execFileSync } from "child_process";
|
|
7
7
|
import { createInterface } from "readline";
|
|
8
8
|
var REGISTRY_URL = "https://registry.npmjs.org/bulletin-deploy/latest";
|
|
9
|
-
var KILL_SWITCH_URL = "https://raw.githubusercontent.com/paritytech/
|
|
9
|
+
var KILL_SWITCH_URL = "https://raw.githubusercontent.com/paritytech/triangle-deploy/main/min-version.json";
|
|
10
10
|
var FETCH_TIMEOUT = 3e3;
|
|
11
11
|
function compareSemver(a, b) {
|
|
12
12
|
const [coreA, preA] = a.split("-", 2);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
package_default,
|
|
3
3
|
writeRunState
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZKHTRUD7.js";
|
|
5
5
|
|
|
6
6
|
// src/memory-report.ts
|
|
7
7
|
import * as fs2 from "fs";
|
|
@@ -13,8 +13,19 @@ import * as v8 from "v8";
|
|
|
13
13
|
import { execSync } from "child_process";
|
|
14
14
|
import { createHash } from "crypto";
|
|
15
15
|
import * as fs from "fs";
|
|
16
|
+
import { createRequire } from "module";
|
|
16
17
|
import * as path from "path";
|
|
17
18
|
var VERSION = package_default.version;
|
|
19
|
+
function resolveDotnsCliVersion() {
|
|
20
|
+
try {
|
|
21
|
+
const require2 = createRequire(import.meta.url);
|
|
22
|
+
const dotnsCliPkg = require2("@parity/dotns-cli/package.json");
|
|
23
|
+
return dotnsCliPkg.version ?? "unknown";
|
|
24
|
+
} catch {
|
|
25
|
+
return "unknown";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
var DOTNS_CLI_VERSION = resolveDotnsCliVersion();
|
|
18
29
|
var DEFAULT_DSN = "https://e021c025d79c4c3ade2862a11f13c40b@o4511059872841728.ingest.de.sentry.io/4511093597405264";
|
|
19
30
|
var INTERNAL_ORG_RE = /^(paritytech|w3f|polkadot-fellows)\//i;
|
|
20
31
|
var PARITY_HOST_APPS = /* @__PURE__ */ new Set(["playground-cli"]);
|
|
@@ -211,6 +222,9 @@ function getDeployAttributes(domain) {
|
|
|
211
222
|
"deploy.dotns.toppedup": "false"
|
|
212
223
|
};
|
|
213
224
|
if (hostApp) attrs["deploy.host_app"] = hostApp;
|
|
225
|
+
const hostAppVersion = process.env.BULLETIN_DEPLOY_HOST_APP_VERSION;
|
|
226
|
+
if (hostAppVersion) attrs["deploy.host_app_version"] = hostAppVersion;
|
|
227
|
+
attrs["deploy.dotns_cli_version"] = DOTNS_CLI_VERSION;
|
|
214
228
|
return attrs;
|
|
215
229
|
}
|
|
216
230
|
function isExpectedError(msg) {
|
|
@@ -452,7 +466,7 @@ async function flush() {
|
|
|
452
466
|
function isBunRuntime() {
|
|
453
467
|
return typeof globalThis.Bun !== "undefined" || typeof process.versions.bun === "string";
|
|
454
468
|
}
|
|
455
|
-
var DEFAULT_THRESHOLD_MB =
|
|
469
|
+
var DEFAULT_THRESHOLD_MB = 800;
|
|
456
470
|
function toMb2(bytes) {
|
|
457
471
|
return Math.round(bytes / 1024 / 1024 * 100) / 100;
|
|
458
472
|
}
|
|
@@ -7,7 +7,7 @@ import * as fs from "fs/promises";
|
|
|
7
7
|
import * as path from "path";
|
|
8
8
|
import * as os from "os";
|
|
9
9
|
import { fileURLToPath } from "url";
|
|
10
|
-
var DEFAULT_ENVIRONMENTS_URL = "https://raw.githubusercontent.com/paritytech/
|
|
10
|
+
var DEFAULT_ENVIRONMENTS_URL = "https://raw.githubusercontent.com/paritytech/triangle-deploy/main/assets/environments.json";
|
|
11
11
|
var DEFAULT_ENV_ID = "paseo-next";
|
|
12
12
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
13
13
|
var FETCH_TIMEOUT_MS = 5e3;
|
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
classifyErrorArea,
|
|
3
3
|
isInteractive,
|
|
4
4
|
promptYesNo
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-H7J5Y73C.js";
|
|
6
6
|
import {
|
|
7
7
|
VERSION,
|
|
8
8
|
getCurrentSentryTraceId
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-QFSPFKE2.js";
|
|
10
10
|
|
|
11
11
|
// src/bug-report.ts
|
|
12
12
|
import { execSync, execFileSync } from "child_process";
|
|
@@ -18,10 +18,10 @@ import {
|
|
|
18
18
|
} from "./chunk-S7EM5VMW.js";
|
|
19
19
|
import {
|
|
20
20
|
setDeployContext
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-XNVUAMAJ.js";
|
|
22
22
|
import {
|
|
23
23
|
probeChunks
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-VTVRSJLE.js";
|
|
25
25
|
import {
|
|
26
26
|
packSection
|
|
27
27
|
} from "./chunk-C2TS5MER.js";
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
parseDomainName,
|
|
33
33
|
popStatusName,
|
|
34
34
|
verifyNonceAdvanced
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-FACUN37U.js";
|
|
36
36
|
import {
|
|
37
37
|
derivePoolAccounts,
|
|
38
38
|
detectTestnet,
|
|
@@ -54,12 +54,12 @@ import {
|
|
|
54
54
|
truncateAddress,
|
|
55
55
|
withDeploySpan,
|
|
56
56
|
withSpan
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-QFSPFKE2.js";
|
|
58
58
|
import {
|
|
59
59
|
DEFAULT_ENV_ID,
|
|
60
60
|
loadEnvironments,
|
|
61
61
|
resolveEndpoints
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-X3F7WHSF.js";
|
|
63
63
|
import {
|
|
64
64
|
NonRetryableError
|
|
65
65
|
} from "./chunk-ZOC4GITL.js";
|
|
@@ -74,7 +74,7 @@ import * as fs2 from "fs";
|
|
|
74
74
|
import * as path2 from "path";
|
|
75
75
|
import { execSync as execSync2 } from "child_process";
|
|
76
76
|
import { importer } from "ipfs-unixfs-importer";
|
|
77
|
-
import { CarReader } from "@ipld/car/reader";
|
|
77
|
+
import { CarReader as CarReader2 } from "@ipld/car/reader";
|
|
78
78
|
import { CarWriter } from "@ipld/car/writer";
|
|
79
79
|
import { CID as CID2 } from "multiformats/cid";
|
|
80
80
|
import * as dagPB2 from "@ipld/dag-pb";
|
|
@@ -101,6 +101,7 @@ import { cryptoWaitReady } from "@polkadot/util-crypto";
|
|
|
101
101
|
import { getPolkadotSigner } from "polkadot-api/signer";
|
|
102
102
|
import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
|
|
103
103
|
import { mnemonicToEntropy, entropyToMiniSecret, ss58Address } from "@polkadot-labs/hdkd-helpers";
|
|
104
|
+
import { CarReader } from "@ipld/car/reader";
|
|
104
105
|
function friendlyChainError(msg) {
|
|
105
106
|
if (/"type":\s*"Invalid"[\s\S]*?"type":\s*"Payment"/i.test(msg)) {
|
|
106
107
|
return "Bulletin quota exhausted (signed extension rejected the tx \u2014 signer is out of allowed txs or bytes; grant quota on-chain)";
|
|
@@ -406,6 +407,22 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
|
|
|
406
407
|
ss58 = provider.ss58;
|
|
407
408
|
ownsClient = true;
|
|
408
409
|
}
|
|
410
|
+
if (existingClient && reconnect) {
|
|
411
|
+
try {
|
|
412
|
+
await unsafeApi.query.System.Number.getValue();
|
|
413
|
+
} catch (e) {
|
|
414
|
+
if (isConnectionError(e)) {
|
|
415
|
+
const fresh = await reconnect();
|
|
416
|
+
client = fresh.client;
|
|
417
|
+
unsafeApi = fresh.unsafeApi;
|
|
418
|
+
signer = fresh.signer;
|
|
419
|
+
ss58 = fresh.ss58;
|
|
420
|
+
ownsClient = true;
|
|
421
|
+
} else {
|
|
422
|
+
throw e;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
409
426
|
const requiredTxs = BigInt(chunks.length + 1);
|
|
410
427
|
const requiredBytes = BigInt(totalBytes);
|
|
411
428
|
const [uploadAuth, currentBlockNum] = await Promise.all([
|
|
@@ -607,6 +624,9 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
|
|
|
607
624
|
}
|
|
608
625
|
}
|
|
609
626
|
if (!retried) {
|
|
627
|
+
if (isConnectionError(fail.error) && reconnectionsUsed >= MAX_RECONNECTIONS) {
|
|
628
|
+
throw new Error(`Connection lost and max reconnections (${MAX_RECONNECTIONS}) exhausted`);
|
|
629
|
+
}
|
|
610
630
|
throw new Error(`Chunk ${fail.index + 1} failed after ${MAX_CHUNK_RETRIES} retries: ${fail.error?.message?.slice(0, 100)}`);
|
|
611
631
|
}
|
|
612
632
|
}
|
|
@@ -699,7 +719,7 @@ async function storeChunkedContent(chunks, { client: existingClient, unsafeApi:
|
|
|
699
719
|
}
|
|
700
720
|
}
|
|
701
721
|
if (ownsClient) client.destroy();
|
|
702
|
-
return { storageCid: result, tier2Verified, tier2Inconclusive, tier2Fallback: fallbackStored.length };
|
|
722
|
+
return { storageCid: result, tier2Verified, tier2Inconclusive, tier2Fallback: fallbackStored.length, liveProvider: { client, unsafeApi, signer, ss58 } };
|
|
703
723
|
} catch (e) {
|
|
704
724
|
if (ownsClient) client.destroy();
|
|
705
725
|
throw e;
|
|
@@ -972,6 +992,7 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
|
|
|
972
992
|
totalSize: `${(phaseA.carBytes.length / 1024 / 1024).toFixed(2)} MB`
|
|
973
993
|
});
|
|
974
994
|
const carMbA = String(Math.round(phaseA.carBytes.length / 1024 / 1024 * 100) / 100);
|
|
995
|
+
let phaseALiveProvider = provider;
|
|
975
996
|
await withSpan("deploy.chunk-upload", "1b. chunk-upload (phase A)", {
|
|
976
997
|
"deploy.chunks.total": carChunksA.length,
|
|
977
998
|
"deploy.chunks.skipped": skipCids.size,
|
|
@@ -979,7 +1000,8 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
|
|
|
979
1000
|
"deploy.car.mb": carMbA
|
|
980
1001
|
}, async () => {
|
|
981
1002
|
sampleMemory("chunk_upload_start");
|
|
982
|
-
await storeChunkedContent(carChunksA, { ...provider, gateway, skipCids, probeFailedCids: probeFailedCidsA });
|
|
1003
|
+
const phaseAUpload = await storeChunkedContent(carChunksA, { ...provider, gateway, skipCids, probeFailedCids: probeFailedCidsA });
|
|
1004
|
+
phaseALiveProvider = { ...provider, ...phaseAUpload.liveProvider };
|
|
983
1005
|
sampleMemory("chunk_upload_end");
|
|
984
1006
|
});
|
|
985
1007
|
const phaseAKnownPresent = new Set(carChunkCidsA);
|
|
@@ -1013,6 +1035,9 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
|
|
|
1013
1035
|
blocks: blocksList,
|
|
1014
1036
|
chunks: chunksMap
|
|
1015
1037
|
});
|
|
1038
|
+
phaseA.blocks.clear();
|
|
1039
|
+
carChunksA.length = 0;
|
|
1040
|
+
phaseA.carBytes = new Uint8Array(0);
|
|
1016
1041
|
const phaseB = await withSpan("deploy.merkleize", "1c. merkleize (js, finalise)", { "deploy.directory": dirBasename }, async () => {
|
|
1017
1042
|
const r = await merkleizeWithStableOrder(directoryPath, phaseA.stableOrder, { useKubo });
|
|
1018
1043
|
sampleMemory("merkleize_finalise_end");
|
|
@@ -1038,7 +1063,7 @@ async function storeDirectoryV2(directoryPath, opts = {}) {
|
|
|
1038
1063
|
"deploy.car.mb": carMbB
|
|
1039
1064
|
}, async () => {
|
|
1040
1065
|
sampleMemory("chunk_upload_b_start");
|
|
1041
|
-
const r = await storeChunkedContent(carChunksB, { ...
|
|
1066
|
+
const r = await storeChunkedContent(carChunksB, { ...phaseALiveProvider, gateway, skipCids: skipCidsB, probeFailedCids: probeFailedCidsA });
|
|
1042
1067
|
sampleMemory("chunk_upload_b_end");
|
|
1043
1068
|
return r;
|
|
1044
1069
|
});
|
|
@@ -1175,7 +1200,8 @@ async function deploy(content, domainName = null, options = {}) {
|
|
|
1175
1200
|
console.log("=".repeat(60));
|
|
1176
1201
|
console.log(` Domain: ${name}.dot`);
|
|
1177
1202
|
if (deployTag) console.log(` Tag: ${deployTag}`);
|
|
1178
|
-
if (
|
|
1203
|
+
if (options.inputCar) console.log(` Input CAR: ${path.resolve(options.inputCar)}`);
|
|
1204
|
+
else if (typeof content === "string") console.log(` Build dir: ${path.resolve(content)}`);
|
|
1179
1205
|
if (process.env.CI) console.log(` Runner: ${resolveRunner()} (${resolveRunnerType()})`);
|
|
1180
1206
|
if (options.password) console.log(` Encrypted: yes`);
|
|
1181
1207
|
let provider;
|
|
@@ -1243,7 +1269,38 @@ async function deploy(content, domainName = null, options = {}) {
|
|
|
1243
1269
|
console.log("Storage");
|
|
1244
1270
|
console.log("=".repeat(60));
|
|
1245
1271
|
await withSpan("deploy.storage", "1. storage", {}, async () => {
|
|
1246
|
-
if (
|
|
1272
|
+
if (options.inputCar) {
|
|
1273
|
+
const carPath = path.resolve(options.inputCar);
|
|
1274
|
+
if (!fs.existsSync(carPath)) throw new Error(`CAR file not found: ${carPath}`);
|
|
1275
|
+
console.log(`
|
|
1276
|
+
Mode: Pre-built CAR`);
|
|
1277
|
+
console.log(` Path: ${carPath}`);
|
|
1278
|
+
let carContent = fs.readFileSync(carPath);
|
|
1279
|
+
console.log(` Size: ${(carContent.length / 1024 / 1024).toFixed(2)} MB`);
|
|
1280
|
+
const reader = await CarReader.fromBytes(carContent);
|
|
1281
|
+
const roots = await reader.getRoots();
|
|
1282
|
+
if (roots.length === 0) throw new Error("CAR file has no roots");
|
|
1283
|
+
ipfsCid = roots[0].toString();
|
|
1284
|
+
console.log(` Root CID: ${ipfsCid}`);
|
|
1285
|
+
if (options.password) {
|
|
1286
|
+
console.log(` Encrypting CAR file...`);
|
|
1287
|
+
carContent = await encryptContent(carContent, options.password);
|
|
1288
|
+
console.log(` Encrypted: ${(carContent.length / 1024 / 1024).toFixed(2)} MB`);
|
|
1289
|
+
}
|
|
1290
|
+
const carChunks = chunk(carContent, CHUNK_SIZE);
|
|
1291
|
+
const predictedStorageCid = computeStorageCid(carChunks);
|
|
1292
|
+
if (options.ghPagesMirror) {
|
|
1293
|
+
mirrorPromise = mirrorToGitHubPages({
|
|
1294
|
+
domain: name,
|
|
1295
|
+
carBytes: carContent,
|
|
1296
|
+
cid: predictedStorageCid,
|
|
1297
|
+
toolVersion: VERSION,
|
|
1298
|
+
bulletinRpc: BULLETIN_ENDPOINTS[0],
|
|
1299
|
+
encrypted: Boolean(options.password)
|
|
1300
|
+
}).catch((err) => err instanceof Error ? err : new Error(String(err)));
|
|
1301
|
+
}
|
|
1302
|
+
cid = (await storeChunkedContent(carChunks, providerWithReconnect)).storageCid;
|
|
1303
|
+
} else if (process.env.IPFS_CID) {
|
|
1247
1304
|
if (options.password) {
|
|
1248
1305
|
throw new Error(
|
|
1249
1306
|
"IPFS_CID and --password are mutually exclusive: IPFS_CID skips the upload step, so there is nothing to encrypt. Either unset IPFS_CID to upload and encrypt fresh content, or remove --password to reuse the existing CID as-is."
|
|
@@ -1591,7 +1648,7 @@ async function merkleizeKuboBackend(directoryPath) {
|
|
|
1591
1648
|
).trim();
|
|
1592
1649
|
execSync2(`ipfs dag export ${cidStr} > "${carPath}"`);
|
|
1593
1650
|
const carBytes = fs2.readFileSync(carPath);
|
|
1594
|
-
const reader = await
|
|
1651
|
+
const reader = await CarReader2.fromBytes(carBytes);
|
|
1595
1652
|
const blocks = /* @__PURE__ */ new Map();
|
|
1596
1653
|
for await (const { cid, bytes } of reader.blocks()) {
|
|
1597
1654
|
blocks.set(cid.toString(), bytes);
|
package/dist/chunk-probe.js
CHANGED
|
@@ -5,9 +5,9 @@ import {
|
|
|
5
5
|
_decodeStorageValue,
|
|
6
6
|
_resetProbeSession,
|
|
7
7
|
probeChunks
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-VTVRSJLE.js";
|
|
9
|
+
import "./chunk-QFSPFKE2.js";
|
|
10
|
+
import "./chunk-ZKHTRUD7.js";
|
|
11
11
|
import "./chunk-QGM4M3NI.js";
|
|
12
12
|
export {
|
|
13
13
|
ChainProbeCrossValidationError,
|
package/dist/deploy.d.ts
CHANGED
|
@@ -68,6 +68,7 @@ declare function storeChunkedContent(chunks: Uint8Array[], { client: existingCli
|
|
|
68
68
|
tier2Verified: number;
|
|
69
69
|
tier2Inconclusive: number;
|
|
70
70
|
tier2Fallback: number;
|
|
71
|
+
liveProvider: ExistingProvider;
|
|
71
72
|
}>;
|
|
72
73
|
declare function chunk(data: Uint8Array, size?: number): Uint8Array[];
|
|
73
74
|
declare function hasIPFS(): boolean;
|
|
@@ -170,6 +171,13 @@ interface DeployOptions {
|
|
|
170
171
|
description?: string;
|
|
171
172
|
/** Skip the 500 MiB abort guard and allow oversized deploys. */
|
|
172
173
|
allowLargeDeploy?: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* Filesystem path to a pre-built `.car` file. When set, skips directory
|
|
176
|
+
* scanning and merkleization; the CAR bytes are read from disk, the root
|
|
177
|
+
* CID is parsed from the CAR header, and the file is uploaded directly.
|
|
178
|
+
* The positional `<build-dir>` argument is not required when this is set.
|
|
179
|
+
*/
|
|
180
|
+
inputCar?: string;
|
|
173
181
|
/**
|
|
174
182
|
* Pin the `deployedAt` timestamp for byte-identical rebuilds.
|
|
175
183
|
* Values: "commit" (git committer date), "epoch:<N>" (Unix epoch seconds),
|
package/dist/deploy.js
CHANGED
|
@@ -32,20 +32,20 @@ import {
|
|
|
32
32
|
storeDirectory,
|
|
33
33
|
storeDirectoryV2,
|
|
34
34
|
storeFile
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-ZLFQ2MK4.js";
|
|
36
36
|
import "./chunk-MJTQOXBC.js";
|
|
37
37
|
import "./chunk-KOSF5FDO.js";
|
|
38
38
|
import "./chunk-5MRZ3V4A.js";
|
|
39
39
|
import "./chunk-S7EM5VMW.js";
|
|
40
|
-
import "./chunk-
|
|
41
|
-
import "./chunk-
|
|
42
|
-
import "./chunk-
|
|
40
|
+
import "./chunk-XNVUAMAJ.js";
|
|
41
|
+
import "./chunk-H7J5Y73C.js";
|
|
42
|
+
import "./chunk-VTVRSJLE.js";
|
|
43
43
|
import "./chunk-C2TS5MER.js";
|
|
44
|
-
import "./chunk-
|
|
44
|
+
import "./chunk-FACUN37U.js";
|
|
45
45
|
import "./chunk-VOEFHED3.js";
|
|
46
|
-
import "./chunk-
|
|
47
|
-
import "./chunk-
|
|
48
|
-
import "./chunk-
|
|
46
|
+
import "./chunk-QFSPFKE2.js";
|
|
47
|
+
import "./chunk-ZKHTRUD7.js";
|
|
48
|
+
import "./chunk-X3F7WHSF.js";
|
|
49
49
|
import {
|
|
50
50
|
EXIT_CODE_NO_RETRY,
|
|
51
51
|
NonRetryableError
|
package/dist/dotns.d.ts
CHANGED
|
@@ -122,6 +122,7 @@ declare function sanitizeDomainLabel(label: string): string;
|
|
|
122
122
|
declare function validateDomainLabel(label: string): string;
|
|
123
123
|
declare function isCommitmentMature(chainNowSeconds: number, commitTimestampSeconds: number, minimumAgeSeconds: number): boolean;
|
|
124
124
|
declare function isExplicitCommitmentBuffer(envValue: string | undefined): boolean;
|
|
125
|
+
declare function isLikelyCommitmentRace(msg: string): boolean;
|
|
125
126
|
declare function classifyDotnsLabel(label: string): {
|
|
126
127
|
status: number;
|
|
127
128
|
message: string;
|
|
@@ -233,7 +234,7 @@ declare class DotNS {
|
|
|
233
234
|
register(label: string, options?: DotNSConnectOptions & {
|
|
234
235
|
status?: string;
|
|
235
236
|
reverse?: boolean;
|
|
236
|
-
}): Promise<{
|
|
237
|
+
}, _cli?: (argv: string[], env?: Record<string, string>) => Promise<unknown>): Promise<{
|
|
237
238
|
label: string;
|
|
238
239
|
owner: string;
|
|
239
240
|
}>;
|
|
@@ -241,4 +242,4 @@ declare class DotNS {
|
|
|
241
242
|
}
|
|
242
243
|
declare const dotns: DotNS;
|
|
243
244
|
|
|
244
|
-
export { CONNECTION_TIMEOUT_MS, CONTRACTS, DECIMALS, DEFAULT_MNEMONIC, DOTNS_TX_MAX_ATTEMPTS, DOT_NODE, DotNS, type DotNSConnectOptions, type DotnsCliInvocation, type DotnsCliInvocationSource, type DotnsPreflightResult, type DotnsSuccessAction, NATIVE_TO_ETH_RATIO, OPERATION_TIMEOUT_MS, type OwnershipResult, type ParsedDomainName, type PriceValidationResult, ProofOfPersonhoodStatus, RPC_ENDPOINTS, TX_CHAIN_TIME_BUDGET_MS, TX_TIMEOUT_MS, TX_WALL_CLOCK_CEILING_MS, WS_HEARTBEAT_TIMEOUT_MS, canRegister, classifyDotnsLabel, classifyTxRetryDecision, computeDomainTokenId, convertWeiToNative, countTrailingDigits, dotns, feeFloorFor, fetchNonce, fmtPas, isCommitmentMature, isExplicitCommitmentBuffer, parseDomainName, parseProofOfPersonhoodStatus, popStatusName, resolveDotnsCliInvocation, runDotnsCli, sanitizeDomainLabel, simulateUserStatus, stripTrailingDigits, validateDomainLabel, verifyNonceAdvanced };
|
|
245
|
+
export { CONNECTION_TIMEOUT_MS, CONTRACTS, DECIMALS, DEFAULT_MNEMONIC, DOTNS_TX_MAX_ATTEMPTS, DOT_NODE, DotNS, type DotNSConnectOptions, type DotnsCliInvocation, type DotnsCliInvocationSource, type DotnsPreflightResult, type DotnsSuccessAction, NATIVE_TO_ETH_RATIO, OPERATION_TIMEOUT_MS, type OwnershipResult, type ParsedDomainName, type PriceValidationResult, ProofOfPersonhoodStatus, RPC_ENDPOINTS, TX_CHAIN_TIME_BUDGET_MS, TX_TIMEOUT_MS, TX_WALL_CLOCK_CEILING_MS, WS_HEARTBEAT_TIMEOUT_MS, canRegister, classifyDotnsLabel, classifyTxRetryDecision, computeDomainTokenId, convertWeiToNative, countTrailingDigits, dotns, feeFloorFor, fetchNonce, fmtPas, isCommitmentMature, isExplicitCommitmentBuffer, isLikelyCommitmentRace, parseDomainName, parseProofOfPersonhoodStatus, popStatusName, resolveDotnsCliInvocation, runDotnsCli, sanitizeDomainLabel, simulateUserStatus, stripTrailingDigits, validateDomainLabel, verifyNonceAdvanced };
|
package/dist/dotns.js
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
fmtPas,
|
|
27
27
|
isCommitmentMature,
|
|
28
28
|
isExplicitCommitmentBuffer,
|
|
29
|
+
isLikelyCommitmentRace,
|
|
29
30
|
parseDomainName,
|
|
30
31
|
parseProofOfPersonhoodStatus,
|
|
31
32
|
popStatusName,
|
|
@@ -36,10 +37,10 @@ import {
|
|
|
36
37
|
stripTrailingDigits,
|
|
37
38
|
validateDomainLabel,
|
|
38
39
|
verifyNonceAdvanced
|
|
39
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-FACUN37U.js";
|
|
40
41
|
import "./chunk-VOEFHED3.js";
|
|
41
|
-
import "./chunk-
|
|
42
|
-
import "./chunk-
|
|
42
|
+
import "./chunk-QFSPFKE2.js";
|
|
43
|
+
import "./chunk-ZKHTRUD7.js";
|
|
43
44
|
import "./chunk-QGM4M3NI.js";
|
|
44
45
|
export {
|
|
45
46
|
CONNECTION_TIMEOUT_MS,
|
|
@@ -69,6 +70,7 @@ export {
|
|
|
69
70
|
fmtPas,
|
|
70
71
|
isCommitmentMature,
|
|
71
72
|
isExplicitCommitmentBuffer,
|
|
73
|
+
isLikelyCommitmentRace,
|
|
72
74
|
parseDomainName,
|
|
73
75
|
parseProofOfPersonhoodStatus,
|
|
74
76
|
popStatusName,
|
package/dist/environments.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const DEFAULT_ENVIRONMENTS_URL = "https://raw.githubusercontent.com/paritytech/
|
|
1
|
+
declare const DEFAULT_ENVIRONMENTS_URL = "https://raw.githubusercontent.com/paritytech/triangle-deploy/main/assets/environments.json";
|
|
2
2
|
declare const DEFAULT_ENV_ID = "paseo-next";
|
|
3
3
|
declare const CACHE_TTL_MS: number;
|
|
4
4
|
declare const FETCH_TIMEOUT_MS = 5000;
|
package/dist/environments.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
deploy,
|
|
3
3
|
merkleizeJS,
|
|
4
4
|
merkleizeWithStableOrder
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ZLFQ2MK4.js";
|
|
6
6
|
import {
|
|
7
7
|
computeStats,
|
|
8
8
|
renderSummary,
|
|
@@ -24,16 +24,16 @@ import {
|
|
|
24
24
|
isVolatilePath,
|
|
25
25
|
parseManifest
|
|
26
26
|
} from "./chunk-S7EM5VMW.js";
|
|
27
|
-
import "./chunk-
|
|
28
|
-
import "./chunk-
|
|
27
|
+
import "./chunk-XNVUAMAJ.js";
|
|
28
|
+
import "./chunk-H7J5Y73C.js";
|
|
29
29
|
import {
|
|
30
30
|
probeChunks
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-VTVRSJLE.js";
|
|
32
32
|
import "./chunk-C2TS5MER.js";
|
|
33
33
|
import {
|
|
34
34
|
DotNS,
|
|
35
35
|
parseDomainName
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-FACUN37U.js";
|
|
37
37
|
import {
|
|
38
38
|
bootstrapPool,
|
|
39
39
|
derivePoolAccounts,
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
fetchPoolAuthorizations,
|
|
42
42
|
selectAccount
|
|
43
43
|
} from "./chunk-VOEFHED3.js";
|
|
44
|
-
import "./chunk-
|
|
44
|
+
import "./chunk-QFSPFKE2.js";
|
|
45
45
|
import {
|
|
46
46
|
VERSION,
|
|
47
47
|
loadRunState,
|
|
@@ -51,8 +51,8 @@ import {
|
|
|
51
51
|
shouldSkipStaleWarning,
|
|
52
52
|
stateFilePath,
|
|
53
53
|
writeRunState
|
|
54
|
-
} from "./chunk-
|
|
55
|
-
import "./chunk-
|
|
54
|
+
} from "./chunk-ZKHTRUD7.js";
|
|
55
|
+
import "./chunk-X3F7WHSF.js";
|
|
56
56
|
import "./chunk-ZOC4GITL.js";
|
|
57
57
|
import "./chunk-HOTQDYHD.js";
|
|
58
58
|
import "./chunk-QGM4M3NI.js";
|
package/dist/memory-report.d.ts
CHANGED
package/dist/memory-report.js
CHANGED
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
maybeWriteMemoryReport,
|
|
6
6
|
safeHeap,
|
|
7
7
|
sampleFromBytes
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-QFSPFKE2.js";
|
|
9
|
+
import "./chunk-ZKHTRUD7.js";
|
|
10
10
|
import "./chunk-QGM4M3NI.js";
|
|
11
11
|
export {
|
|
12
12
|
DEFAULT_THRESHOLD_MB,
|
package/dist/merkle.js
CHANGED
|
@@ -5,20 +5,20 @@ import {
|
|
|
5
5
|
merkleizeJSBackend,
|
|
6
6
|
merkleizeKuboBackend,
|
|
7
7
|
merkleizeWithStableOrder
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ZLFQ2MK4.js";
|
|
9
9
|
import "./chunk-MJTQOXBC.js";
|
|
10
10
|
import "./chunk-KOSF5FDO.js";
|
|
11
11
|
import "./chunk-5MRZ3V4A.js";
|
|
12
12
|
import "./chunk-S7EM5VMW.js";
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-XNVUAMAJ.js";
|
|
14
|
+
import "./chunk-H7J5Y73C.js";
|
|
15
|
+
import "./chunk-VTVRSJLE.js";
|
|
16
16
|
import "./chunk-C2TS5MER.js";
|
|
17
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-FACUN37U.js";
|
|
18
18
|
import "./chunk-VOEFHED3.js";
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-
|
|
21
|
-
import "./chunk-
|
|
19
|
+
import "./chunk-QFSPFKE2.js";
|
|
20
|
+
import "./chunk-ZKHTRUD7.js";
|
|
21
|
+
import "./chunk-X3F7WHSF.js";
|
|
22
22
|
import "./chunk-ZOC4GITL.js";
|
|
23
23
|
import "./chunk-HOTQDYHD.js";
|
|
24
24
|
import "./chunk-QGM4M3NI.js";
|
package/dist/run-state.js
CHANGED
package/dist/telemetry.js
CHANGED
package/dist/version-check.js
CHANGED
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
isPreReleaseVersion,
|
|
9
9
|
preReleaseWarning,
|
|
10
10
|
promptYesNo
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-H7J5Y73C.js";
|
|
12
|
+
import "./chunk-QFSPFKE2.js";
|
|
13
|
+
import "./chunk-ZKHTRUD7.js";
|
|
14
14
|
import "./chunk-QGM4M3NI.js";
|
|
15
15
|
export {
|
|
16
16
|
assessVersion,
|
package/docs/telemetry.md
CHANGED
|
@@ -29,8 +29,11 @@ If another app embeds `bulletin-deploy` and already owns Sentry initialization,
|
|
|
29
29
|
```sh
|
|
30
30
|
BULLETIN_DEPLOY_USE_AMBIENT_SENTRY=1
|
|
31
31
|
BULLETIN_DEPLOY_HOST_APP=<your-app-name>
|
|
32
|
+
BULLETIN_DEPLOY_HOST_APP_VERSION=<your-app-version>
|
|
32
33
|
```
|
|
33
34
|
|
|
35
|
+
`BULLETIN_DEPLOY_HOST_APP_VERSION` is optional but recommended — it populates `deploy.host_app_version` on every span, enabling version-correlated triage in the dashboard.
|
|
36
|
+
|
|
34
37
|
That makes `bulletin-deploy` reuse the existing Sentry client instead of calling its own `Sentry.init()`.
|
|
35
38
|
|
|
36
39
|
Requirements:
|