feat(paths): follow XDG Base Directory Specification#3336
Open
Sayt-0 wants to merge 3 commits into
Open
Conversation
docker-agent stored data under ~/.cagent and config under ~/.config/cagent, polluting $HOME and ignoring XDG and OS-native conventions. Resolve config, data and cache directories via the XDG Base Directory Specification, honouring XDG_CONFIG_HOME, XDG_DATA_HOME and XDG_CACHE_HOME on every platform with OS-native fallback otherwise (Linux ~/.config, ~/.local/share, ~/.cache; macOS ~/Library; Windows %AppData% and %LocalAppData%). Existing installs are migrated once at startup: ~/.cagent and ~/.config/cagent are relocated entry by entry into the new directories, merging without clobbering (on macOS both share ~/Library/Application Support/cagent). The move is skipped for overridden directories, and the getters fall back to the legacy directory until it runs, so no data is lost. Route the OCI store, models.dev cache and prompt history through the paths package, and resolve the session-db default lazily from the data dir so the data-dir override and SetRoot are honoured. Closes #1638
paths.GetConfigDir now honours XDG_CONFIG_HOME (via os.UserConfigDir),
so tests that isolate user config with only t.Setenv("HOME", ...) leak
alias writes into the shared XDG config dir when XDG_CONFIG_HOME is set
(as on CI runners). A "default" alias written by pkg/config tests then
made pkg/sandbox/kit and pkg/teamloader resolve "default" to an OCI ref
and fail. Clear the XDG variables from each package's TestMain to
restore HOME-based isolation.
…ashes The one-shot migration relied solely on os.Rename, which fails with EXDEV when the legacy dir and the XDG dir live on different filesystems (HOME on another mount, XDG_DATA_HOME on another disk). Worse, the destination was created before any move: when every rename failed it was left behind empty, the getters resolved to it instead of the legacy fallback, and existing state became invisible. Harden the migration, now in its own file: - fall back to copy-then-delete when rename fails: entries are copied to a reserved .cagent-migrate-* sibling (files fsynced), renamed atomically to their final name, the parent dir is synced, and only then is the source removed; a crash mid-copy can never surface a truncated entry under its final name, and stale temporaries are cleaned up on the next run - remove the destination if it was created but nothing moved into it, so the legacy fallback in the getters stays effective and no data becomes unreachable - serialize concurrent processes with a lock file (flock/LockFileEx) keyed on the destination; a bounded wait skips the run if another process is still migrating, retried on next start - preserve permissions and symlinks in the copy fallback and leave irregular files (sockets, FIFOs) in place instead of failing the whole entry
Member
Author
Migration hardeningThe follow-up commit hardens Problems addressed
Properties
Testing
|
dgageot
approved these changes
Jul 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
docker-agent now follows the XDG Base Directory Specification for its config, data and cache directories. This resolves #1638: docker-agent polluted
$HOMEwith~/.cagentwhile also creating~/.config/cagent, and ignored XDG and OS-native conventions.Resolved locations
XDG_*variables are honoured on every platform (not only Linux), so users who set them, including on macOS, get the location they expect; otherwise the OS-native directory is used.XDG_CONFIG_HOME~/.config/cagent~/Library/Application Support/cagent%AppData%\cagentXDG_DATA_HOME~/.local/share/cagent~/Library/Application Support/cagent%LocalAppData%\cagentXDG_CACHE_HOME~/.cache/cagent~/Library/Caches/cagent%LocalAppData%\cagentMigration of existing installs
On first run (root
PersistentPreRunE),paths.MigrateLegacy()relocates the historical layout:~/.cagentto the data dir~/.config/cagentto the config dirProperties:
os.Rename, merging into the destination without clobbering existing entries (on macOS config and data resolve to the same dir, so a merge is required);--data-dir/--config-dir/--cache-dirorpaths.SetRoot, so embedders (e.g. Gordon) are untouched;Related fixes exposed by the move
~/.cagent--session-dbdefault~/.cagent/session.dbat flag-registration time, ignoring--data-dir--data-dirandSetRootare honoured--data-dir/--cache-dirinto the container (never bind-mounted)Issue #1638 expectations
$HOMEwith~/.cagentTesting
migrateDir(move/merge/no-clobber/remove-empty/no-op),MigrateLegacy(default layout, macOS shared-dir collision, override skip), native default, and the--session-dbregression;go build ./...,go vet,gofmt,golangci-lintclean;~/.cagent+~/.config/cagentwere migrated into the XDG/native dir with content intact and the legacy dirs removed;sbx createmounts the config dir at the resolved XDG path read-only and forwards no data/cache host path.Note on dependency
This reproduces
github.com/adrg/xdg's behaviour in three small helpers rather than taking the dependency (suggested in the issue). Swapping toadrg/xdgis a one-line change per helper if maintainers prefer the library.