Skip to content

feat(cli): opt-in crash reporting to Better Stack via Sentry SDK#595

Open
NisargIO wants to merge 1 commit into
mainfrom
feat/betterstack-error-tracking
Open

feat(cli): opt-in crash reporting to Better Stack via Sentry SDK#595
NisargIO wants to merge 1 commit into
mainfrom
feat/betterstack-error-tracking

Conversation

@NisargIO
Copy link
Copy Markdown
Member

Summary

Adds strictly opt-in crash reporting for the react-doctor CLI to Better Stack (via the Sentry SDK), plus debug-ID source-map upload at publish time so reported stack traces de-minify to the original TypeScript.

Mirrors the repo's existing opt-in OTLP posture: nothing is sent unless the user sets REACT_DOCTOR_ERROR_REPORTING=1, and @sentry/node is lazy-imported only on that opt-in, so the common (non-opted-in) run pays zero startup cost and never loads the SDK.

What's included

Capture

  • cli/utils/error-tracking.tsinitErrorTracking() (no global handlers, no OpenTelemetry — the CLI owns its own SIGINT/SIGTERM/EPIPE/exit) + captureCliError() that flushes before the process exits.
  • Reported at every fatal chokepoint: the top-level .catch, the inspect/install command catches, and new process-level uncaughtException / unhandledRejection safety nets — all routed through a single reportFatalError sink.

Context (enriches each event; non-source only — mirrors what the prefilled GitHub issue already exposes)

  • Searchable tags: origin (handled command vs top-level vs uncaughtException vs unhandledRejection), command, ci + ci.provider, coding_agent, json_mode, interactive, node.version, platform, arch.
  • Structured react-doctor context block: version, argv, cwd (ENOENT-guarded), runtime, CI/agent, OTLP-configured flags.

Source maps

  • Production build runs sentry-cli sourcemaps inject dist, so the shipped bundle carries debug IDs.
  • publish.yml uploads matching maps to Better Stack on both the stable release (gated on outputs.published) and the dev-snapshot job. Maps match by debug ID and are not shipped in the npm tarball (files ships dist/**/*.js, not *.map).
  • @sentry/cli added to root onlyBuiltDependencies (binary postinstall under pnpm); @sentry/node externalized in the bundle.

⚠️ Required GitHub config (source-map upload skips cleanly until set)

Type Name Value
Secret BETTER_STACK_SOURCEMAP_TOKEN Telemetry API token
Variable BETTER_STACK_SOURCEMAP_URL region source-maps endpoint (e.g. https://eu-fsn-3-sourcemaps.betterstackdata.com — confirm in dashboard)
Variable BETTER_STACK_SOURCEMAP_ORG Better Stack team ID
Variable BETTER_STACK_SOURCEMAP_PROJECT 2476923

Notes / judgment calls

  • Install weight: @sentry/node v10 pulls in the OpenTelemetry tree (externalized + lazy-loaded, but still installed). Can swap to the lighter @sentry/node-core if preferred.
  • Changeset: patch (the fixed group would otherwise bump the lint plugins on a minor). Bump to minor if you'd rather mark it a feature.
  • PII: cwd and post-node argv are included, consistent with handle-error.ts's issue template. Easy to drop if you'd rather keep paths out entirely.
  • Behavior change: uncaught exceptions / unhandled rejections now render via handleError (styled + prefilled issue URL) and exit 1, instead of Node's raw stack dump.

Test plan

  • typecheck ✓, lint ✓ (0 errors), format:check ✓, build ✓ (debug IDs verified in bundle), smoke:json-report
  • New tests/error-tracking.test.ts (3 cases) mocks @sentry/node and asserts the real captureCliError attaches the expected tags/contexts (local inspect; CI + Claude Code install via uncaughtException; generic CI=true + OTLP via unhandledRejection).
  • Full suite: 1418 pass (one pre-existing flaky /tmp cleanup race in install-git-hook.test.ts, passes 29/29 in isolation).
  • To test end-to-end (sends one real event): REACT_DOCTOR_ERROR_REPORTING=1 against a run that throws.

🤖 Generated with Claude Code

Add strictly opt-in error tracking for the CLI, mirroring the existing
opt-in OTLP posture: nothing is sent unless the user sets
REACT_DOCTOR_ERROR_REPORTING=1, and @sentry/node is lazy-imported only
on that opt-in so the common path pays zero cost.

- error-tracking.ts: init (no global handlers, no OTel — the CLI owns its
  own SIGINT/EPIPE/exit) + captureCliError that flushes before exit.
- Capture at every fatal chokepoint: the top-level .catch, the inspect /
  install command catches, plus new process-level uncaughtException /
  unhandledRejection safety nets routed through one reportFatalError sink.
- Enrich each event with non-source debugging context: origin (handled vs
  uncaught vs unhandled), command, CI + provider, coding agent, platform,
  node, TTY, JSON mode, OTLP-configured flags.
- Source maps: production build injects debug IDs (sentry-cli sourcemaps
  inject) and publish.yml uploads matching maps to Better Stack (gated on
  BETTER_STACK_SOURCEMAP_TOKEN). Maps match by debug ID and stay out of
  the npm tarball.
- @sentry/cli added to onlyBuiltDependencies so its binary postinstall is
  allowed under pnpm; @sentry/node externalized in the bundle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

React Doctor

React Doctor found 14 files changed in this pull request, but none matched the files covered by its enabled checks.

Scope: 14 files changed on feat/betterstack-error-tracking vs. main.

View workflow run

Generated by React Doctor. Questions? Contact founders@million.dev.

@NisargIO NisargIO requested a review from aidenybai May 30, 2026 05:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant