Skip to content

feat(config): follow XDG Base Directory spec for config location - #1503

Merged
BYK merged 18 commits into
mainfrom
issue-1502-xdg-base-dir
Sep 8, 2026
Merged

feat(config): follow XDG Base Directory spec for config location#1503
BYK merged 18 commits into
mainfrom
issue-1502-xdg-base-dir

Conversation

@jared-outpost

@jared-outpost jared-outpost Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Closes #1502

What

sentry stored its config/data (the cli.db SQLite 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 ~/.sentry installs into the new locations on first setup.

Config directory

getConfigDir() resolves via a pure resolveConfigDir(env, home) helper:

  1. SENTRY_CONFIG_DIR — explicit override (highest priority)
  2. Legacy ~/.sentry — used only when it actually holds config (cli.db or config.json), so a bare ~/.sentry/bin from the installer no longer blocks XDG
  3. $XDG_CONFIG_HOME/sentry — defaulting to ~/.config/sentry. A non-absolute XDG_CONFIG_HOME is ignored per the spec.

resolveXdgConfigDir() exposes the XDG target directly (bypassing legacy detection) so migration doesn't no-op while cli.db still sits in ~/.sentry.

Binary install directory

determineInstallDir() now resolves:

  1. SENTRY_INSTALL_DIR — explicit override
  2. $XDG_BIN_HOME — when set to an absolute path, per the XDG spec
  3. ~/.local/bin or ~/bin — when either exists and is already on PATH
  4. ~/.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 recognizes XDG_BIN_HOME.

Migration (in sentry cli setup)

On first run, setup migrates the legacy ~/.sentry layout into the XDG locations. Config and binary migration are independent (a failure in one can't skip the other):

  • Config — closes the SQLite DB (it's opened at startup, and an open file can't be renamed on Windows), then moves cli.db + WAL sidecars and config.json into the XDG config dir. Skipped when a target cli.db already exists.
  • Binary — looks for a binary in a genuinely-legacy install dir (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 via setInstallInfo, and removes the old copy. ~/.local/bin and ~/bin are valid current XDG targets, so they are never treated as migration sources. setup then adopts the new path so PATH setup 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/promises and run through the shared bestEffort() helper, so any failure is surfaced as a warning and reported to Sentry (captureException) without ever aborting setup.

sentry upgrade runs setup on the new binary, so it migrates too — but conservatively, because upgrade never edits PATH (it runs --no-modify-path). A legacy ~/.sentry/bin binary is relocated to the XDG install dir only when that dir is already on PATH (resolveUpgradeInstallDir), so the moved binary stays discoverable; setup's legacy-binary migration moves the old binary and removes it before --install writes the new one. If the XDG dir isn't on PATH, upgrade leaves the binary in place — run sentry cli setup explicitly to relocate it and update PATH. Legacy config is migrated on upgrade regardless.

Changes

  • packages/cli/src/lib/db/index.tsresolveConfigDir + new resolveXdgConfigDir helper.
  • packages/cli/src/lib/binary.ts — XDG-aware determineInstallDir (XDG_BIN_HOME, ~/.local/bin fallback).
  • packages/cli/src/lib/upgrade.ts — known-curl-path detection + fallback install path track the XDG resolution.
  • packages/cli/src/lib/binary.ts — shared samePath, LEGACY_INSTALL_SUBDIR, and getLegacyInstallDirs() (used by both setup and upgrade).
  • packages/cli/src/commands/cli/setup.tsmigrateLegacyConfig / migrateLegacyBinary (async, bestEffort-wrapped), adopted into the setup flow.
  • packages/cli/install — install script recognizes XDG_BIN_HOME.
  • Tests: 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), and test/e2e/migration.test.ts (spawns the real CLI and verifies config-DB + legacy-binary migration end-to-end).
  • Docs: apps/cli-docs/src/fragments/configuration.md documents config location, binary install location, and migration behavior.

Testing

vitest run for config / binary / setup suites is green (one pre-existing root-only acquireLock permission test fails on main too, since root ignores chmod 0o000). tsc --noEmit clean; Biome clean on changed files.

Review follow-ups addressed

  • BugBot: DB rename on an open handle, stale-path-after-migration, and upgrade-pin/docs over-claim — all fixed.
  • Sentry review bot: copyFileSync permission concern verified (mode is preserved) and hardened with an explicit chmod + test.

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
@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
cli Ready Ready Preview Sep 7, 2026 10:31pm UTC

Request Review

@jared-outpost
jared-outpost Bot marked this pull request as ready for review August 28, 2026 17:58
@jared-outpost

jared-outpost Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

CI green on the feature branch (check_suite success). Self-review clean. Marking ready and requesting review from the issue author.

@github-actions github-actions Bot added the risk: high PR risk score: high label Aug 28, 2026
Comment thread packages/cli/src/lib/db/index.ts
…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.
@BYK

BYK commented Aug 29, 2026

Copy link
Copy Markdown
Member

Jared, we need to do the following:

  1. Move the binary install path to either the new config directory or the appropriate directory based on the XDG Base Directory spec
  2. We should migrate the config directory instead of leaving it at the old place. The best place for this is the setup command I think

@jared-outpost

jared-outpost Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

ack — will update binary install paths to follow XDG (via resolveConfigDir) and add migration logic inside setup so legacy ~/.sentry moves to the new location on first run. expanding scope as requested.

@BYK

BYK commented Aug 29, 2026

Copy link
Copy Markdown
Member

Jared, update the patch already?

@jared-outpost

jared-outpost Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

working on it — expanding to XDG-aware binary paths + migration inside setup. will push the update shortly.

@jared-outpost

jared-outpost Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

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.

@jared-outpost

jared-outpost Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

expanding the patch now to cover XDG binary paths + migration in setup. pushing shortly.

@jared-outpost

jared-outpost Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

not yet — expanding determineInstallDir + setup migration for binary paths now. will push once the XDG-aware logic + migration is in.

@BYK

BYK commented Sep 4, 2026

Copy link
Copy Markdown
Member

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.
@jared-outpost

jared-outpost Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

pushed in 90c9cb2 — expanded per your two points:

  1. binary install path now follows XDG. determineInstallDir honors an absolute XDG_BIN_HOME (after SENTRY_INSTALL_DIR) and falls back to ~/.local/bin instead of ~/.sentry/bin. upgrade's known-curl-path detection + fallback and the install script track the same resolution.

  2. migration happens in setup. on first run (including via sentry upgrade), setup moves an existing ~/.sentry/bin binary and legacy config (cli.db, config.json) into the XDG locations, skipping when a target already exists. added resolveXdgConfigDir so the migration target bypasses legacy detection (otherwise it'd no-op while cli.db still sits in ~/.sentry).

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 (acquireLock permission test, fails on main too since root ignores chmod 0o000). docs updated with the install-location + migration behavior.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/cli/src/commands/cli/setup.ts
Comment thread packages/cli/src/commands/cli/setup.ts
Comment thread packages/cli/src/commands/cli/setup.ts Outdated
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
@jared-outpost

jared-outpost Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

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.

@BYK BYK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we also need one e2e migration test to make sure the migration is actually working.

Comment thread packages/cli/src/commands/cli/setup.ts Outdated
Comment thread packages/cli/src/commands/cli/setup.ts Outdated
Comment thread packages/cli/src/commands/cli/setup.ts Outdated
Comment thread packages/cli/src/commands/cli/setup.ts Outdated
Comment thread packages/cli/src/commands/cli/upgrade.ts
@BYK BYK added the jared Trigger the Jared agent to work on stuff label Sep 5, 2026
- Binary migration is no longer hardcoded to ~/.sentry/bin: findMigratableBinary
  scans getKnownInstallDirs() (derived from KNOWN_CURL_DIRS) for a binary in any
  known install dir other than the resolved target.
- Deduplicate path logic: samePath and LEGACY_INSTALL_SUBDIR now live in
  binary.ts and are shared by setup and upgrade (removed the duplicate samePath
  in upgrade.ts).
- Use async node:fs/promises (mkdir/copyFile/chmod/rename/unlink) in the
  migration paths.
- Report migration failures to Sentry: both migrations now run through
  bestEffort(), which warns the user and captureException()s while never
  aborting setup.
- Add an e2e migration test (test/e2e/migration.test.ts) that spawns the real
  CLI and verifies both the config DB and a legacy ~/.sentry/bin binary migrate
  to the XDG locations. Plus unit tests for samePath / getKnownInstallDirs.

Addresses BYK's CHANGES_REQUESTED review on #1503.
@jared-outpost

jared-outpost Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — all five review comments are addressed in b03db3b, and threads resolved:

  • e2e migration test: added test/e2e/migration.test.ts, which spawns the real CLI and verifies both the config DB (~/.sentry/cli.db~/.config/sentry) and a legacy ~/.sentry/bin binary migrate to the XDG locations end-to-end.
  • legacy binary not only ~/.sentry: findMigratableBinary now scans getKnownInstallDirs() (from KNOWN_CURL_DIRS).
  • async fs: migration uses node:fs/promises.
  • report to Sentry: both migrations run through bestEffort() (warns + captureException).
  • dedupe: samePath/LEGACY_INSTALL_SUBDIR moved to binary.ts, shared by setup and upgrade.

PR description updated to match.

Comment thread packages/cli/src/commands/cli/upgrade.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/cli/src/commands/cli/setup.ts
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.
Comment thread packages/cli/src/lib/binary.ts

@BYK BYK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would also be great if the tests too used node:fs/promises variants rather than the sync ones.

Comment thread packages/cli/src/lib/binary.ts Outdated
Comment thread packages/cli/src/lib/binary.ts
Comment thread packages/cli/src/lib/shell.ts Outdated
… 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.
Comment thread packages/cli/src/lib/binary.ts
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.
Comment thread packages/cli/src/lib/upgrade.ts Outdated
…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.
Comment thread packages/cli/src/lib/binary.ts Outdated
…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.
@jared-outpost

jared-outpost Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Status on the latest push (9a84530):

  • BYK's final comment (static samePath, no per-call platform check) — done; thread resolved.
  • Independent adversarial review — ran a fresh critical pass over the whole diff. The one genuinely in-scope finding was trailing-separator sensitivity in path comparison; folded a fix into samePath (strips a trailing sep, never from a bare root) so PATH entries like ~/.local/bin/ match ~/.local/bin across isInPath / determineInstallDir / upgrade relocation. Also guarded the install script to only honor an absolute XDG_BIN_HOME. The other review notes were pre-existing/out-of-scope (unvalidated SENTRY_*_DIR overrides) or exotic symlink cases already guarded by the existsSync(target) early-returns.
  • Seer / BugBot — all previously-raised threads are resolved; re-running on this commit.

I've re-requested your review since all threads are now resolved. I'm holding off on merging: the PR is CHANGES_REQUESTED (your review) and CI + Seer/BugBot are still running on 9a84530 — I won't merge over a maintainer's requested-changes or before checks are green. Happy to merge once you re-approve and CI passes (or let me know if you'd like me to proceed differently).

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread packages/cli/install Outdated
…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).
@BYK
BYK merged commit 6e3e7e1 into main Sep 8, 2026
34 checks passed
@BYK
BYK deleted the issue-1502-xdg-base-dir branch September 8, 2026 01:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jared Trigger the Jared agent to work on stuff risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support XDG_BASE_DIRECTORY specification.

1 participant