bulletin-deploy 0.22.0-rc.1 → 0.22.0

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.
Files changed (53) hide show
  1. package/dist/auth/index.js +4 -4
  2. package/dist/auth/vendor/index.js +5 -5
  3. package/dist/auth-config.js +3 -3
  4. package/dist/bug-report.js +4 -4
  5. package/dist/chain-health.js +3 -3
  6. package/dist/{chunk-NTJRL6TA.js → chunk-2SOCCHTI.js} +1 -1
  7. package/dist/{chunk-KDVHMIKK.js → chunk-5SHGORQT.js} +1 -1
  8. package/dist/{chunk-RMO7G7X2.js → chunk-5UZK76A7.js} +3 -3
  9. package/dist/chunk-6DDB5QOM.js +40 -0
  10. package/dist/{chunk-KXYSKT5U.js → chunk-BU2OJPLK.js} +21 -1
  11. package/dist/{chunk-DKTKQS2N.js → chunk-CL2X6RL2.js} +1 -1
  12. package/dist/{chunk-7G475SPF.js → chunk-CW2MW6SR.js} +1 -1
  13. package/dist/{chunk-BMUDRQYV.js → chunk-DGLZHLY5.js} +94 -4
  14. package/dist/{chunk-4FAL7DFB.js → chunk-FE6BHGRP.js} +174 -40
  15. package/dist/{chunk-OFR4K7P3.js → chunk-KGIJV4SI.js} +22 -7
  16. package/dist/{chunk-LTUECTW2.js → chunk-KKNTPOAI.js} +272 -121
  17. package/dist/{chunk-6RQY4VPG.js → chunk-OZBNRLGB.js} +4 -4
  18. package/dist/{chunk-Z6EIQVNP.js → chunk-RIVRHMMY.js} +4 -4
  19. package/dist/{chunk-FUU5U5GB.js → chunk-UUIIID2J.js} +2 -2
  20. package/dist/{chunk-S2N25SYI.js → chunk-XBASBXMQ.js} +1 -1
  21. package/dist/chunk-probe.js +3 -3
  22. package/dist/commands/login.js +18 -17
  23. package/dist/commands/logout.js +5 -5
  24. package/dist/commands/transfer.js +6 -6
  25. package/dist/commands/whoami.js +3 -3
  26. package/dist/deploy-actors.js +8 -8
  27. package/dist/deploy.d.ts +71 -27
  28. package/dist/deploy.js +28 -17
  29. package/dist/dotns.d.ts +18 -5
  30. package/dist/dotns.js +6 -6
  31. package/dist/e2e-nonce-barrier.d.ts +24 -0
  32. package/dist/e2e-nonce-barrier.js +12 -0
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.js +44 -35
  35. package/dist/manifest/product-preflight.js +3 -3
  36. package/dist/manifest/publish.js +22 -21
  37. package/dist/memory-report.js +2 -2
  38. package/dist/merkle.js +18 -17
  39. package/dist/personhood/bootstrap.js +16 -16
  40. package/dist/personhood/people-client.js +6 -6
  41. package/dist/pool.d.ts +7 -2
  42. package/dist/pool.js +9 -7
  43. package/dist/run-state.d.ts +22 -2
  44. package/dist/run-state.js +9 -1
  45. package/dist/sss-allowance-cache.js +4 -4
  46. package/dist/storage-signer.js +18 -17
  47. package/dist/telemetry.d.ts +1 -1
  48. package/dist/telemetry.js +2 -2
  49. package/dist/version-check.js +3 -3
  50. package/dist/ws.js +4 -4
  51. package/package.json +1 -1
  52. package/tools/release-retry-wrapper.mjs +27 -4
  53. package/dist/{chunk-EHAL4CJM.js → chunk-DBDOISNQ.js} +3 -3
@@ -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 {
@@ -10,10 +10,10 @@ import {
10
10
  hasPersistedSession,
11
11
  readNetworkSuffix,
12
12
  resolveBulletinEndpoints
13
- } from "./chunk-KDVHMIKK.js";
13
+ } from "./chunk-5SHGORQT.js";
14
14
  import "./chunk-3NR3T7US.js";
15
- import "./chunk-KXYSKT5U.js";
16
- import "./chunk-BMUDRQYV.js";
15
+ import "./chunk-BU2OJPLK.js";
16
+ import "./chunk-DGLZHLY5.js";
17
17
  import "./chunk-SH2LG52Z.js";
18
18
  import "./chunk-DNXE3Q5D.js";
19
19
  export {
@@ -10,10 +10,10 @@ import {
10
10
  offerBugReport,
11
11
  scrubSecrets,
12
12
  setDeployContext
13
- } from "./chunk-RMO7G7X2.js";
14
- import "./chunk-S2N25SYI.js";
15
- import "./chunk-KXYSKT5U.js";
16
- import "./chunk-BMUDRQYV.js";
13
+ } from "./chunk-5UZK76A7.js";
14
+ import "./chunk-XBASBXMQ.js";
15
+ import "./chunk-BU2OJPLK.js";
16
+ import "./chunk-DGLZHLY5.js";
17
17
  import "./chunk-SH2LG52Z.js";
18
18
  export {
19
19
  buildCliFlagsSummary,
@@ -3,9 +3,9 @@ import {
3
3
  DEFAULT_WINDOW_BLOCKS,
4
4
  MAX_SPANS_PER_SAMPLER,
5
5
  endpointHost
6
- } from "./chunk-NTJRL6TA.js";
7
- import "./chunk-KXYSKT5U.js";
8
- import "./chunk-BMUDRQYV.js";
6
+ } from "./chunk-2SOCCHTI.js";
7
+ import "./chunk-BU2OJPLK.js";
8
+ import "./chunk-DGLZHLY5.js";
9
9
  import "./chunk-SH2LG52Z.js";
10
10
  export {
11
11
  ChainHealthSampler,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  emitIntervalSpan,
3
3
  setDeployAttribute
4
- } from "./chunk-KXYSKT5U.js";
4
+ } from "./chunk-BU2OJPLK.js";
5
5
 
6
6
  // src/chain-health.ts
7
7
  var DEFAULT_WINDOW_BLOCKS = 10;
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-3NR3T7US.js";
4
4
  import {
5
5
  VERSION
6
- } from "./chunk-KXYSKT5U.js";
6
+ } from "./chunk-BU2OJPLK.js";
7
7
  import {
8
8
  CLI_NAME
9
9
  } from "./chunk-DNXE3Q5D.js";
@@ -2,15 +2,15 @@ import {
2
2
  classifyErrorArea,
3
3
  isInteractive,
4
4
  promptYesNo
5
- } from "./chunk-S2N25SYI.js";
5
+ } from "./chunk-XBASBXMQ.js";
6
6
  import {
7
7
  VERSION,
8
8
  getCurrentSentryTraceId,
9
9
  resolveIssueRepoSlug
10
- } from "./chunk-KXYSKT5U.js";
10
+ } from "./chunk-BU2OJPLK.js";
11
11
  import {
12
12
  package_default
13
- } from "./chunk-BMUDRQYV.js";
13
+ } from "./chunk-DGLZHLY5.js";
14
14
 
15
15
  // src/bug-report.ts
16
16
  import { execSync, execFileSync } from "child_process";
@@ -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-BMUDRQYV.js";
4
+ } from "./chunk-DGLZHLY5.js";
5
5
  import {
6
6
  FALLBACK_CONFIG_ORIGIN,
7
7
  SHIPPED_CONFIG_ORIGIN
@@ -352,6 +352,10 @@ function getDeployAttributes(domain) {
352
352
  "deploy.tool_version": VERSION,
353
353
  "deploy.runner": resolveRunner(),
354
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",
355
359
  // Present on every span so a dashboard can compute count_if(deploy.version_status,
356
360
  // "outdated") / count() — see classifyVersionFreshness in version-check.ts.
357
361
  "deploy.version_status": versionFreshness,
@@ -428,6 +432,16 @@ var ERROR_KIND_RULES = [
428
432
  // (2026-08-06 telemetry sweep: this was the largest live `unknown` bucket —
429
433
  // 31 spans, 30 of them env=preview, from the per-env authorizer gap #1209.)
430
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"],
431
445
  // Revive surfaces the personhood gate as a bare revert reason rather than the
432
446
  // "requires ProofOfPersonhoodX, but this signer is NoStatus" prose matched
433
447
  // further down — same user-actionable cause, so it shares that kind. Ordered
@@ -620,6 +634,12 @@ var ERROR_KIND_CATEGORY = {
620
634
  "chain.quota_exhausted": "user",
621
635
  "chain.bad_proof": "environment",
622
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",
623
643
  "chain.tx_dropped": "environment",
624
644
  "chain.tx_invalid": "environment",
625
645
  "chain.not_finalised": "environment",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  captureWarning
3
- } from "./chunk-KXYSKT5U.js";
3
+ } from "./chunk-BU2OJPLK.js";
4
4
 
5
5
  // src/chunk-probe.ts
6
6
  import { Twox128, Blake2128Concat, decAnyMetadata, unifyMetadata } from "@polkadot-api/substrate-bindings";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-MRZL4IXT.js";
4
4
  import {
5
5
  DOT_DAPP_ID
6
- } from "./chunk-KDVHMIKK.js";
6
+ } from "./chunk-5SHGORQT.js";
7
7
 
8
8
  // src/sss-allowance-cache.ts
9
9
  import { mkdir, readFile, writeFile, unlink } from "fs/promises";
@@ -2,11 +2,12 @@
2
2
  import * as fs from "fs";
3
3
  import * as os from "os";
4
4
  import * as path from "path";
5
+ import * as crypto from "crypto";
5
6
 
6
7
  // package.json
7
8
  var package_default = {
8
9
  name: "bulletin-deploy",
9
- version: "0.22.0-rc.1",
10
+ version: "0.22.0",
10
11
  private: false,
11
12
  repository: {
12
13
  type: "git",
@@ -212,6 +213,13 @@ function commitmentKey(environmentId, tld, owner, label) {
212
213
  function commitmentStateFilePath(environmentId, tld, owner, label) {
213
214
  return path.join(resolveStateDir(), `commitment-${commitmentKey(environmentId, tld, owner, label)}.json`);
214
215
  }
216
+ function commitmentLockFilePath(environmentId, tld, owner, label) {
217
+ return path.join(resolveStateDir(), `commitment-${commitmentKey(environmentId, tld, owner, label)}.lock`);
218
+ }
219
+ function commitmentRecordKey(record) {
220
+ if (!record) return null;
221
+ return typeof record.recordId === "string" ? record.recordId : `legacy:${record.commitment}`;
222
+ }
215
223
  var FALLBACK_MAX_COMMITMENT_AGE_SECONDS = 86400;
216
224
  var COMMITMENT_RECORD_MAX_AGE_MS = 2 * FALLBACK_MAX_COMMITMENT_AGE_SECONDS * 1e3;
217
225
  function pruneStaleCommitmentRecords(nowMs = Date.now()) {
@@ -236,7 +244,7 @@ function pruneStaleCommitmentRecords(nowMs = Date.now()) {
236
244
  const record = parseJsonRecord(raw);
237
245
  const isStale = record === null || typeof record.savedAt !== "number" || nowMs - record.savedAt > COMMITMENT_RECORD_MAX_AGE_MS;
238
246
  if (isStale) tryUnlink(file);
239
- } else if (entry.endsWith(".tmp")) {
247
+ } else if (entry.endsWith(".tmp") || entry.endsWith(".lock") || entry.endsWith(".stale")) {
240
248
  let mtimeMs;
241
249
  try {
242
250
  mtimeMs = fs.statSync(file).mtimeMs;
@@ -254,12 +262,90 @@ function loadCommitmentRecord(environmentId, tld, owner, label) {
254
262
  function writeCommitmentRecord(record) {
255
263
  return writeJsonAtomic(() => commitmentStateFilePath(record.environmentId, record.tld, record.owner, record.label), record, { mode: COMMITMENT_FILE_MODE });
256
264
  }
257
- function clearCommitmentRecord(environmentId, tld, owner, label) {
265
+ function replaceCommitmentRecord(record, expectedKey) {
266
+ const current = readJsonSafe(() => commitmentStateFilePath(record.environmentId, record.tld, record.owner, record.label));
267
+ if (commitmentRecordKey(current) !== expectedKey) return false;
268
+ return writeCommitmentRecord(record);
269
+ }
270
+ function clearCommitmentRecord(environmentId, tld, owner, label, expectedKey) {
258
271
  try {
272
+ const current = readJsonSafe(() => commitmentStateFilePath(environmentId, tld, owner, label));
273
+ if (commitmentRecordKey(current) !== expectedKey) return;
259
274
  tryUnlink(commitmentStateFilePath(environmentId, tld, owner, label));
260
275
  } catch {
261
276
  }
262
277
  }
278
+ var LOCK_GARBAGE_GRACE_MS = 6e4;
279
+ function isStaleLock(holder, mtimeMs, nowMs) {
280
+ if (!holder || typeof holder.pid !== "number" || typeof holder.createdAt !== "number") {
281
+ return nowMs - mtimeMs > LOCK_GARBAGE_GRACE_MS;
282
+ }
283
+ if (nowMs - holder.createdAt > COMMITMENT_RECORD_MAX_AGE_MS) return true;
284
+ return holder.host === os.hostname() && !isPidAlive(holder.pid);
285
+ }
286
+ var NO_LOCK = { token: null, release: () => {
287
+ } };
288
+ function tryAcquireCommitmentLock(environmentId, tld, owner, label, nowMs = Date.now()) {
289
+ let file;
290
+ try {
291
+ file = commitmentLockFilePath(environmentId, tld, owner, label);
292
+ fs.mkdirSync(path.dirname(file), { recursive: true, mode: 448 });
293
+ } catch {
294
+ return NO_LOCK;
295
+ }
296
+ const token = crypto.randomBytes(16).toString("hex");
297
+ const mine = { pid: process.pid, host: os.hostname(), token, createdAt: nowMs };
298
+ for (let attempt = 0; attempt < 3; attempt++) {
299
+ try {
300
+ fs.writeFileSync(file, JSON.stringify(mine), { encoding: "utf-8", flag: "wx", mode: COMMITMENT_FILE_MODE });
301
+ return { token, release: () => releaseCommitmentLock(file, token) };
302
+ } catch (err) {
303
+ if (err.code !== "EEXIST") return NO_LOCK;
304
+ }
305
+ let raw;
306
+ let mtimeMs;
307
+ try {
308
+ raw = fs.readFileSync(file, "utf-8");
309
+ mtimeMs = fs.statSync(file).mtimeMs;
310
+ } catch {
311
+ continue;
312
+ }
313
+ const holder = parseJsonRecord(raw);
314
+ if (!isStaleLock(holder, mtimeMs, nowMs)) {
315
+ return { heldBy: holder ?? { pid: 0, host: "unknown", token: "", createdAt: mtimeMs } };
316
+ }
317
+ const grabbed = `${file}.${process.pid}.${token}.stale`;
318
+ try {
319
+ fs.renameSync(file, grabbed);
320
+ } catch {
321
+ continue;
322
+ }
323
+ let grabbedRaw = "";
324
+ try {
325
+ grabbedRaw = fs.readFileSync(grabbed, "utf-8");
326
+ } catch {
327
+ }
328
+ if (grabbedRaw !== raw) {
329
+ try {
330
+ fs.linkSync(grabbed, file);
331
+ } catch {
332
+ }
333
+ tryUnlink(grabbed);
334
+ const fresh = parseJsonRecord(grabbedRaw);
335
+ if (fresh) return { heldBy: fresh };
336
+ continue;
337
+ }
338
+ tryUnlink(grabbed);
339
+ }
340
+ return NO_LOCK;
341
+ }
342
+ function releaseCommitmentLock(file, token) {
343
+ try {
344
+ const holder = parseJsonRecord(fs.readFileSync(file, "utf-8"));
345
+ if (holder?.token === token) tryUnlink(file);
346
+ } catch {
347
+ }
348
+ }
263
349
 
264
350
  export {
265
351
  package_default,
@@ -272,8 +358,12 @@ export {
272
358
  probablyOomRssMb,
273
359
  shouldShowOomHint,
274
360
  commitmentStateFilePath,
361
+ commitmentLockFilePath,
362
+ commitmentRecordKey,
275
363
  FALLBACK_MAX_COMMITMENT_AGE_SECONDS,
276
364
  loadCommitmentRecord,
277
365
  writeCommitmentRecord,
278
- clearCommitmentRecord
366
+ replaceCommitmentRecord,
367
+ clearCommitmentRecord,
368
+ tryAcquireCommitmentLock
279
369
  };