feat(config): follow XDG Base Directory spec for config location - #1503
Conversation
Resolve the config/data directory via the XDG Base Directory specification instead of always using `~/.sentry`. Precedence: 1. `SENTRY_CONFIG_DIR` override (unchanged, highest priority) 2. Legacy `~/.sentry` when it already exists (no breakage for existing installs) 3. `/sentry`, defaulting to `~/.config/sentry` A non-absolute `XDG_CONFIG_HOME` is ignored per the spec. This keeps credentials and caches out of the home directory root, which also unblocks environments (e.g. coding agents) that restrict writes to `/root`. Fixes #1502
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
CI green on the feature branch (check_suite success). Self-review clean. Marking ready and requesting review from the issue author. |
…onfig.json A bare ~/.sentry/bin created by the curl installer should not prevent new XDG-based installs. The legacy check now requires the presence of the actual database or the old JSON config file. Fixes the Cursor Bugbot report on PR #1503.
|
Jared, we need to do the following:
|
|
ack — will update binary install paths to follow XDG (via resolveConfigDir) and add migration logic inside |
|
Jared, update the patch already? |
|
working on it — expanding to XDG-aware binary paths + migration inside setup. will push the update shortly. |
|
yep, expanding the patch now to also move binary install paths to XDG (via XDG_BIN_HOME / ~/.local/bin) and add migration inside setup. will push the update. |
|
expanding the patch now to cover XDG binary paths + migration in setup. pushing shortly. |
|
not yet — expanding determineInstallDir + setup migration for binary paths now. will push once the XDG-aware logic + migration is in. |
|
Jared, update the patch based on our earlier discussions |
…acy layout Extend the XDG work to the binary install directory and add automatic migration of the legacy ~/.sentry layout in `sentry cli setup`. - determineInstallDir now honors an absolute XDG_BIN_HOME (after SENTRY_INSTALL_DIR) and falls back to ~/.local/bin instead of ~/.sentry/bin. - setup migrates an existing ~/.sentry/bin binary and legacy config (cli.db, config.json) into the XDG locations on first run; skipped when the target already exists. - upgrade's known-curl-path detection and fallback install path track the same XDG-aware resolution. - install script and getCurlInstallPaths recognize XDG_BIN_HOME. - resolveXdgConfigDir exposes the XDG target (bypassing legacy detection) so migration doesn't no-op. - docs: document binary install location + migration. Addresses review feedback on #1503.
|
pushed in 90c9cb2 — expanded per your two points:
tests: new XDG cases in binary.test.ts + a migration suite in setup.test.ts; full run green except one pre-existing root-only failure ( |
Reevaluated after scope expanded to include binary install paths. - Close the SQLite DB before renaming cli.db/WAL sidecars. The DB is opened at startup (cleanup-old-binary), and an open file cannot be renamed on Windows — previously the whole migration threw and was swallowed. Split config and binary migration into separate functions with independent try/catch so one failing can't skip the other. - migrateLegacyBinary now returns the new path; setup adopts it as binaryPath/binaryDir so PATH modification and setInstallInfo point at the migrated binary instead of the deleted legacy path (a --method setInstallInfo no longer overwrites it with the old location). - docs: clarify that 'sentry upgrade' keeps the binary in place (pins SENTRY_INSTALL_DIR) and only 'sentry cli setup' relocates it; config data still migrates on upgrade. - test: record-new-path regression covering the stale-path fix. Addresses cursor[bot] review on #1503.
# Conflicts: # packages/cli/plugins/sentry-cli/skills/sentry-cli/references/dashboard.md
|
merged latest main into the branch in 4148c42 — the conflict (a generated skill reference doc) is resolved and generated files are regenerated against the merged sources. PR is mergeable again; tsc + setup/binary/config tests green locally. |
|
Thanks — all five review comments are addressed in b03db3b, and threads resolved:
PR description updated to match. |
Address BugBot + Seer findings on the latest revision. - BugBot (high): findMigratableBinary scanned all known install dirs, so a stray binary in ~/.local/bin or ~/bin — both valid *current* XDG targets — could be moved out and the original deleted, leaving the real ~/.sentry/bin install behind. Replace getKnownInstallDirs with getLegacyInstallDirs, which is limited to the genuinely pre-XDG ~/.sentry/bin. Add setup tests asserting binaries in ~/.local/bin and ~/bin are never relocated. - Seer (medium): isInPath used a case-sensitive membership check; on Windows/ macOS a PATH entry can differ only in casing from a computed dir. Compare case-insensitively on those platforms, matching samePath. Add isInPath tests. Unit + e2e migration tests green; tsc + biome clean.
BYK
left a comment
There was a problem hiding this comment.
Would also be great if the tests too used node:fs/promises variants rather than the sync ones.
… list Address BYK review comments. - samePath: collapse to a single expression using a module-level CASE_INSENSITIVE_PLATFORMS Set instead of an if/branch. - isInPath (shell.ts): reuse samePath from binary.ts instead of duplicating the case-insensitive PATH comparison. - getLegacyInstallDirs: back it with a LEGACY_INSTALL_SUBDIRS array (currently the single pre-XDG ~/.sentry/bin) so the 'list' is real and extensible, matching the doc comment. No behavior change; tsc + biome clean, unit + e2e migration tests green.
The ~/.local/bin and ~/bin candidate check used pathDirs.includes(dir), a case-sensitive comparison. On Windows/macOS a PATH entry that differs only in casing from the computed dir would miss, falling back to ~/.local/bin and prompting a PATH edit when a valid dir was already present. Use samePath (case-insensitive on win32/darwin) for consistency with isInPath and the rest of the PR. Add a test covering the mixed-case PATH entry.
…paths getKnownCurlPaths appended `sep` to a raw XDG_BIN_HOME, so a value with a trailing slash (e.g. /custom/bin/) produced /custom/bin// — a double separator that made process.execPath.startsWith(dir) miss, breaking curl install-method detection and upgrade path resolution. Extract the logic into a pure, testable buildKnownCurlPaths(homeDir, env) and normalize the XDG entry with join(xdgBinHome, '.') + sep so exactly one trailing separator is emitted. Add unit tests covering the trailing-slash, absolute, and non-absolute cases.
…erant Address BYK's review plus an independent adversarial review pass. - BYK: process.platform never changes at runtime, so compute IS_CASE_INSENSITIVE_FS once at module load (dropping the Set) and pick the case-folding step statically — no per-call platform check. - Adversarial review: samePath now strips a trailing separator (never from a bare root) before comparing, so a PATH entry like ~/.local/bin/ matches the computed ~/.local/bin. This fixes false negatives across all samePath call sites (isInPath, determineInstallDir PATH matching, upgrade relocation). - install script: only honor XDG_BIN_HOME when absolute, mirroring the Node determineInstallDir logic. - Tests: trailing-separator and root-not-stripped cases for samePath. tsc + biome clean; unit + e2e migration tests green.
|
Status on the latest push (9a84530):
I've re-requested your review since all threads are now resolved. I'm holding off on merging: the PR is |
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.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 9a84530. Configure here.
…ariants - BugBot: the install-script absolute-path guard only accepted POSIX /… paths, dropping a Windows drive-letter XDG_BIN_HOME (C:\… or C:/…) while Node's determineInstallDir (isAbsolute) still installs there — so SENTRY_INIT=1 couldn't find the binary. Accept drive-letter absolute paths too. - Per review: drop the foldCase helper and select the whole samePath implementation statically from IS_CASE_INSENSITIVE_FS (toLowerCase variant vs plain comparison).
…l UI, env vars (#1579) ## Weekly Documentation Audit — 2026-09-14 This PR fixes documentation gaps found by cross-referencing the CLI implementation against its docs, focusing on changes since the last audit (2026-09-07). --- ### Gap Report #### A. Undocumented or missing commands/subcommands No new undocumented commands found. All 112 commands have auto-generated doc pages. The `local` commands (`serve`, `run`) are fully documented. #### B. Undocumented flags **`--open` flag on `sentry local serve` and `sentry local run`** (added in #1560): - Both commands added an `--open` flag to launch the browser-based Sentry Local UI at `local.sentry.dev` - The flag was listed in auto-generated Options tables but had no examples or explanation in the hand-written fragment - **Fixed**: Added a "Browser UI" section to `local.md` with usage examples and constraints #### C. Missing usage examples The Local UI (`--open`) had no examples → added. #### D. Stale descriptions No stale `brief` strings found. #### E. Missing route mappings in skill generator All routes are covered — `groupRoutesByReference()` provides automatic 1:1 mapping. #### F. Installation / distribution gaps **`SENTRY_CONFIG_DIR` stale default in env-registry.ts**: - Source: `packages/cli/src/lib/env-registry.ts` line 151–154 - The description said "Defaults to `~/.sentry/`" and `defaultValue` was `"~/.sentry/"` - Since PR #1503 (XDG Base Directory migration), the actual default is `$XDG_CONFIG_HOME/sentry/` (i.e. `~/.config/sentry/`) - **Fixed**: Updated description, defaultValue, and devGuide to reflect XDG paths - **Cascaded**: Regenerated `configuration.md` and `DEVELOPMENT.md` (auto-generated from env-registry) **Curl install detection path stale in `cli.md`**: - Source: `apps/cli-docs/src/fragments/commands/cli.md` line 56 - The upgrade detection table said curl binary is "in `~/.sentry/bin`" — this is the legacy path - The XDG-aligned default is `~/.local/bin` - **Fixed**: Table now shows both paths: "XDG: `~/.local/bin`; legacy: `~/.sentry/bin`" #### G. Undocumented environment variables All `SENTRY_*` env vars in `env-registry.ts` are documented in `configuration.md` (auto-generated). The `SENTRY_RELEASE` var injected by `local run` was missing from the fragment's env var table → **fixed**. Remaining intentionally excluded env vars (internal, test-only, or SDK-inherited): `SENTRY_ENVIRONMENT`, `SENTRY_CLI_NO_EXIT_TRAP`, `SENTRY_SCAN_DISABLE_WORKERS`, `SENTRY_CLI_INTEGRATION_TEST_VERSION_OVERRIDE`, `SENTRY_RN_*`, `SENTRY_TRACES_SAMPLE_RATE`, `SENTRY_MONITOR_SLUG`, `SENTRY_DIST`. #### H. Auth / self-hosted gaps **Auth credential storage path stale in `auth.md`**: - Source: `apps/cli-docs/src/fragments/commands/auth.md` line 109 - Said credentials stored in `~/.sentry/` — stale since XDG migration (#1503) - **Fixed**: Updated to `$XDG_CONFIG_HOME/sentry/` with legacy fallback note No other auth/self-hosted gaps found. OAuth scopes, trust anchor system, and self-hosted guide are current. #### I. Plugin/skills gaps **Newly-detected agents missing from `agentic-usage.md`**: - Source: `packages/cli/src/lib/detect-agent.ts` (`ENV_VAR_AGENTS`, `PROCESS_NAME_AGENTS`) - PR #1571 added detection for Cline, OpenClaw, Kimi, Grok, and Junie - The Cowork variant of Claude Code was also undocumented - **Fixed**: Added all 5 new agents plus Cowork to both the intro paragraph and requirements section Skill installation targets (`~/.claude`, `~/.agents`) and embedded content system are correctly documented. #### J. README / DEVELOPMENT.md drift - `packages/cli/README.md` correctly uses XDG paths — no drift - `DEVELOPMENT.md` env var table was auto-updated by the env-registry regeneration - Node.js version requirement (22.15+ dev, 20+ runtime) is correctly documented - Build/test commands in `README.md` and `DEVELOPMENT.md` match `package.json` --- ### Top 5 Most Impactful Fixes (Prioritized) 1. **`SENTRY_CONFIG_DIR` stale default** — The env-registry (source of truth for generated docs) pointed users at the wrong directory. This cascaded to `configuration.md`, `DEVELOPMENT.md`, and the `sentry --help` output. High impact because it directly misleads users about where their credentials are stored. 2. **Auth credential path stale** — The auth command docs told users their tokens live in `~/.sentry/` when they actually live in `~/.config/sentry/`. Users looking for their stored credentials would check the wrong directory. 3. **5 newly-detected agents undocumented** — Cline, Grok, Kimi, Junie, and OpenClaw users wouldn't know the CLI recognizes their agent, potentially causing confusion about skill installation behavior. 4. **`--open` / Local UI undocumented in fragment** — The browser-based Sentry Local UI is a significant new feature with no examples in the hand-written docs. Users wouldn't discover it without reading `--help`. 5. **`SENTRY_RELEASE` env var missing from local run table** — Minor but affects users who need to understand what environment variables are injected into their child process. <div><a href="https://cursor.com/agents/bc-dcce6e5a-d036-4d6c-ba6c-54750bd16fdb?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/automations/8b0c0f35-da5e-409d-984c-5e39518ffb8a"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/view-automation-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/view-automation-light.png"><img alt="View Automation" width="141" height="28" src="https://cursor.com/assets/images/view-automation-dark.png"></picture></a> </div> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Miguel Betegón <miguelbetegongarcia@gmail.com>

Closes #1502
What
sentrystored its config/data (thecli.dbSQLite database with credentials and caches) and its installed binary under~/.sentry, cluttering the home directory and breaking in environments that block writes to$HOME(e.g. sandboxed coding agents).This makes both the config directory and the binary install directory follow the XDG Base Directory specification, matching how the CLI already resolves shell/completion paths, and migrates existing
~/.sentryinstalls into the new locations on firstsetup.Config directory
getConfigDir()resolves via a pureresolveConfigDir(env, home)helper:SENTRY_CONFIG_DIR— explicit override (highest priority)~/.sentry— used only when it actually holds config (cli.dborconfig.json), so a bare~/.sentry/binfrom the installer no longer blocks XDG$XDG_CONFIG_HOME/sentry— defaulting to~/.config/sentry. A non-absoluteXDG_CONFIG_HOMEis ignored per the spec.resolveXdgConfigDir()exposes the XDG target directly (bypassing legacy detection) so migration doesn't no-op whilecli.dbstill sits in~/.sentry.Binary install directory
determineInstallDir()now resolves:SENTRY_INSTALL_DIR— explicit override$XDG_BIN_HOME— when set to an absolute path, per the XDG spec~/.local/binor~/bin— when either exists and is already onPATH~/.local/bin— default fallback (previously~/.sentry/bin)upgrade's known-curl-path detection and its fallback install path track the same resolution, and the install script recognizesXDG_BIN_HOME.Migration (in
sentry cli setup)On first run,
setupmigrates the legacy~/.sentrylayout into the XDG locations. Config and binary migration are independent (a failure in one can't skip the other):cli.db+ WAL sidecars andconfig.jsoninto the XDG config dir. Skipped when a targetcli.dbalready exists.getLegacyInstallDirs(), currently just the pre-XDG~/.sentry/bin) and, when found, copies it into the resolved install target,chmods it executable, records the new path viasetInstallInfo, and removes the old copy.~/.local/binand~/binare valid current XDG targets, so they are never treated as migration sources.setupthen adopts the new path soPATHsetup and recorded install metadata point at the migrated binary, not the deleted legacy location. Skipped when a binary already exists at the target.Both migrations use async
node:fs/promisesand run through the sharedbestEffort()helper, so any failure is surfaced as a warning and reported to Sentry (captureException) without ever aborting setup.sentry upgraderunssetupon the new binary, so it migrates too — but conservatively, because upgrade never editsPATH(it runs--no-modify-path). A legacy~/.sentry/binbinary is relocated to the XDG install dir only when that dir is already onPATH(resolveUpgradeInstallDir), so the moved binary stays discoverable; setup's legacy-binary migration moves the old binary and removes it before--installwrites the new one. If the XDG dir isn't onPATH, upgrade leaves the binary in place — runsentry cli setupexplicitly to relocate it and updatePATH. Legacy config is migrated on upgrade regardless.Changes
packages/cli/src/lib/db/index.ts—resolveConfigDir+ newresolveXdgConfigDirhelper.packages/cli/src/lib/binary.ts— XDG-awaredetermineInstallDir(XDG_BIN_HOME,~/.local/binfallback).packages/cli/src/lib/upgrade.ts— known-curl-path detection + fallback install path track the XDG resolution.packages/cli/src/lib/binary.ts— sharedsamePath,LEGACY_INSTALL_SUBDIR, andgetLegacyInstallDirs()(used by both setup and upgrade).packages/cli/src/commands/cli/setup.ts—migrateLegacyConfig/migrateLegacyBinary(async,bestEffort-wrapped), adopted into the setup flow.packages/cli/install— install script recognizesXDG_BIN_HOME.test/lib/config.test.ts,test/lib/binary.test.ts(XDG cases +samePath/getLegacyInstallDirs),test/commands/cli/setup.test.ts(migration + records-new-path + executability),test/commands/cli/upgrade.test.ts(resolveUpgradeInstallDir), andtest/e2e/migration.test.ts(spawns the real CLI and verifies config-DB + legacy-binary migration end-to-end).apps/cli-docs/src/fragments/configuration.mddocuments config location, binary install location, and migration behavior.Testing
vitest runfor config / binary / setup suites is green (one pre-existing root-onlyacquireLockpermission test fails onmaintoo, since root ignoreschmod 0o000).tsc --noEmitclean; Biome clean on changed files.Review follow-ups addressed
copyFileSyncpermission concern verified (mode is preserved) and hardened with an explicitchmod+ test.