feat(cli): add sentry cli defaults command for persistent settings#721
feat(cli): add sentry cli defaults command for persistent settings#721
sentry cli defaults command for persistent settings#721Conversation
Semver Impact of This PR🟡 Minor (new features) 📋 Changelog PreviewThis is how your changes will appear in the changelog. New Features ✨
Bug Fixes 🐛Upgrade
Other
Internal Changes 🔧
🤖 This preview updates automatically when you update the PR. |
|
Codecov Results 📊✅ 134 passed | Total: 134 | Pass Rate: 100% | Execution Time: 0ms 📊 Comparison with Base Branch
✨ No test changes detected All tests are passing successfully. ✅ Patch coverage is 99.25%. Project has 1590 uncovered lines. Files with missing lines (1)
Coverage diff@@ Coverage Diff @@
## main #PR +/-##
==========================================
+ Coverage 95.30% 95.35% +0.05%
==========================================
Files 232 234 +2
Lines 33993 34208 +215
Branches 0 0 —
==========================================
+ Hits 32395 32618 +223
- Misses 1598 1590 -8
- Partials 0 0 —Generated by Codecov Action |
| * Used for display in show mode to help users understand when env vars | ||
| * override their stored preference. | ||
| */ | ||
| function computeTelemetryEffective(): DefaultsResult["telemetryEffective"] { |
There was a problem hiding this comment.
Shouldn't this live in a generic helper library for Sentry.init() system to use? I'm also concerned about how we track auto-completions. I'm assuming they'd still honor this and drop the temp entries from the DB without sending them?
There was a problem hiding this comment.
isTelemetryEnabled() already lives in src/lib/telemetry.ts (the generic helper) which withTelemetry() calls. computeTelemetryEffective() stays in the command file because it is display-only — it computes the source string for the human formatter, not just the boolean.
Re completions: they honor this correctly. The completion fast-path sets SENTRY_CLI_NO_TELEMETRY=1 (highest priority in isTelemetryEnabled()). Deferred queue entries are only drained when enabled=true in withTelemetry(), so if the persistent preference is off, queued entries are never sent.
| /** | ||
| * Read the current value of a single default for display or change tracking. | ||
| */ | ||
| function getCurrentValue(key: DefaultKey): string | boolean | null { |
There was a problem hiding this comment.
I feel like a map of key to handlers would be easier to maintain compared to a big switch statement?
There was a problem hiding this comment.
Done — replaced all three switch functions (getCurrentValue, setDefault, clearDefault) with a single DEFAULTS_REGISTRY: Record<DefaultKey, DefaultHandler> map. Each key maps to { get, set, clear } handlers. The command dispatch just does handler.get() / handler.set(value) / handler.clear().
| type DefaultKey = "organization" | "project" | "telemetry" | "url"; | ||
|
|
||
| /** Map user-facing key names to canonical DefaultsState keys */ | ||
| function normalizeKey(key: string): DefaultKey | null { |
There was a problem hiding this comment.
Should we instead have a global ALIASES const was a map org -> organization and just use key.toLowerCase() and that map instead of a switch?
There was a problem hiding this comment.
Done — replaced with a KEY_ALIASES: Record<string, DefaultKey> map. normalizeKey() is now a one-liner: KEY_ALIASES[key.toLowerCase()] ?? null.
| */ | ||
| function computeTelemetryEffective(): DefaultsResult["telemetryEffective"] { | ||
| const env = getEnv(); | ||
| if (env.SENTRY_CLI_NO_TELEMETRY === "1") { |
There was a problem hiding this comment.
These ENV variable names should probably be in some constant?
There was a problem hiding this comment.
Done — extracted TELEMETRY_ENV_VAR and DO_NOT_TRACK_ENV_VAR as exported constants in src/lib/telemetry.ts. Used in both isTelemetryEnabled() and computeTelemetryEffective(). The formatter also simplified to just check source.startsWith("env:") and slice the env var name.
| /** | ||
| * Set a default value. Validates and stores the value. | ||
| */ | ||
| function setDefault(key: DefaultKey, value: string): void { |
There was a problem hiding this comment.
Same as above: name -> handler pattern rather than a switch?
There was a problem hiding this comment.
Done — same DEFAULTS_REGISTRY handler map covers this too. See reply on the other handler map thread.
9357c90 to
9993366
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d934972. Configure here.
Implement a command to view and manage persistent CLI defaults: organization, project, telemetry preference, and Sentry URL (for self-hosted instances). - Consolidate storage from the unused `defaults` single-row table to the `metadata` KV table (migration 13 copies any data, then drops the table) - Add `parseBoolValue()` utility for human-friendly boolean parsing (on/off, yes/no, true/false, 1/0) adapted from Sentry JS SDK's `envToBool` - Add persistent telemetry opt-out with priority chain: SENTRY_CLI_NO_TELEMETRY > DO_NOT_TRACK > SQLite preference > default (on) - Add persistent URL default applied in `preloadProjectContext()` after `.sentryclirc` env shim, using the same `env.SENTRY_URL` mechanism - Property-based tests for parseBoolValue, unit tests for defaults storage and isTelemetryEnabled priority chain Closes #304

Summary
Implements
sentry cli defaults— a command for viewing and managing persistent CLI defaults. Closes #304.sentry cli defaultsdisplays all current settingssentry cli defaults org my-org,sentry cli defaults telemetry off, etc.sentry cli defaults org --clearsentry cli defaults --clear --yesSupported keys:
org,project,telemetry(on/off/yes/no/true/false/1/0),url(for self-hosted).Storage consolidation
The
defaultssingle-row table was never written by production code. This PR:metadataKV table (keys:defaults.org,defaults.project,defaults.telemetry,defaults.url)metadatainstead ofdefaultsPublic getters (
getDefaultOrganization(),getDefaultProject()) keep the same signatures — all consumers work unchanged.Telemetry preference
Adds persistent telemetry opt-out with a 4-level priority chain:
SENTRY_CLI_NO_TELEMETRY=1env var (highest)DO_NOT_TRACK=1env var (consoledonottrack.com)sentry cli defaults telemetry offURL default
Adds persistent URL default for self-hosted instances, applied in
preloadProjectContext()after.sentryclircenv shim using the sameenv.SENTRY_URLmechanism.New utility
parseBoolValue()insrc/lib/parse-bool.ts— human-friendly boolean parser adapted from Sentry JS SDK'senvToBool. Handles on/off, yes/no, true/false, 1/0, t/f, y/n.Tests
parseBoolValue(8 properties, 50 runs each)isTelemetryEnabled()priority chain (26 tests)setDefaults()function