bulletin-deploy 0.21.0 → 0.22.0-rc.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/bin/bulletin-deploy +12 -8
- package/dist/auth/index.js +4 -4
- package/dist/auth/vendor/index.js +5 -5
- package/dist/auth-config.js +3 -3
- package/dist/bug-report.js +4 -4
- package/dist/chain-health.js +3 -3
- package/dist/chunk-6DDB5QOM.js +40 -0
- package/dist/{chunk-4FIMYLWV.js → chunk-7JM6FJM3.js} +162 -45
- package/dist/{chunk-K57H4IRT.js → chunk-C75KFENW.js} +1 -1
- package/dist/{chunk-CQA3JPYX.js → chunk-CFSMENKV.js} +3 -3
- package/dist/{chunk-WYZUDE6I.js → chunk-HE6CD5SW.js} +2 -2
- package/dist/{chunk-BDUL42WH.js → chunk-L6TCHNO6.js} +130 -1
- package/dist/{chunk-IOLWREZB.js → chunk-PR52KVWQ.js} +174 -40
- package/dist/{chunk-GLLLSFMA.js → chunk-PY4R353E.js} +3 -3
- package/dist/{chunk-KOLWOUWP.js → chunk-PZA33HNQ.js} +1 -1
- package/dist/{chunk-LPG3QSOM.js → chunk-S24T3JSD.js} +4 -4
- package/dist/{chunk-UPENVZ7J.js → chunk-UWTR5Z27.js} +1 -1
- package/dist/{chunk-E77HESGN.js → chunk-V2UID6F6.js} +1 -1
- package/dist/{chunk-7QQZ57E6.js → chunk-WM4BQ7YJ.js} +94 -4
- package/dist/{chunk-EWQ2QZ44.js → chunk-YQF5Y2HS.js} +327 -128
- package/dist/{chunk-GL7JKE7O.js → chunk-ZTXHG3LV.js} +1 -1
- package/dist/chunk-probe.js +3 -3
- package/dist/commands/login.js +18 -17
- package/dist/commands/logout.js +5 -5
- package/dist/commands/transfer.js +6 -6
- package/dist/commands/whoami.js +3 -3
- package/dist/deploy-actors.js +8 -8
- package/dist/deploy.d.ts +71 -27
- package/dist/deploy.js +28 -17
- package/dist/dotns.d.ts +18 -5
- package/dist/dotns.js +6 -6
- package/dist/e2e-nonce-barrier.d.ts +24 -0
- package/dist/e2e-nonce-barrier.js +12 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +36 -27
- package/dist/manifest/publish.js +19 -18
- package/dist/memory-report.js +2 -2
- package/dist/merkle.js +18 -17
- package/dist/personhood/bootstrap.js +6 -6
- package/dist/personhood/people-client.js +6 -6
- package/dist/pool.d.ts +67 -1
- package/dist/pool.js +25 -7
- package/dist/run-state.d.ts +22 -2
- package/dist/run-state.js +9 -1
- package/dist/sss-allowance-cache.js +4 -4
- package/dist/storage-signer.js +18 -17
- package/dist/telemetry.d.ts +4 -2
- package/dist/telemetry.js +6 -2
- package/dist/version-check.js +3 -3
- package/dist/ws.js +4 -4
- package/package.json +1 -1
- package/tools/release-retry-wrapper.mjs +35 -4
package/bin/bulletin-deploy
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import "../dist/suppress-localstorage-warning.js";
|
|
8
8
|
|
|
9
9
|
import { deploy, DEFAULT_BULLETIN_RPC, DEFAULT_POOL_SIZE, NonRetryableError, EXIT_CODE_NO_RETRY, isConnectionError, isBenignTeardownError, resolveEffectiveMnemonic, mnemonicConflictNotice, resolveEnvId, shouldPublishManifest, nonInteractivePhoneConfirmationError, printDeploymentCompleteBanner } from "../dist/deploy.js";
|
|
10
|
-
import { VERSION, setDeployAttribute, captureWarning, closeTelemetry, setRunStateActive, markRelaunchOomHintShown, setVersionFreshness } from "../dist/telemetry.js";
|
|
10
|
+
import { VERSION, setDeployAttribute, captureWarning, closeTelemetry, markDeployFatal, setRunStateActive, markRelaunchOomHintShown, setVersionFreshness } from "../dist/telemetry.js";
|
|
11
11
|
import { handleFailedDeploy, handlePreflightVersionCheck, fetchVersionInfo, preReleaseWarning, checkNodeVersion, classifyVersionFreshness, isInteractive } from "../dist/version-check.js";
|
|
12
12
|
import { setDeployContext, installLogCapture, buildCliFlagsSummary } from "../dist/bug-report.js";
|
|
13
13
|
import { loadRunState, writeRunState, shouldSkipStaleWarning, shouldShowOomHint, probablyOomRssMb } from "../dist/run-state.js";
|
|
@@ -355,9 +355,6 @@ if (!flags.help && !flags.version) {
|
|
|
355
355
|
if (finalizing) return;
|
|
356
356
|
finalizing = true;
|
|
357
357
|
try {
|
|
358
|
-
setDeployAttribute("deploy.status", "killed");
|
|
359
|
-
setDeployAttribute("deploy.killed", reason);
|
|
360
|
-
setDeployAttribute("deploy.sad", "true");
|
|
361
358
|
captureWarning(`deploy process terminated: ${reason}`);
|
|
362
359
|
} catch { /* telemetry best-effort */ }
|
|
363
360
|
try { await closeTelemetry(1000); } catch { /* ignore */ }
|
|
@@ -390,7 +387,7 @@ if (!flags.help && !flags.version) {
|
|
|
390
387
|
} catch { /* telemetry best-effort */ }
|
|
391
388
|
return;
|
|
392
389
|
}
|
|
393
|
-
try {
|
|
390
|
+
try { markDeployFatal(e); } catch {}
|
|
394
391
|
finalize(kind, 2);
|
|
395
392
|
};
|
|
396
393
|
process.on("uncaughtException", (e) => handleUnhandled(e, "uncaught"));
|
|
@@ -400,9 +397,16 @@ if (!flags.help && !flags.version) {
|
|
|
400
397
|
// and are retryable from the consumer's perspective. Contrast with
|
|
401
398
|
// NonRetryableError exits (code 78, POSIX EX_CONFIG) which callers must
|
|
402
399
|
// NOT retry — see EXIT_CODE_NO_RETRY in src/errors.ts.
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
400
|
+
const onSignal = (sig, exitCode) => {
|
|
401
|
+
try {
|
|
402
|
+
setDeployAttribute("deploy.status", "killed");
|
|
403
|
+
setDeployAttribute("deploy.killed", sig);
|
|
404
|
+
} catch { /* telemetry best-effort */ }
|
|
405
|
+
finalize(sig, exitCode);
|
|
406
|
+
};
|
|
407
|
+
process.on("SIGINT", () => onSignal("SIGINT", 130));
|
|
408
|
+
process.on("SIGTERM", () => onSignal("SIGTERM", 143));
|
|
409
|
+
process.on("SIGHUP", () => onSignal("SIGHUP", 129));
|
|
406
410
|
}
|
|
407
411
|
|
|
408
412
|
try {
|
package/dist/auth/index.js
CHANGED
|
@@ -8,6 +8,10 @@ import {
|
|
|
8
8
|
renderLogoutStatus
|
|
9
9
|
} from "../chunk-YP3LINCF.js";
|
|
10
10
|
import "../chunk-NJKXXDDK.js";
|
|
11
|
+
import {
|
|
12
|
+
SignerNotAvailableError,
|
|
13
|
+
resolveSigner
|
|
14
|
+
} from "../chunk-2C6SLLBI.js";
|
|
11
15
|
import {
|
|
12
16
|
createAuthClient
|
|
13
17
|
} from "../chunk-PN5JKONF.js";
|
|
@@ -21,10 +25,6 @@ import {
|
|
|
21
25
|
import {
|
|
22
26
|
SESSION_EXPIRED_MESSAGE
|
|
23
27
|
} from "../chunk-IMCDXJ4G.js";
|
|
24
|
-
import {
|
|
25
|
-
SignerNotAvailableError,
|
|
26
|
-
resolveSigner
|
|
27
|
-
} from "../chunk-2C6SLLBI.js";
|
|
28
28
|
import "../chunk-SH2LG52Z.js";
|
|
29
29
|
import "../chunk-DNXE3Q5D.js";
|
|
30
30
|
export {
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import "../../chunk-NJKXXDDK.js";
|
|
2
|
+
import {
|
|
3
|
+
SignerNotAvailableError,
|
|
4
|
+
parseDevAccountName,
|
|
5
|
+
resolveSigner
|
|
6
|
+
} from "../../chunk-2C6SLLBI.js";
|
|
2
7
|
import {
|
|
3
8
|
createAuthClient
|
|
4
9
|
} from "../../chunk-PN5JKONF.js";
|
|
@@ -16,11 +21,6 @@ import {
|
|
|
16
21
|
deriveProductPublicKey,
|
|
17
22
|
sessionRootPublicKey
|
|
18
23
|
} from "../../chunk-IMCDXJ4G.js";
|
|
19
|
-
import {
|
|
20
|
-
SignerNotAvailableError,
|
|
21
|
-
parseDevAccountName,
|
|
22
|
-
resolveSigner
|
|
23
|
-
} from "../../chunk-2C6SLLBI.js";
|
|
24
24
|
import "../../chunk-SH2LG52Z.js";
|
|
25
25
|
import "../../chunk-DNXE3Q5D.js";
|
|
26
26
|
export {
|
package/dist/auth-config.js
CHANGED
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
hasPersistedSession,
|
|
11
11
|
readNetworkSuffix,
|
|
12
12
|
resolveBulletinEndpoints
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-ZTXHG3LV.js";
|
|
14
14
|
import "./chunk-3NR3T7US.js";
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-7JM6FJM3.js";
|
|
16
|
+
import "./chunk-WM4BQ7YJ.js";
|
|
17
17
|
import "./chunk-SH2LG52Z.js";
|
|
18
18
|
import "./chunk-DNXE3Q5D.js";
|
|
19
19
|
export {
|
package/dist/bug-report.js
CHANGED
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
offerBugReport,
|
|
11
11
|
scrubSecrets,
|
|
12
12
|
setDeployContext
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-PY4R353E.js";
|
|
14
|
+
import "./chunk-PZA33HNQ.js";
|
|
15
|
+
import "./chunk-7JM6FJM3.js";
|
|
16
|
+
import "./chunk-WM4BQ7YJ.js";
|
|
17
17
|
import "./chunk-SH2LG52Z.js";
|
|
18
18
|
export {
|
|
19
19
|
buildCliFlagsSummary,
|
package/dist/chain-health.js
CHANGED
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
DEFAULT_WINDOW_BLOCKS,
|
|
4
4
|
MAX_SPANS_PER_SAMPLER,
|
|
5
5
|
endpointHost
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-UWTR5Z27.js";
|
|
7
|
+
import "./chunk-7JM6FJM3.js";
|
|
8
|
+
import "./chunk-WM4BQ7YJ.js";
|
|
9
9
|
import "./chunk-SH2LG52Z.js";
|
|
10
10
|
export {
|
|
11
11
|
ChainHealthSampler,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// src/e2e-nonce-barrier.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
var NONCE_SEED_BARRIER_ENV = "BULLETIN_DEPLOY_E2E_NONCE_SEED_BARRIER";
|
|
5
|
+
async function awaitNonceSeedBarrier(opts) {
|
|
6
|
+
const parties = opts.parties ?? 2;
|
|
7
|
+
const deadline = Date.now() + (opts.timeoutMs ?? 12e4);
|
|
8
|
+
const pollMs = opts.pollMs ?? 200;
|
|
9
|
+
fs.mkdirSync(opts.dir, { recursive: true });
|
|
10
|
+
fs.writeFileSync(path.join(opts.dir, `${opts.id ?? process.pid}.seed`), String(opts.seed));
|
|
11
|
+
const seedFiles = () => fs.readdirSync(opts.dir).filter((f) => f.endsWith(".seed"));
|
|
12
|
+
const readSeeds = (files) => files.map((f) => Number(fs.readFileSync(path.join(opts.dir, f), "utf8"))).sort((a, b) => a - b);
|
|
13
|
+
for (; ; ) {
|
|
14
|
+
const files = seedFiles();
|
|
15
|
+
if (files.length >= parties) return { met: true, seeds: readSeeds(files) };
|
|
16
|
+
if (Date.now() >= deadline) return { met: false, seeds: readSeeds(files) };
|
|
17
|
+
await new Promise((r) => setTimeout(r, pollMs));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
var used = false;
|
|
21
|
+
async function e2eNonceSeedBarrier(seed, env = process.env) {
|
|
22
|
+
const dir = env[NONCE_SEED_BARRIER_ENV];
|
|
23
|
+
if (!dir || used) return;
|
|
24
|
+
used = true;
|
|
25
|
+
const parties = Number(env[`${NONCE_SEED_BARRIER_ENV}_PARTIES`] || 2);
|
|
26
|
+
const timeoutMs = Number(env[`${NONCE_SEED_BARRIER_ENV}_TIMEOUT_MS`] || 12e4);
|
|
27
|
+
console.log(` E2E nonce-seed barrier: seed ${seed}, waiting for ${parties} parties`);
|
|
28
|
+
const r = await awaitNonceSeedBarrier({ dir, seed, parties, timeoutMs });
|
|
29
|
+
console.log(r.met ? ` E2E nonce-seed barrier met: seeds [${r.seeds.join(", ")}]` : ` E2E nonce-seed barrier timed out after ${timeoutMs}ms: seeds [${r.seeds.join(", ")}]`);
|
|
30
|
+
}
|
|
31
|
+
function __resetNonceSeedBarrierForTest() {
|
|
32
|
+
used = false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
NONCE_SEED_BARRIER_ENV,
|
|
37
|
+
awaitNonceSeedBarrier,
|
|
38
|
+
e2eNonceSeedBarrier,
|
|
39
|
+
__resetNonceSeedBarrierForTest
|
|
40
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
package_default,
|
|
3
3
|
writeRunState
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WM4BQ7YJ.js";
|
|
5
5
|
import {
|
|
6
6
|
FALLBACK_CONFIG_ORIGIN,
|
|
7
7
|
SHIPPED_CONFIG_ORIGIN
|
|
@@ -131,6 +131,11 @@ function setVersionFreshness(status) {
|
|
|
131
131
|
versionFreshness = status;
|
|
132
132
|
}
|
|
133
133
|
async function closeTelemetry(timeoutMs) {
|
|
134
|
+
if (deployRootSpan) {
|
|
135
|
+
if (deployRootSpan === deploySpan) deployRootSpan.setAttribute("deploy.outcome", computeDeployOutcome(currentErrorCategory, currentDeploySad, currentSadReason));
|
|
136
|
+
deployRootSpan.end();
|
|
137
|
+
deployRootSpan = null;
|
|
138
|
+
}
|
|
134
139
|
if (!Sentry) return;
|
|
135
140
|
try {
|
|
136
141
|
await Sentry.close(timeoutMs);
|
|
@@ -152,6 +157,9 @@ function initTelemetry() {
|
|
|
152
157
|
// Sentry Node SDK captures os.hostname() by default, which leaks personal
|
|
153
158
|
// machine names (e.g. "Mac.fritz.box"). Override to something anonymous.
|
|
154
159
|
serverName: process.env.CI ? process.env.RUNNER_NAME ?? "ci" : "local",
|
|
160
|
+
// Library mode registers no uncaughtException listener, so Sentry's fatal
|
|
161
|
+
// handler is what exits the process; report the deploy as failed first.
|
|
162
|
+
integrations: [Sentry.onUncaughtExceptionIntegration({ onFatalError: exitOnFatalError })],
|
|
155
163
|
beforeSend(event) {
|
|
156
164
|
if (event.server_name) event.server_name = process.env.CI ? process.env.RUNNER_NAME ?? "ci" : "local";
|
|
157
165
|
if (event.message) event.message = scrubPaths(event.message);
|
|
@@ -284,6 +292,9 @@ var DEPLOY_SEED_POOL = {
|
|
|
284
292
|
"deploy.pool.nonce_collision_missing": 0,
|
|
285
293
|
"deploy.pool.nonce_collision_reupload_count": 0
|
|
286
294
|
};
|
|
295
|
+
var DEPLOY_SEED_POOL_HEALTH = {
|
|
296
|
+
"deploy.pool.stuck_skipped": "none"
|
|
297
|
+
};
|
|
287
298
|
var DEPLOY_SEED_MANIFEST = {
|
|
288
299
|
"deploy.manifest.fetch_source": "none",
|
|
289
300
|
"deploy.manifest.fetch_attempts": "0",
|
|
@@ -327,6 +338,7 @@ function getDeployAttributes(domain) {
|
|
|
327
338
|
...DEPLOY_SEED_STORAGE,
|
|
328
339
|
...DEPLOY_SEED_PROBE,
|
|
329
340
|
...DEPLOY_SEED_POOL,
|
|
341
|
+
...DEPLOY_SEED_POOL_HEALTH,
|
|
330
342
|
...DEPLOY_SEED_MANIFEST,
|
|
331
343
|
...DEPLOY_SEED_BULLETIN_UPLOAD,
|
|
332
344
|
...DEPLOY_SEED_BULLETIN_AUTH,
|
|
@@ -340,13 +352,22 @@ function getDeployAttributes(domain) {
|
|
|
340
352
|
"deploy.tool_version": VERSION,
|
|
341
353
|
"deploy.runner": resolveRunner(),
|
|
342
354
|
"deploy.runner_type": resolveRunnerType(),
|
|
355
|
+
// #1646: the telemetry verifier scopes its Sentry query to the current CI run
|
|
356
|
+
// with these (strings, present on every span: both-values rule).
|
|
357
|
+
"deploy.ci_run_id": process.env.GITHUB_RUN_ID || "none",
|
|
358
|
+
"deploy.ci_run_attempt": process.env.GITHUB_RUN_ATTEMPT || "none",
|
|
343
359
|
// Present on every span so a dashboard can compute count_if(deploy.version_status,
|
|
344
360
|
// "outdated") / count() — see classifyVersionFreshness in version-check.ts.
|
|
345
361
|
"deploy.version_status": versionFreshness,
|
|
346
362
|
// #1539: unconditional (both-values rule) so count_if(deploy.is_e2e_fixture,
|
|
347
363
|
// "true") / "false" both work — see isE2eFixtureTag above for the full
|
|
348
364
|
// rationale, and setDeployAttribute below for the options.tag/--tag path.
|
|
349
|
-
"deploy.is_e2e_fixture": String(isE2eFixtureTag(process.env.DEPLOY_TAG))
|
|
365
|
+
"deploy.is_e2e_fixture": String(isE2eFixtureTag(process.env.DEPLOY_TAG)),
|
|
366
|
+
// #1626: the E2E harness sets BULLETIN_E2E_EXPECT_FAIL=1 on a deploy it
|
|
367
|
+
// asserts will fail (S3 owned-elsewhere, governance-reserved, subdomain
|
|
368
|
+
// orphan…), so the E2E dashboard can exclude those deliberate error spans.
|
|
369
|
+
// A tag only: nothing else reads the var. Unconditional (both-values rule).
|
|
370
|
+
"deploy.expected_error": String(process.env.BULLETIN_E2E_EXPECT_FAIL === "1")
|
|
350
371
|
};
|
|
351
372
|
if (hostApp) attrs["deploy.host_app"] = hostApp;
|
|
352
373
|
const hostAppVersion = process.env.BULLETIN_DEPLOY_HOST_APP_VERSION;
|
|
@@ -369,23 +390,19 @@ function cameFromOurConfig(msg) {
|
|
|
369
390
|
const lower = msg.toLowerCase();
|
|
370
391
|
return lower.includes(`came from ${SHIPPED_CONFIG_ORIGIN.toLowerCase()}`) || lower.includes(`came from ${FALLBACK_CONFIG_ORIGIN.toLowerCase()}`);
|
|
371
392
|
}
|
|
372
|
-
|
|
393
|
+
function contractAddressCategory(msg) {
|
|
394
|
+
if (CONTRACT_ADDRESS_FAILURE.test(msg) && /this address came from/i.test(msg) && !cameFromOurConfig(msg)) return "user";
|
|
395
|
+
return /could not determine the DotNS ABI profile/i.test(msg) ? "environment" : "unknown";
|
|
396
|
+
}
|
|
397
|
+
function categoryOf(kind, msg) {
|
|
398
|
+
if (kind === "naming.contract_unavailable" || kind === "naming.contract_function_removed") return contractAddressCategory(msg);
|
|
399
|
+
return ERROR_KIND_CATEGORY[kind];
|
|
400
|
+
}
|
|
373
401
|
function isExpectedError(msg, variant) {
|
|
374
|
-
|
|
375
|
-
if (structuralCategory) return structuralCategory === "user";
|
|
376
|
-
if (GENERIC_USER_ERROR.test(msg)) return true;
|
|
377
|
-
if (CONTRACT_ADDRESS_FAILURE.test(msg)) {
|
|
378
|
-
return /this address came from/i.test(msg) && !cameFromOurConfig(msg);
|
|
379
|
-
}
|
|
380
|
-
return false;
|
|
402
|
+
return classifyDeployError(msg, variant) === "user";
|
|
381
403
|
}
|
|
382
404
|
function classifyDeployError(msg, variant) {
|
|
383
|
-
|
|
384
|
-
if (structuralCategory) return structuralCategory;
|
|
385
|
-
if (isExpectedError(msg)) return "user";
|
|
386
|
-
if (/chunk.*failed after.*retr|tx dropped from best chain|timed out after \d+s waiting for block|Contract reverted|Contract execution would revert|dotns register failed|All promises were rejected|"type"\s*:\s*"Invalid"|Commitment still too new|not finalised after \d+s|chain may have (dropped|evicted)|ReviveApi.*timed out|ReviveApi.*returned empty result|\b(?:commit|register|setSubnodeOwner|setResolver|setContenthash|setText|Revive\.call|Utility\.batch_all) timed out after \d+ms|transaction watcher silent for|could not determine the DotNS ABI profile/i.test(msg)) return "environment";
|
|
387
|
-
if (/javascript heap out of memory|allocation failed.*heap|External signer mode is not supported with dotns-cli/i.test(msg)) return "internal";
|
|
388
|
-
return "unknown";
|
|
405
|
+
return structuralCategoryOf(variant) ?? categoryOf(classifyErrorKind(msg, variant), msg);
|
|
389
406
|
}
|
|
390
407
|
function classifySadReason(message) {
|
|
391
408
|
if (/process terminated: SIG/i.test(message)) return "killed";
|
|
@@ -415,6 +432,16 @@ var ERROR_KIND_RULES = [
|
|
|
415
432
|
// (2026-08-06 telemetry sweep: this was the largest live `unknown` bucket —
|
|
416
433
|
// 31 spans, 30 of them env=preview, from the per-env authorizer gap #1209.)
|
|
417
434
|
[/is not authorized for Bulletin storage/i, "storage.rejected"],
|
|
435
|
+
// The three chunk-inclusion / stuck-queue messages (src/deploy.ts
|
|
436
|
+
// ChunkInclusionUnverifiedError, src/pool.ts stuckQueueMessage). Ahead of the
|
|
437
|
+
// generic infra rules: the unverified message interpolates the probe's failure
|
|
438
|
+
// reason, which routinely reads "timed out" / "websocket closed" and would be
|
|
439
|
+
// claimed by chain-timeout or connection; the stuck-queue message says "every
|
|
440
|
+
// chunk would time out", and both inclusion messages end up before the broader
|
|
441
|
+
// /chunk.*failed after.*retr/ and /not finalised after/ fallbacks.
|
|
442
|
+
[/Could not verify that chunk .+ is stored on-chain/i, "chain.chunk_unverified"],
|
|
443
|
+
[/still absent at the best block after \d+ re-upload rounds/i, "chain.chunk_not_included"],
|
|
444
|
+
[/has a stuck pending-tx queue/i, "chain.queue_stuck"],
|
|
418
445
|
// Revive surfaces the personhood gate as a bare revert reason rather than the
|
|
419
446
|
// "requires ProofOfPersonhoodX, but this signer is NoStatus" prose matched
|
|
420
447
|
// further down — same user-actionable cause, so it shares that kind. Ordered
|
|
@@ -450,8 +477,17 @@ var ERROR_KIND_RULES = [
|
|
|
450
477
|
// Ahead of the generic contract-revert rule: a soulbound refusal is a
|
|
451
478
|
// permanent naming fact, not a transient chain error.
|
|
452
479
|
[/is soulbound and cannot be transferred/i, "naming.soulbound"],
|
|
480
|
+
// Operator-fixable phrasings, above contract-revert so the actionable kind wins over the wrapper.
|
|
481
|
+
// formatUnregistrableReason's phrase (#1185); above the label rules since it can quote the PopError it explains.
|
|
482
|
+
[/cannot register it|base name is \d+ chars|reserved for original/i, "naming.governance_reserved"],
|
|
483
|
+
[/NameNotAvailable/i, "naming.name_unavailable"],
|
|
484
|
+
[/invalid domain label|name must be lowercase/i, "naming.invalid_label"],
|
|
485
|
+
[/insufficient balance|insufficient funds|signer balance .* < estimated fee/i, "funding.insufficient_balance"],
|
|
486
|
+
[/insufficient .* authorization/i, "storage.allowance_insufficient"],
|
|
487
|
+
[/bip39 mnemonic/i, "signer.invalid_mnemonic"],
|
|
488
|
+
[/ipfs cli not installed/i, "tool.ipfs_missing"],
|
|
453
489
|
[/Contract reverted|Contract execution would revert|revert(?:ed|ing)?\s*\(flags=[0-9]+\)|"type"\s*:\s*"ContractReverted"/i, "contract-revert"],
|
|
454
|
-
[/timed out after \d+s waiting for block|Transaction not included after \d+s|Transaction did not settle within|Commitment still too new
|
|
490
|
+
[/timed out after \d+s waiting for block|Transaction not included after \d+s|Transaction did not settle within|Commitment still too new/i, "chain-timeout"],
|
|
455
491
|
[/\bstale\b.*nonce|nonce.*\bstale\b|"type"\s*:\s*"(?:Future|Stale)"|Invalid::Future|tx rejected by pool/i, "nonce-stale"],
|
|
456
492
|
// Invalid::BadProof: the extrinsic's signature didn't verify (wrong genesis
|
|
457
493
|
// hash / mortality window / spec version, or a re-signed payload mismatch).
|
|
@@ -465,6 +501,8 @@ var ERROR_KIND_RULES = [
|
|
|
465
501
|
[/short names are not on sale on|Short names are not for sale/i, "naming.short_names_closed"],
|
|
466
502
|
[/requires ProofOfPersonhood(?:Full|Lite|Light),\s*but this signer is NoStatus/i, "naming.pop_required"],
|
|
467
503
|
[/requires NoStatus,\s*but this signer is ProofOfPersonhood/i, "naming.nostatus_required"],
|
|
504
|
+
// Other personhood phrasings ("Requires Full Personhood verification", "personhood check failed"): after both specific rules above so their kinds win.
|
|
505
|
+
[/personhood/i, "naming.pop_required"],
|
|
468
506
|
// #1345 (2026-09-01 previewnet TLD change, fixed in #1324): checkTldNodeConsistency's
|
|
469
507
|
// safety-net throw (src/dotns.ts). Was 100% of the 48-span 'unknown' majority in the
|
|
470
508
|
// 7d sweep — anchored on the stable prefix, never on the interpolated namehash values.
|
|
@@ -473,6 +511,11 @@ var ERROR_KIND_RULES = [
|
|
|
473
511
|
// Anchored on the stable "DotNS signer ... PAS free; needs ≥" shape, never on the
|
|
474
512
|
// interpolated address/amounts — those vary per signer and per fee floor.
|
|
475
513
|
[/DotNS signer .+ PAS free; needs ≥/i, "funding.insufficient_balance"],
|
|
514
|
+
// Revive's TransferFailed module error, raised by register / finalize-registration
|
|
515
|
+
// (formatDispatchError JSON) when concurrent deploys on one shared signer spent its
|
|
516
|
+
// balance after the preflight check passed. Matches the variant name alone: the
|
|
517
|
+
// enclosing JSON can be cut by truncation, the innermost variant is the tail.
|
|
518
|
+
[/"type"\s*:\s*"TransferFailed"/i, "funding.transfer_failed"],
|
|
476
519
|
[/Cannot decode zero data.*with ABI parameters/i, "dotns.abi_decode_empty"],
|
|
477
520
|
// Issue #1060: contractCall's own empty-`0x`-data guard (src/dotns.ts, #729) throws
|
|
478
521
|
// an actionable wrapper instead of letting the raw viem message above reach a
|
|
@@ -512,33 +555,34 @@ var ERROR_KIND_RULES = [
|
|
|
512
555
|
// coming back with empty data. Genuinely a missing-contract config problem,
|
|
513
556
|
// not a removed-function one — stays naming.contract_unavailable.
|
|
514
557
|
[/Invalid contract address for \w+ in environment/i, "naming.contract_unavailable"],
|
|
515
|
-
[new RegExp(`Domain\\s+\\S+\\.${TLD_FRAGMENT}\\s+is already owned by\\s+0x[a-fA-F0-9]+`, "i"), "naming.already_owned"],
|
|
516
|
-
// #1185: formatUnregistrableReason's distinctive phrase — present in both
|
|
517
|
-
// the unregistered ("...is not registered, and bulletin-deploy cannot
|
|
518
|
-
// register it: ...") and owned-by-another-account ("...is owned by
|
|
519
|
-
// 0x..., and bulletin-deploy cannot register it for a different account:
|
|
520
|
-
// ...") variants, so one rule classifies both. Ordered before
|
|
521
|
-
// naming.subdomain_orphan (a different message shape entirely) and after
|
|
522
|
-
// naming.already_owned (whose "already owned by 0x..." phrasing this rule
|
|
523
|
-
// never produces, so there's no overlap either way).
|
|
524
|
-
[/cannot register it/i, "naming.governance_reserved"],
|
|
525
558
|
[new RegExp(`Cannot deploy\\s+[\\w.-]+\\.${TLD_FRAGMENT}:\\s*parent\\s+[\\w.-]+\\.${TLD_FRAGMENT}\\s+is owned by`, "i"), "naming.subdomain_orphan"],
|
|
559
|
+
// After subdomain_orphan, whose "is owned by" it would otherwise claim.
|
|
560
|
+
[/owner mismatch|\bowned by\b/i, "naming.already_owned"],
|
|
526
561
|
[/Post-deploy verification failed for .+: on-chain contenthash is /i, "verify.contenthash_mismatch"],
|
|
527
562
|
[/Deploy verification failed:\s*DAG-PB root.+not finalised/i, "verify.dagpb_not_finalised"],
|
|
528
563
|
[/Retry budget exhausted:.*recovery attempts/i, "network.recovery_exhausted"],
|
|
529
564
|
[/Account auto-mapping did not take effect on-chain/i, "account.mapping_pending"],
|
|
530
|
-
[/ReviveApi
|
|
531
|
-
[/ReviveApi
|
|
532
|
-
[/transaction watcher silent for
|
|
565
|
+
[/ReviveApi.*timed out/i, "chain.api_timeout"],
|
|
566
|
+
[/ReviveApi.*returned empty result/i, "chain.api_timeout"],
|
|
567
|
+
[/transaction watcher silent for/i, "chain.tx_silent"],
|
|
533
568
|
[/(?:commit|register|setSubnodeOwner|setResolver|setContenthash|setText|Revive\.call|Utility\.batch_all) timed out after \d+ms/i, "chain.tx_timeout"],
|
|
534
569
|
// Prefix, not the full `AncientBirthBlock` variant name: at 17 chars it is
|
|
535
570
|
// long enough to be cut in half by the 100-char truncation both src/deploy.ts
|
|
536
571
|
// producer sites apply to the inner chain error, which sent a live span to
|
|
537
572
|
// `unknown` with the message ending `"type": "AncientBirth`.
|
|
538
573
|
[/AncientBirth/i, "chain.extrinsic_expired"],
|
|
539
|
-
[/
|
|
574
|
+
[/quota exhausted/i, "chain.quota_exhausted"],
|
|
540
575
|
[/Mobile (?:transaction )?signing (?:failed|rejected).*message too big/i, "signer.message_too_large"],
|
|
541
|
-
[/^INVARIANT FAILED:/i, "tool.invariant"]
|
|
576
|
+
[/^INVARIANT FAILED:/i, "tool.invariant"],
|
|
577
|
+
// Broad fallbacks, last.
|
|
578
|
+
[/javascript heap out of memory|allocation failed.*heap/i, "tool.out_of_memory"],
|
|
579
|
+
[/External signer mode is not supported with dotns-cli/i, "tool.external_signer_unsupported"],
|
|
580
|
+
[/All promises were rejected/i, "network.endpoints_failed"],
|
|
581
|
+
[/tx dropped from best chain|chain may have (?:dropped|evicted)/i, "chain.tx_dropped"],
|
|
582
|
+
[/not finalised after \d+s/i, "chain.not_finalised"],
|
|
583
|
+
[/chunk.*failed after.*retr/i, "chain.chunk_failed"],
|
|
584
|
+
[/"type"\s*:\s*"Invalid"/i, "chain.tx_invalid"],
|
|
585
|
+
[/dotns register failed/i, "dotns.register_failed"]
|
|
542
586
|
];
|
|
543
587
|
var INVALID_TRANSACTION_VARIANT_KIND = {
|
|
544
588
|
Future: "nonce-stale",
|
|
@@ -555,6 +599,63 @@ function classifyErrorKind(msg, variant) {
|
|
|
555
599
|
}
|
|
556
600
|
return "unknown";
|
|
557
601
|
}
|
|
602
|
+
var ERROR_KIND_CATEGORY = {
|
|
603
|
+
"contract-revert": "environment",
|
|
604
|
+
"chain-timeout": "environment",
|
|
605
|
+
"nonce-stale": "environment",
|
|
606
|
+
"connection": "environment",
|
|
607
|
+
"naming.pop_required": "user",
|
|
608
|
+
"naming.nostatus_required": "user",
|
|
609
|
+
"naming.contract_unavailable": "unknown",
|
|
610
|
+
// see contractAddressCategory
|
|
611
|
+
"naming.contract_function_removed": "unknown",
|
|
612
|
+
// see contractAddressCategory
|
|
613
|
+
"dotns.abi_decode_empty": "unknown",
|
|
614
|
+
// never attributed; kept visible
|
|
615
|
+
"naming.already_owned": "user",
|
|
616
|
+
"naming.governance_reserved": "user",
|
|
617
|
+
"naming.short_names_closed": "user",
|
|
618
|
+
"naming.soulbound": "user",
|
|
619
|
+
"naming.subdomain_orphan": "user",
|
|
620
|
+
"naming.invalid_label": "user",
|
|
621
|
+
"naming.name_unavailable": "user",
|
|
622
|
+
"naming.tld_mismatch": "environment",
|
|
623
|
+
"funding.insufficient_balance": "user",
|
|
624
|
+
"funding.transfer_failed": "user",
|
|
625
|
+
"verify.contenthash_mismatch": "environment",
|
|
626
|
+
"verify.dagpb_not_finalised": "environment",
|
|
627
|
+
"network.recovery_exhausted": "environment",
|
|
628
|
+
"network.endpoints_failed": "environment",
|
|
629
|
+
"account.mapping_pending": "environment",
|
|
630
|
+
"chain.api_timeout": "environment",
|
|
631
|
+
"chain.tx_timeout": "environment",
|
|
632
|
+
"chain.tx_silent": "environment",
|
|
633
|
+
"chain.extrinsic_expired": "environment",
|
|
634
|
+
"chain.quota_exhausted": "user",
|
|
635
|
+
"chain.bad_proof": "environment",
|
|
636
|
+
"chain.chunk_failed": "environment",
|
|
637
|
+
"chain.chunk_unverified": "environment",
|
|
638
|
+
"chain.chunk_not_included": "environment",
|
|
639
|
+
// Always the node's pool state, not the operator's: a pinned index is only the
|
|
640
|
+
// remedy (unset/pick another), the stuck queue is the cause, and the same
|
|
641
|
+
// message is also thrown for a direct signer that pinned nothing.
|
|
642
|
+
"chain.queue_stuck": "environment",
|
|
643
|
+
"chain.tx_dropped": "environment",
|
|
644
|
+
"chain.tx_invalid": "environment",
|
|
645
|
+
"chain.not_finalised": "environment",
|
|
646
|
+
"dotns.register_failed": "environment",
|
|
647
|
+
"signer.message_too_large": "environment",
|
|
648
|
+
"signer.invalid_mnemonic": "user",
|
|
649
|
+
"storage.rejected": "user",
|
|
650
|
+
"storage.allowance_insufficient": "user",
|
|
651
|
+
"user.aborted": "user",
|
|
652
|
+
"signer.phone_confirmation_unavailable": "user",
|
|
653
|
+
"tool.invariant": "internal",
|
|
654
|
+
"tool.ipfs_missing": "user",
|
|
655
|
+
"tool.out_of_memory": "internal",
|
|
656
|
+
"tool.external_signer_unsupported": "internal",
|
|
657
|
+
"unknown": "unknown"
|
|
658
|
+
};
|
|
558
659
|
function chainErrorVariantOf(error) {
|
|
559
660
|
return error?.chainErrorVariant;
|
|
560
661
|
}
|
|
@@ -609,6 +710,7 @@ async function withSpan(op, description, attributes, fn) {
|
|
|
609
710
|
}
|
|
610
711
|
var memoryPeak = null;
|
|
611
712
|
var deployRootSpan = null;
|
|
713
|
+
var deploySpan = null;
|
|
612
714
|
var stageSamples = {};
|
|
613
715
|
var reportContext = {};
|
|
614
716
|
var currentErrorCategory = null;
|
|
@@ -674,6 +776,7 @@ async function withDeploySpan(domain, fn) {
|
|
|
674
776
|
try {
|
|
675
777
|
return await Sentry.startSpan({ op: "deploy", name: `deploy ${domain}`, attributes: attrs }, async (span) => {
|
|
676
778
|
deployRootSpan = span;
|
|
779
|
+
deploySpan = span;
|
|
677
780
|
span.setAttribute("deploy.tool_version", VERSION);
|
|
678
781
|
if (relaunchOomHintShown) {
|
|
679
782
|
span.setAttribute("deploy.relaunch.oom_hint_shown", "true");
|
|
@@ -694,19 +797,7 @@ async function withDeploySpan(domain, fn) {
|
|
|
694
797
|
span.setAttribute("deploy.status", "ok");
|
|
695
798
|
return result;
|
|
696
799
|
} catch (error) {
|
|
697
|
-
|
|
698
|
-
const variant = chainErrorVariantOf(error);
|
|
699
|
-
span.setAttribute("deploy.status", "error");
|
|
700
|
-
setDeployErrorOnSpan(span, msg, variant);
|
|
701
|
-
const errorCategory = classifyDeployError(msg, variant);
|
|
702
|
-
span.setAttribute("deploy.error_category", errorCategory);
|
|
703
|
-
currentErrorCategory = errorCategory;
|
|
704
|
-
const isExpected = isExpectedError(msg, variant);
|
|
705
|
-
span.setAttribute("deploy.expected", isExpected ? "true" : "false");
|
|
706
|
-
span.setAttribute("deploy.sad", isExpected ? "false" : "true");
|
|
707
|
-
if (!isExpected) {
|
|
708
|
-
span.setStatus({ code: 2, message: "internal_error" });
|
|
709
|
-
}
|
|
800
|
+
recordDeployFailure(span, error);
|
|
710
801
|
throw error;
|
|
711
802
|
} finally {
|
|
712
803
|
sampleMemory("end");
|
|
@@ -757,6 +848,7 @@ async function withDeploySpan(domain, fn) {
|
|
|
757
848
|
} finally {
|
|
758
849
|
memoryPeak = null;
|
|
759
850
|
deployRootSpan = null;
|
|
851
|
+
deploySpan = null;
|
|
760
852
|
stageSamples = {};
|
|
761
853
|
reportContext = {};
|
|
762
854
|
currentErrorCategory = null;
|
|
@@ -828,6 +920,29 @@ function setDeployError(msg, variant) {
|
|
|
828
920
|
if (!deployRootSpan) return;
|
|
829
921
|
setDeployErrorOnSpan(deployRootSpan, msg, variant);
|
|
830
922
|
}
|
|
923
|
+
function recordDeployFailure(span, error) {
|
|
924
|
+
const msg = error?.message ?? String(error);
|
|
925
|
+
const variant = chainErrorVariantOf(error);
|
|
926
|
+
span.setAttribute("deploy.status", "error");
|
|
927
|
+
setDeployErrorOnSpan(span, msg, variant);
|
|
928
|
+
const errorCategory = classifyDeployError(msg, variant);
|
|
929
|
+
span.setAttribute("deploy.error_category", errorCategory);
|
|
930
|
+
currentErrorCategory = errorCategory;
|
|
931
|
+
const isExpected = isExpectedError(msg, variant);
|
|
932
|
+
span.setAttribute("deploy.expected", isExpected ? "true" : "false");
|
|
933
|
+
span.setAttribute("deploy.sad", isExpected ? "false" : "true");
|
|
934
|
+
if (!isExpected) {
|
|
935
|
+
span.setStatus({ code: 2, message: "internal_error" });
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
function markDeployFatal(error) {
|
|
939
|
+
if (deployRootSpan && deployRootSpan === deploySpan) recordDeployFailure(deployRootSpan, error);
|
|
940
|
+
}
|
|
941
|
+
function exitOnFatalError(error) {
|
|
942
|
+
console.error(error);
|
|
943
|
+
markDeployFatal(error);
|
|
944
|
+
void closeTelemetry(2e3).finally(() => process.exit(1));
|
|
945
|
+
}
|
|
831
946
|
function __setDeployRootSpanForTest(span) {
|
|
832
947
|
deployRootSpan = span;
|
|
833
948
|
}
|
|
@@ -1035,6 +1150,7 @@ export {
|
|
|
1035
1150
|
computeDeployOutcome,
|
|
1036
1151
|
classifiableErrorKinds,
|
|
1037
1152
|
classifyErrorKind,
|
|
1153
|
+
ERROR_KIND_CATEGORY,
|
|
1038
1154
|
sanitizeErrorMessage,
|
|
1039
1155
|
analyseErrorPattern,
|
|
1040
1156
|
withSpan,
|
|
@@ -1044,6 +1160,7 @@ export {
|
|
|
1044
1160
|
setDeployReportContext,
|
|
1045
1161
|
setDeployAttribute,
|
|
1046
1162
|
setDeployError,
|
|
1163
|
+
markDeployFatal,
|
|
1047
1164
|
__setDeployRootSpanForTest,
|
|
1048
1165
|
__setSentryForTest,
|
|
1049
1166
|
getCurrentSentryTraceId,
|
|
@@ -19,13 +19,13 @@ import {
|
|
|
19
19
|
setBulletinEndpoints,
|
|
20
20
|
storeDirectory,
|
|
21
21
|
storeFile
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-YQF5Y2HS.js";
|
|
23
23
|
import {
|
|
24
24
|
DEFAULT_TLD,
|
|
25
25
|
DotNS,
|
|
26
26
|
parseDomainName,
|
|
27
27
|
stripTldSuffix
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-PR52KVWQ.js";
|
|
29
29
|
import {
|
|
30
30
|
DEFAULT_ENV_ID,
|
|
31
31
|
getPopSelfServeConfig,
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
setDeployAttribute,
|
|
37
37
|
withManifestSpan,
|
|
38
38
|
withSpan
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-7JM6FJM3.js";
|
|
40
40
|
import {
|
|
41
41
|
NonRetryableError
|
|
42
42
|
} from "./chunk-SH2LG52Z.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
endpointHost
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UWTR5Z27.js";
|
|
4
4
|
import {
|
|
5
5
|
captureWarning
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-7JM6FJM3.js";
|
|
7
7
|
|
|
8
8
|
// src/ws.ts
|
|
9
9
|
import { getWsProvider as papiGetWsProvider } from "polkadot-api/ws";
|