bulletin-deploy 0.7.0 → 0.7.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/README.md CHANGED
@@ -259,6 +259,7 @@ Sentry telemetry is **off by default for external users**. It's automatically on
259
259
 
260
260
  - `BULLETIN_DEPLOY_TELEMETRY=1` — explicit opt-in (useful if you want to help us debug an issue you're seeing).
261
261
  - `BULLETIN_DEPLOY_TELEMETRY=0` — force off regardless of context.
262
+ - When running under **Bun**, the Parity-internal memory-report diagnostic bundle is skipped (basic deploy telemetry still works). The bundle relies on Node's `v8` module, which Bun implements only partially.
262
263
 
263
264
  Detection signals (OR'd together):
264
265
  1. `GITHUB_REPOSITORY` matches a known-internal org — Parity-owned CI workflow.
@@ -4,9 +4,13 @@ import { deploy, DEFAULT_BULLETIN_RPC, DEFAULT_POOL_SIZE, NonRetryableError, EXI
4
4
  import { bootstrapPool } from "../dist/pool.js";
5
5
  import { VERSION } from "../dist/telemetry.js";
6
6
  import { handleFailedDeploy, preReleaseWarning } from "../dist/version-check.js";
7
- import { setDeployContext } from "../dist/bug-report.js";
7
+ import { setDeployContext, installLogCapture, buildCliFlagsSummary } from "../dist/bug-report.js";
8
8
  import * as fs from "fs";
9
9
 
10
+ // Install early so anything printed during flag parsing / preflight is
11
+ // available to the bug-report log tail.
12
+ installLogCapture();
13
+
10
14
  const args = process.argv.slice(2);
11
15
 
12
16
  const flags = {};
@@ -67,12 +71,25 @@ try {
67
71
  if (!domain) { console.error("Error: domain required (e.g. my-app.dot)"); process.exit(1); }
68
72
  if (!fs.existsSync(buildDir)) { console.error(`Error: ${buildDir} does not exist`); process.exit(1); }
69
73
 
74
+ const effectiveRpc = flags.rpc ?? process.env.BULLETIN_RPC ?? DEFAULT_BULLETIN_RPC;
75
+ const deployTag = flags.tag ?? process.env.DEPLOY_TAG;
76
+ const ci = process.env.GITHUB_ACTIONS === "true" ? {
77
+ runUrl: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID
78
+ ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}${process.env.GITHUB_RUN_ATTEMPT ? `/attempts/${process.env.GITHUB_RUN_ATTEMPT}` : ""}`
79
+ : undefined,
80
+ workflow: process.env.GITHUB_WORKFLOW,
81
+ job: process.env.GITHUB_JOB,
82
+ sha: process.env.GITHUB_SHA,
83
+ } : undefined;
70
84
  setDeployContext({
71
85
  domain,
72
- rpc: flags.rpc,
86
+ rpc: effectiveRpc,
73
87
  repo: process.env.GITHUB_REPOSITORY,
74
88
  branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME,
75
89
  signerMode: flags.mnemonic ? "direct" : "pool",
90
+ deployTag,
91
+ cliFlags: buildCliFlagsSummary(flags),
92
+ ci,
76
93
  });
77
94
 
78
95
  const result = await deploy(buildDir, domain, {
@@ -6,13 +6,24 @@ interface DeployContext {
6
6
  chunkCount?: number;
7
7
  totalSize?: string;
8
8
  rpc?: string;
9
- sentryTraceId?: string;
9
+ deployTag?: string;
10
+ cliFlags?: string;
11
+ ci?: {
12
+ runUrl?: string;
13
+ workflow?: string;
14
+ job?: string;
15
+ sha?: string;
16
+ };
10
17
  }
11
18
  declare function setDeployContext(ctx: Partial<DeployContext>): void;
19
+ declare function installLogCapture(): void;
20
+ declare function getCapturedTail(): string;
21
+ declare function scrubSecrets(text: string): string;
22
+ declare function buildCliFlagsSummary(flags: Record<string, unknown>): string;
12
23
  declare function buildReportBody(error: Error): string;
13
24
  declare function buildTitle(error: Error): string;
14
25
  declare function buildLabels(error: Error): string[];
15
- declare function createGhIssue(title: string, body: string, labels: string[]): void;
26
+ declare function createGhIssue(title: string, body: string, labels: string[]): string;
16
27
  declare function offerBugReport(error: Error): Promise<void>;
17
28
 
18
- export { buildLabels, buildReportBody, buildTitle, createGhIssue, offerBugReport, setDeployContext };
29
+ export { buildCliFlagsSummary, buildLabels, buildReportBody, buildTitle, createGhIssue, getCapturedTail, installLogCapture, offerBugReport, scrubSecrets, setDeployContext };
@@ -1,121 +1,27 @@
1
1
  import {
2
- classifyErrorArea,
3
- isInteractive,
4
- promptYesNo
5
- } from "./chunk-PA3AS7QR.js";
6
- import {
7
- VERSION
8
- } from "./chunk-Q42TQHNL.js";
2
+ buildCliFlagsSummary,
3
+ buildLabels,
4
+ buildReportBody,
5
+ buildTitle,
6
+ createGhIssue,
7
+ getCapturedTail,
8
+ installLogCapture,
9
+ offerBugReport,
10
+ scrubSecrets,
11
+ setDeployContext
12
+ } from "./chunk-LMSMDKVU.js";
13
+ import "./chunk-4UNQGG3O.js";
14
+ import "./chunk-4FUUYJP2.js";
9
15
  import "./chunk-QGM4M3NI.js";
10
-
11
- // src/bug-report.ts
12
- import { execSync, execFileSync } from "child_process";
13
- import * as os from "os";
14
- var _deployContext = {};
15
- function setDeployContext(ctx) {
16
- _deployContext = { ..._deployContext, ...ctx };
17
- }
18
- function hasGhCli() {
19
- try {
20
- execSync("gh --version", { stdio: "pipe" });
21
- return true;
22
- } catch {
23
- return false;
24
- }
25
- }
26
- function buildReportBody(error) {
27
- const lines = [
28
- "## Environment",
29
- "",
30
- `- **bulletin-deploy**: ${VERSION}`,
31
- `- **Node.js**: ${process.version}`,
32
- `- **OS**: ${os.platform()} ${os.arch()} ${os.release()}`,
33
- "",
34
- "## Error",
35
- "",
36
- "```",
37
- error.stack || error.message,
38
- "```",
39
- ""
40
- ];
41
- const ctx = _deployContext;
42
- if (ctx.domain || ctx.repo || ctx.rpc) {
43
- lines.push("## Deploy Context", "");
44
- if (ctx.domain) lines.push(`- **Domain**: ${ctx.domain}`);
45
- if (ctx.repo) lines.push(`- **Repo**: ${ctx.repo}`);
46
- if (ctx.branch) lines.push(`- **Branch**: ${ctx.branch}`);
47
- if (ctx.signerMode) lines.push(`- **Signer mode**: ${ctx.signerMode}`);
48
- if (ctx.chunkCount != null) lines.push(`- **Chunks**: ${ctx.chunkCount}`);
49
- if (ctx.totalSize) lines.push(`- **Total size**: ${ctx.totalSize}`);
50
- if (ctx.rpc) lines.push(`- **RPC**: ${ctx.rpc}`);
51
- if (ctx.sentryTraceId) lines.push(`- **Sentry trace**: ${ctx.sentryTraceId}`);
52
- lines.push("");
53
- }
54
- return lines.join("\n");
55
- }
56
- function buildTitle(error) {
57
- const msg = error.message.slice(0, 60);
58
- return `[deploy-bug] ${msg}`;
59
- }
60
- function buildLabels(error) {
61
- const labels = ["bug", "auto-report"];
62
- const area = classifyErrorArea(error.message);
63
- if (area) labels.push(area);
64
- return labels;
65
- }
66
- function createGhIssue(title, body, labels) {
67
- const args = [
68
- "issue",
69
- "create",
70
- "--repo",
71
- "paritytech/bulletin-deploy",
72
- "--title",
73
- title,
74
- ...labels.flatMap((l) => ["--label", l]),
75
- "--body-file",
76
- "-"
77
- ];
78
- execFileSync("gh", args, { input: body, stdio: ["pipe", "inherit", "inherit"] });
79
- }
80
- async function offerBugReport(error) {
81
- if (!isInteractive()) return;
82
- const yes = await promptYesNo("\n This looks like a bug. Open an issue with debug info? [Y/n] ");
83
- if (!yes) return;
84
- const title = buildTitle(error);
85
- const body = buildReportBody(error);
86
- const labels = buildLabels(error);
87
- if (hasGhCli()) {
88
- try {
89
- createGhIssue(title, body, labels);
90
- console.error(" Issue created.");
91
- } catch {
92
- try {
93
- console.error(" Retrying without labels...");
94
- createGhIssue(title, body, []);
95
- console.error(" Issue created (without labels).");
96
- } catch {
97
- console.error(" Failed to create issue. Debug info below:\n");
98
- printFallback(title, body, labels);
99
- }
100
- }
101
- } else {
102
- console.error("\n gh CLI not found. Debug info below \u2014 paste into a new issue:\n");
103
- console.error(` https://github.com/paritytech/bulletin-deploy/issues/new
104
- `);
105
- printFallback(title, body, labels);
106
- }
107
- }
108
- function printFallback(title, body, labels) {
109
- console.error(` Title: ${title}`);
110
- console.error(` Labels: ${labels.join(", ")}
111
- `);
112
- console.error(body);
113
- }
114
16
  export {
17
+ buildCliFlagsSummary,
115
18
  buildLabels,
116
19
  buildReportBody,
117
20
  buildTitle,
118
21
  createGhIssue,
22
+ getCapturedTail,
23
+ installLogCapture,
119
24
  offerBugReport,
25
+ scrubSecrets,
120
26
  setDeployContext
121
27
  };
@@ -13,7 +13,7 @@ import * as path from "path";
13
13
  // package.json
14
14
  var package_default = {
15
15
  name: "bulletin-deploy",
16
- version: "0.7.0",
16
+ version: "0.7.2",
17
17
  private: false,
18
18
  repository: {
19
19
  type: "git",
@@ -316,35 +316,41 @@ async function withDeploySpan(domain, fn) {
316
316
  } finally {
317
317
  sampleMemory("end");
318
318
  if (memoryPeak) {
319
- const report = maybeWriteMemoryReport({
320
- peak: sampleFromBytes(memoryPeak),
321
- stages: stageSamples,
322
- deploy: {
323
- domain,
324
- repo: attrs["deploy.repo"],
325
- deployTag: attrs["deploy.tag"],
326
- durationMs: Date.now() - deployStartMs,
327
- sentryTraceId: span.spanContext?.().traceId,
328
- ...reportContext
329
- },
330
- outputDir: reportContext.outputDir,
331
- onSentryAttach: (r) => {
332
- try {
333
- Sentry.captureMessage(`deploy memory threshold crossed (${r.threshold.peakRssMb} MB)`, {
334
- level: "warning",
335
- tags: { "deploy.mem.report": "1" },
336
- extra: { memoryReport: r }
337
- });
338
- } catch {
319
+ try {
320
+ const report = maybeWriteMemoryReport({
321
+ peak: sampleFromBytes(memoryPeak),
322
+ stages: stageSamples,
323
+ deploy: {
324
+ domain,
325
+ repo: attrs["deploy.repo"],
326
+ deployTag: attrs["deploy.tag"],
327
+ durationMs: Date.now() - deployStartMs,
328
+ sentryTraceId: span.spanContext?.().traceId,
329
+ ...reportContext
330
+ },
331
+ outputDir: reportContext.outputDir,
332
+ onSentryAttach: (r) => {
333
+ try {
334
+ Sentry.captureMessage(`deploy memory threshold crossed (${r.threshold.peakRssMb} MB)`, {
335
+ level: "warning",
336
+ tags: { "deploy.mem.report": "1" },
337
+ extra: { memoryReport: r }
338
+ });
339
+ } catch {
340
+ }
339
341
  }
340
- }
341
- });
342
- if (report.status === "written") {
343
- span.setAttribute("deploy.mem.report_written", "true");
344
- span.setAttribute("deploy.mem.report_path", report.path);
345
- console.log(`
342
+ });
343
+ if (report.status === "written") {
344
+ span.setAttribute("deploy.mem.report_written", "true");
345
+ span.setAttribute("deploy.mem.report_path", report.path);
346
+ console.log(`
346
347
  High memory usage detected (peak ${report.peakRssMb} MB, threshold ${report.thresholdMb} MB).`);
347
- console.log(` Diagnostic report written to ${report.path}`);
348
+ console.log(` Diagnostic report written to ${report.path}`);
349
+ }
350
+ } catch (e) {
351
+ captureWarning("maybeWriteMemoryReport threw", {
352
+ error: e?.message?.slice(0, 200)
353
+ });
348
354
  }
349
355
  }
350
356
  }
@@ -365,6 +371,12 @@ function setDeployAttribute(key, value) {
365
371
  const span = Sentry.getActiveSpan();
366
372
  if (span) span.setAttribute(key, value);
367
373
  }
374
+ function getCurrentSentryTraceId() {
375
+ if (!Sentry) return void 0;
376
+ const span = Sentry.getActiveSpan();
377
+ if (!span) return void 0;
378
+ return span.spanContext?.().traceId;
379
+ }
368
380
  function setDeploySentryTag(key, value) {
369
381
  if (!Sentry) return;
370
382
  Sentry.setTag(key, value);
@@ -385,10 +397,20 @@ async function flush() {
385
397
  }
386
398
 
387
399
  // src/memory-report.ts
400
+ function isBunRuntime() {
401
+ return typeof globalThis.Bun !== "undefined" || typeof process.versions.bun === "string";
402
+ }
388
403
  var DEFAULT_THRESHOLD_MB = 1500;
389
404
  function toMb2(bytes) {
390
405
  return Math.round(bytes / 1024 / 1024 * 100) / 100;
391
406
  }
407
+ function safeHeap(f) {
408
+ try {
409
+ return f();
410
+ } catch {
411
+ return void 0;
412
+ }
413
+ }
392
414
  function countByType(items) {
393
415
  const counts = {};
394
416
  for (const item of items) {
@@ -399,13 +421,17 @@ function countByType(items) {
399
421
  }
400
422
  function readActiveHandles() {
401
423
  const proc = process;
402
- const handles = typeof proc._getActiveHandles === "function" ? proc._getActiveHandles() : [];
403
- return countByType(handles);
424
+ return safeHeap(() => {
425
+ const handles = typeof proc._getActiveHandles === "function" ? proc._getActiveHandles() : [];
426
+ return countByType(handles);
427
+ }) ?? {};
404
428
  }
405
429
  function readActiveRequests() {
406
430
  const proc = process;
407
- const reqs = typeof proc._getActiveRequests === "function" ? proc._getActiveRequests() : [];
408
- return countByType(reqs);
431
+ return safeHeap(() => {
432
+ const reqs = typeof proc._getActiveRequests === "function" ? proc._getActiveRequests() : [];
433
+ return countByType(reqs);
434
+ }) ?? {};
409
435
  }
410
436
  function readPolkadotApiVersion() {
411
437
  try {
@@ -432,8 +458,8 @@ function buildMemoryReport(input) {
432
458
  deploy: input.deploy,
433
459
  memory: { peak: input.peak, stages: input.stages },
434
460
  v8: {
435
- heapStatistics: v8.getHeapStatistics(),
436
- heapSpaceStatistics: v8.getHeapSpaceStatistics()
461
+ heapStatistics: safeHeap(() => v8.getHeapStatistics()),
462
+ heapSpaceStatistics: safeHeap(() => v8.getHeapSpaceStatistics())
437
463
  },
438
464
  runtime: {
439
465
  nodeVersion: process.version,
@@ -454,6 +480,9 @@ function maybeWriteMemoryReport(input) {
454
480
  if (process.env.BULLETIN_DEPLOY_MEM_REPORT === "0") {
455
481
  return { status: "disabled", thresholdMb, peakRssMb };
456
482
  }
483
+ if (isBunRuntime()) {
484
+ return { status: "unsupported-runtime", thresholdMb, peakRssMb };
485
+ }
457
486
  if (!Number.isFinite(thresholdMb) || peakRssMb < thresholdMb) {
458
487
  return { status: "below-threshold", thresholdMb, peakRssMb };
459
488
  }
@@ -482,7 +511,9 @@ function sampleFromBytes(m) {
482
511
  }
483
512
 
484
513
  export {
514
+ isBunRuntime,
485
515
  DEFAULT_THRESHOLD_MB,
516
+ safeHeap,
486
517
  buildMemoryReport,
487
518
  maybeWriteMemoryReport,
488
519
  sampleFromBytes,
@@ -504,6 +535,7 @@ export {
504
535
  withDeploySpan,
505
536
  setDeployReportContext,
506
537
  setDeployAttribute,
538
+ getCurrentSentryTraceId,
507
539
  setDeploySentryTag,
508
540
  captureWarning,
509
541
  flush
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-Q42TQHNL.js";
3
+ } from "./chunk-4FUUYJP2.js";
4
4
 
5
5
  // src/version-check.ts
6
6
  import { execSync, execFileSync } from "child_process";
@@ -0,0 +1,223 @@
1
+ import {
2
+ classifyErrorArea,
3
+ isInteractive,
4
+ promptYesNo
5
+ } from "./chunk-4UNQGG3O.js";
6
+ import {
7
+ VERSION,
8
+ getCurrentSentryTraceId
9
+ } from "./chunk-4FUUYJP2.js";
10
+
11
+ // src/bug-report.ts
12
+ import { execSync, execFileSync } from "child_process";
13
+ import * as os from "os";
14
+ var _deployContext = {};
15
+ function setDeployContext(ctx) {
16
+ _deployContext = { ..._deployContext, ...ctx };
17
+ }
18
+ function hasGhCli() {
19
+ try {
20
+ execSync("gh --version", { stdio: "pipe" });
21
+ return true;
22
+ } catch {
23
+ return false;
24
+ }
25
+ }
26
+ var LOG_TAIL_BYTES = 32 * 1024;
27
+ var _logBuffer = "";
28
+ var _logCaptureInstalled = false;
29
+ function installLogCapture() {
30
+ if (_logCaptureInstalled) return;
31
+ _logCaptureInstalled = true;
32
+ const append = (args) => {
33
+ const line = args.map((a) => typeof a === "string" ? a : safeStringify(a)).join(" ") + "\n";
34
+ _logBuffer += line;
35
+ if (_logBuffer.length > LOG_TAIL_BYTES * 2) {
36
+ _logBuffer = _logBuffer.slice(_logBuffer.length - LOG_TAIL_BYTES);
37
+ }
38
+ };
39
+ const wrap = (key) => {
40
+ const orig = console[key].bind(console);
41
+ console[key] = (...a) => {
42
+ append(a);
43
+ orig(...a);
44
+ };
45
+ };
46
+ wrap("log");
47
+ wrap("error");
48
+ wrap("warn");
49
+ }
50
+ function safeStringify(v) {
51
+ try {
52
+ if (v instanceof Error) return v.stack || v.message;
53
+ return JSON.stringify(v);
54
+ } catch {
55
+ return String(v);
56
+ }
57
+ }
58
+ function getCapturedTail() {
59
+ if (!_logBuffer) return "";
60
+ const tail = _logBuffer.length > LOG_TAIL_BYTES ? "\u2026 [truncated]\n" + _logBuffer.slice(_logBuffer.length - LOG_TAIL_BYTES) : _logBuffer;
61
+ return scrubSecrets(tail);
62
+ }
63
+ function scrubSecrets(text) {
64
+ if (!text) return text;
65
+ let out = text;
66
+ out = out.replace(/(--mnemonic(?:=|\s+))("[^"]*"|'[^']*'|\S+)/gi, "$1<REDACTED>");
67
+ out = out.replace(/(--password(?:=|\s+))("[^"]*"|'[^']*'|\S+)/gi, "$1<REDACTED>");
68
+ out = out.replace(/\b(MNEMONIC|PASSWORD|BULLETIN_MNEMONIC|GITHUB_TOKEN|GH_TOKEN|NPM_TOKEN|SENTRY_AUTH_TOKEN)=([^\s]+)/gi, "$1=<REDACTED>");
69
+ out = out.replace(/\b(ghp|ghs|gho|ghu|ghr)_[A-Za-z0-9]{20,}\b/g, "<REDACTED_TOKEN>");
70
+ out = out.replace(/\bgithub_pat_[A-Za-z0-9_]{20,}\b/g, "<REDACTED_TOKEN>");
71
+ out = out.replace(/\b(?:[a-z]{3,10}\s+){11}[a-z]{3,10}\b/g, "<REDACTED_MNEMONIC>");
72
+ out = out.replace(/([a-z][a-z0-9+.-]*:\/\/)[^:@\s]+:[^@\s]+@/gi, "$1<REDACTED>@");
73
+ return out;
74
+ }
75
+ function buildCliFlagsSummary(flags) {
76
+ const parts = [];
77
+ if (flags.bootstrap) parts.push("--bootstrap");
78
+ if (flags.jsMerkle) parts.push("--js-merkle");
79
+ if (flags.ghPagesMirror) parts.push("--gh-pages-mirror");
80
+ if (flags.poolSize != null) parts.push(`--pool-size ${String(flags.poolSize)}`);
81
+ if (typeof flags.tag === "string" && flags.tag) parts.push(`--tag ${flags.tag}`);
82
+ if (flags.mnemonic) parts.push("--mnemonic <set>");
83
+ if (flags.password) parts.push("--password <set>");
84
+ if (flags.derivationPath) parts.push("--derivation-path <set>");
85
+ if (typeof flags.rpc === "string" && flags.rpc) parts.push("--rpc <set>");
86
+ return parts.join(" ");
87
+ }
88
+ function buildReportBody(error) {
89
+ const lines = [
90
+ "## Environment",
91
+ "",
92
+ `- **bulletin-deploy**: ${VERSION}`,
93
+ `- **Node.js**: ${process.version}`,
94
+ `- **OS**: ${os.platform()} ${os.arch()} ${os.release()}`,
95
+ "",
96
+ "## Error",
97
+ "",
98
+ "```",
99
+ scrubSecrets(error.stack || error.message),
100
+ "```",
101
+ ""
102
+ ];
103
+ const ctx = _deployContext;
104
+ const traceId = getCurrentSentryTraceId();
105
+ const hasCtx = ctx.domain || ctx.repo || ctx.rpc || ctx.cliFlags || ctx.ci?.runUrl || traceId;
106
+ if (hasCtx) {
107
+ lines.push("## Deploy Context", "");
108
+ if (ctx.domain) lines.push(`- **Domain**: ${ctx.domain}`);
109
+ if (ctx.repo) lines.push(`- **Repo**: ${ctx.repo}`);
110
+ if (ctx.branch) lines.push(`- **Branch**: ${ctx.branch}`);
111
+ if (ctx.signerMode) lines.push(`- **Signer mode**: ${ctx.signerMode}`);
112
+ if (ctx.chunkCount != null) lines.push(`- **Chunks**: ${ctx.chunkCount}`);
113
+ if (ctx.totalSize) lines.push(`- **Total size**: ${ctx.totalSize}`);
114
+ if (ctx.rpc) lines.push(`- **RPC**: ${ctx.rpc}`);
115
+ if (ctx.deployTag) lines.push(`- **Deploy tag**: ${ctx.deployTag}`);
116
+ if (ctx.cliFlags) lines.push(`- **CLI flags**: \`${ctx.cliFlags}\``);
117
+ if (traceId) lines.push(`- **Sentry trace**: ${traceId}`);
118
+ lines.push("");
119
+ }
120
+ if (ctx.ci?.runUrl) {
121
+ lines.push("## CI", "");
122
+ lines.push(`- **Run**: ${ctx.ci.runUrl}`);
123
+ if (ctx.ci.workflow) lines.push(`- **Workflow**: ${ctx.ci.workflow}`);
124
+ if (ctx.ci.job) lines.push(`- **Job**: ${ctx.ci.job}`);
125
+ if (ctx.ci.sha) lines.push(`- **SHA**: ${ctx.ci.sha}`);
126
+ lines.push("");
127
+ }
128
+ const tail = getCapturedTail();
129
+ if (tail) {
130
+ lines.push("## Log tail", "", "<details><summary>Last ~32 KB of stdout/stderr (secrets scrubbed)</summary>", "", "```", tail.trimEnd(), "```", "", "</details>", "");
131
+ }
132
+ return lines.join("\n");
133
+ }
134
+ function buildTitle(error) {
135
+ const msg = error.message.slice(0, 60);
136
+ return `[deploy-bug] ${msg}`;
137
+ }
138
+ function buildLabels(error) {
139
+ const labels = ["bug", "auto-report"];
140
+ const area = classifyErrorArea(error.message);
141
+ if (area) labels.push(area);
142
+ return labels;
143
+ }
144
+ function createGhIssue(title, body, labels) {
145
+ const args = [
146
+ "issue",
147
+ "create",
148
+ "--repo",
149
+ "paritytech/bulletin-deploy",
150
+ "--title",
151
+ title,
152
+ ...labels.flatMap((l) => ["--label", l]),
153
+ "--body-file",
154
+ "-"
155
+ ];
156
+ const out = execFileSync("gh", args, { input: body, stdio: ["pipe", "pipe", "inherit"] });
157
+ return out.toString("utf8").trim();
158
+ }
159
+ function applyCoreLabels(issueUrl) {
160
+ try {
161
+ execFileSync(
162
+ "gh",
163
+ ["issue", "edit", issueUrl, "--add-label", "bug", "--add-label", "auto-report"],
164
+ { stdio: ["ignore", "pipe", "pipe"] }
165
+ );
166
+ return true;
167
+ } catch {
168
+ return false;
169
+ }
170
+ }
171
+ async function offerBugReport(error) {
172
+ if (!isInteractive()) return;
173
+ const yes = await promptYesNo("\n This looks like a bug. Open an issue with debug info? [Y/n] ");
174
+ if (!yes) return;
175
+ const title = buildTitle(error);
176
+ const body = buildReportBody(error);
177
+ const labels = buildLabels(error);
178
+ if (!hasGhCli()) {
179
+ console.error("\n gh CLI not found. Debug info below \u2014 paste into a new issue:\n");
180
+ console.error(` https://github.com/paritytech/bulletin-deploy/issues/new
181
+ `);
182
+ printFallback(title, body, labels);
183
+ return;
184
+ }
185
+ try {
186
+ const url = createGhIssue(title, body, labels);
187
+ console.error(` Issue created: ${url}`);
188
+ return;
189
+ } catch {
190
+ }
191
+ try {
192
+ console.error(" Retrying without labels...");
193
+ const url = createGhIssue(title, body, []);
194
+ const applied = applyCoreLabels(url);
195
+ if (applied) {
196
+ console.error(` Issue created: ${url} (labels applied after retry)`);
197
+ } else {
198
+ console.error(` Issue created: ${url} (labels could not be applied; please add 'bug' and 'auto-report' manually)`);
199
+ }
200
+ } catch {
201
+ console.error(" Failed to create issue. Debug info below:\n");
202
+ printFallback(title, body, labels);
203
+ }
204
+ }
205
+ function printFallback(title, body, labels) {
206
+ console.error(` Title: ${title}`);
207
+ console.error(` Labels: ${labels.join(", ")}
208
+ `);
209
+ console.error(body);
210
+ }
211
+
212
+ export {
213
+ setDeployContext,
214
+ installLogCapture,
215
+ getCapturedTail,
216
+ scrubSecrets,
217
+ buildCliFlagsSummary,
218
+ buildReportBody,
219
+ buildTitle,
220
+ buildLabels,
221
+ createGhIssue,
222
+ offerBugReport
223
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  captureWarning,
3
3
  withSpan
4
- } from "./chunk-Q42TQHNL.js";
4
+ } from "./chunk-4FUUYJP2.js";
5
5
  import {
6
6
  isTestnetSpecName
7
7
  } from "./chunk-JHNW2EKY.js";
@@ -1,10 +1,13 @@
1
+ import {
2
+ setDeployContext
3
+ } from "./chunk-LMSMDKVU.js";
1
4
  import {
2
5
  DotNS,
3
6
  TX_TIMEOUT_MS,
4
7
  fetchNonce,
5
8
  popStatusName,
6
9
  validateDomainLabel
7
- } from "./chunk-OCOCVZQV.js";
10
+ } from "./chunk-NEV6WTYM.js";
8
11
  import {
9
12
  MirrorSkipped,
10
13
  mirrorToGitHubPages,
@@ -23,7 +26,7 @@ import {
23
26
  truncateAddress,
24
27
  withDeploySpan,
25
28
  withSpan
26
- } from "./chunk-Q42TQHNL.js";
29
+ } from "./chunk-4FUUYJP2.js";
27
30
  import {
28
31
  merkleizeJS
29
32
  } from "./chunk-B7GUYYAN.js";
@@ -77,7 +80,7 @@ var POOL_SIZE = DEFAULT_POOL_SIZE;
77
80
  var CHUNK_SIZE = 2 * 1024 * 1024;
78
81
  var MAX_FILE_SIZE = 8 * 1024 * 1024;
79
82
  var MAX_RECONNECTIONS = parseInt(process.env.BULLETIN_MAX_RECONNECTIONS ?? "3", 10);
80
- var CHUNK_TIMEOUT_MS = parseInt(process.env.BULLETIN_CHUNK_TIMEOUT_MS ?? "60000", 10);
83
+ var CHUNK_TIMEOUT_MS = parseInt(process.env.BULLETIN_CHUNK_TIMEOUT_MS ?? "180000", 10);
81
84
  var RETRY_BASE_DELAY_MS = 2e3;
82
85
  var RETRY_MAX_DELAY_MS = 15e3;
83
86
  var WS_HEARTBEAT_TIMEOUT_MS = 3e5;
@@ -616,6 +619,10 @@ async function storeDirectory(directoryPath, providerOrOptions = {}, password, j
616
619
  carBytes: carContent.length,
617
620
  outputDir: path.dirname(directoryPath)
618
621
  });
622
+ setDeployContext({
623
+ chunkCount: carChunks.length,
624
+ totalSize: `${(carContent.length / 1024 / 1024).toFixed(2)} MB`
625
+ });
619
626
  const carMb = String(Math.round(carContent.length / 1024 / 1024 * 100) / 100);
620
627
  const storageCid = await withSpan("deploy.chunk-upload", "1b. chunk-upload", { "deploy.chunks.total": String(carChunks.length), "deploy.car.bytes": String(carContent.length), "deploy.car.mb": carMb }, async () => {
621
628
  sampleMemory("chunk_upload_start");
package/dist/deploy.js CHANGED
@@ -24,10 +24,12 @@ import {
24
24
  storeChunkedContent,
25
25
  storeDirectory,
26
26
  storeFile
27
- } from "./chunk-G3YCSH44.js";
28
- import "./chunk-OCOCVZQV.js";
27
+ } from "./chunk-VJLTIZ6S.js";
28
+ import "./chunk-LMSMDKVU.js";
29
+ import "./chunk-4UNQGG3O.js";
30
+ import "./chunk-NEV6WTYM.js";
29
31
  import "./chunk-2Q2WSKFD.js";
30
- import "./chunk-Q42TQHNL.js";
32
+ import "./chunk-4FUUYJP2.js";
31
33
  import "./chunk-B7GUYYAN.js";
32
34
  import "./chunk-JHNW2EKY.js";
33
35
  import "./chunk-QGM4M3NI.js";
package/dist/dotns.js CHANGED
@@ -29,8 +29,8 @@ import {
29
29
  simulateUserStatus,
30
30
  stripTrailingDigits,
31
31
  validateDomainLabel
32
- } from "./chunk-OCOCVZQV.js";
33
- import "./chunk-Q42TQHNL.js";
32
+ } from "./chunk-NEV6WTYM.js";
33
+ import "./chunk-4FUUYJP2.js";
34
34
  import "./chunk-JHNW2EKY.js";
35
35
  import "./chunk-QGM4M3NI.js";
36
36
  export {
package/dist/index.js CHANGED
@@ -1,11 +1,13 @@
1
1
  import {
2
2
  deploy
3
- } from "./chunk-G3YCSH44.js";
3
+ } from "./chunk-VJLTIZ6S.js";
4
+ import "./chunk-LMSMDKVU.js";
5
+ import "./chunk-4UNQGG3O.js";
4
6
  import {
5
7
  DotNS
6
- } from "./chunk-OCOCVZQV.js";
8
+ } from "./chunk-NEV6WTYM.js";
7
9
  import "./chunk-2Q2WSKFD.js";
8
- import "./chunk-Q42TQHNL.js";
10
+ import "./chunk-4FUUYJP2.js";
9
11
  import {
10
12
  merkleizeJS
11
13
  } from "./chunk-B7GUYYAN.js";
@@ -1,5 +1,6 @@
1
1
  import * as v8 from 'node:v8';
2
2
 
3
+ declare function isBunRuntime(): boolean;
3
4
  declare const DEFAULT_THRESHOLD_MB = 1500;
4
5
  interface MemorySampleMb {
5
6
  rssMb: number;
@@ -35,8 +36,8 @@ interface MemoryReport {
35
36
  stages: Record<string, MemorySampleMb>;
36
37
  };
37
38
  v8: {
38
- heapStatistics: ReturnType<typeof v8.getHeapStatistics>;
39
- heapSpaceStatistics: ReturnType<typeof v8.getHeapSpaceStatistics>;
39
+ heapStatistics: ReturnType<typeof v8.getHeapStatistics> | undefined;
40
+ heapSpaceStatistics: ReturnType<typeof v8.getHeapSpaceStatistics> | undefined;
40
41
  };
41
42
  runtime: {
42
43
  nodeVersion: string;
@@ -53,6 +54,7 @@ interface MemoryReport {
53
54
  clientsCreated?: number;
54
55
  };
55
56
  }
57
+ declare function safeHeap<T>(f: () => T): T | undefined;
56
58
  declare function buildMemoryReport(input: {
57
59
  thresholdMb: number;
58
60
  peak: MemorySampleMb;
@@ -76,7 +78,7 @@ interface MaybeWriteMemoryReportInput {
76
78
  }
77
79
  interface MemoryReportResult {
78
80
  /** Why the report did/didn't fire. Useful for tests and CLI logging. */
79
- status: "disabled" | "below-threshold" | "not-internal" | "written";
81
+ status: "disabled" | "below-threshold" | "not-internal" | "written" | "unsupported-runtime";
80
82
  thresholdMb: number;
81
83
  peakRssMb: number;
82
84
  path?: string;
@@ -90,4 +92,4 @@ declare function sampleFromBytes(m: {
90
92
  arrayBuffers: number;
91
93
  }): MemorySampleMb;
92
94
 
93
- export { DEFAULT_THRESHOLD_MB, type DeployContextForReport, type MaybeWriteMemoryReportInput, type MemoryReport, type MemoryReportResult, type MemorySampleMb, buildMemoryReport, maybeWriteMemoryReport, sampleFromBytes };
95
+ export { DEFAULT_THRESHOLD_MB, type DeployContextForReport, type MaybeWriteMemoryReportInput, type MemoryReport, type MemoryReportResult, type MemorySampleMb, buildMemoryReport, isBunRuntime, maybeWriteMemoryReport, safeHeap, sampleFromBytes };
@@ -1,13 +1,17 @@
1
1
  import {
2
2
  DEFAULT_THRESHOLD_MB,
3
3
  buildMemoryReport,
4
+ isBunRuntime,
4
5
  maybeWriteMemoryReport,
6
+ safeHeap,
5
7
  sampleFromBytes
6
- } from "./chunk-Q42TQHNL.js";
8
+ } from "./chunk-4FUUYJP2.js";
7
9
  import "./chunk-QGM4M3NI.js";
8
10
  export {
9
11
  DEFAULT_THRESHOLD_MB,
10
12
  buildMemoryReport,
13
+ isBunRuntime,
11
14
  maybeWriteMemoryReport,
15
+ safeHeap,
12
16
  sampleFromBytes
13
17
  };
@@ -26,8 +26,9 @@ declare function setDeployReportContext(patch: Partial<DeployContextForReport> &
26
26
  outputDir?: string;
27
27
  }): void;
28
28
  declare function setDeployAttribute(key: string, value: string | number | boolean): void;
29
+ declare function getCurrentSentryTraceId(): string | undefined;
29
30
  declare function setDeploySentryTag(key: string, value: string): void;
30
31
  declare function captureWarning(message: string, context?: Record<string, unknown>): void;
31
32
  declare function flush(): Promise<void>;
32
33
 
33
- export { type InternalContextSignals, VERSION, captureWarning, flush, getDeployAttributes, initTelemetry, isExpectedError, isInternalContext, isInternalContextFromSignals, resolveRepo, resolveRunner, resolveRunnerType, sampleMemory, sanitizeBranch, sanitizeRepo, scrubPaths, setDeployAttribute, setDeployReportContext, setDeploySentryTag, truncateAddress, withDeploySpan, withSpan };
34
+ export { type InternalContextSignals, VERSION, captureWarning, flush, getCurrentSentryTraceId, getDeployAttributes, initTelemetry, isExpectedError, isInternalContext, isInternalContextFromSignals, resolveRepo, resolveRunner, resolveRunnerType, sampleMemory, sanitizeBranch, sanitizeRepo, scrubPaths, setDeployAttribute, setDeployReportContext, setDeploySentryTag, truncateAddress, withDeploySpan, withSpan };
package/dist/telemetry.js CHANGED
@@ -2,6 +2,7 @@ import {
2
2
  VERSION,
3
3
  captureWarning,
4
4
  flush,
5
+ getCurrentSentryTraceId,
5
6
  getDeployAttributes,
6
7
  initTelemetry,
7
8
  isExpectedError,
@@ -20,12 +21,13 @@ import {
20
21
  truncateAddress,
21
22
  withDeploySpan,
22
23
  withSpan
23
- } from "./chunk-Q42TQHNL.js";
24
+ } from "./chunk-4FUUYJP2.js";
24
25
  import "./chunk-QGM4M3NI.js";
25
26
  export {
26
27
  VERSION,
27
28
  captureWarning,
28
29
  flush,
30
+ getCurrentSentryTraceId,
29
31
  getDeployAttributes,
30
32
  initTelemetry,
31
33
  isExpectedError,
@@ -8,8 +8,8 @@ import {
8
8
  isPreReleaseVersion,
9
9
  preReleaseWarning,
10
10
  promptYesNo
11
- } from "./chunk-PA3AS7QR.js";
12
- import "./chunk-Q42TQHNL.js";
11
+ } from "./chunk-4UNQGG3O.js";
12
+ import "./chunk-4FUUYJP2.js";
13
13
  import "./chunk-QGM4M3NI.js";
14
14
  export {
15
15
  assessVersion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulletin-deploy",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",