Skip to content

Default to XDG-style file locations#1674

Open
florinungur wants to merge 7 commits into
apple:mainfrom
florinungur:xdg-default-dirs
Open

Default to XDG-style file locations#1674
florinungur wants to merge 7 commits into
apple:mainfrom
florinungur:xdg-default-dirs

Conversation

@florinungur

Copy link
Copy Markdown

Default the CLI's file locations to XDG-style paths instead of placing everything under ~/.pkl.

With this PR, a default Pkl setup writes nothing to $HOME/.pkl:

  • Package cache: ~/.cache/pkl (was ~/.pkl/cache)
  • Settings file: ~/.config/pkl/settings.pkl (was ~/.pkl/settings.pkl)
  • CA certificates: ~/.config/pkl/cacerts (was ~/.pkl/cacerts)
  • REPL history: ~/.local/state/pkl/repl-history (was ~/.pkl/repl-history; XDG state home, where history files belong)

These are fixed defaults; no environment variables (XDG_CACHE_HOME, XDG_CONFIG_HOME, XDG_STATE_HOME) are read, matching the design principle that Pkl doesn't configure itself from the environment.

Backward compatible, no migration needed: each legacy ~/.pkl location is still used when it already exists. In particular, a pre-existing ~/.pkl/cache is still used, so cached packages aren't re-downloaded. The one-time JLine2 ~/.pkl/repl-history.bin cleanup is left pointing at the legacy path. An empty ~/.config/pkl/cacerts directory counts as absent, so it doesn't shadow a populated legacy ~/.pkl/cacerts.

Public Java-API change: PklSettings.loadFromPklHomeDir() is renamed to loadFromDefaultLocation() since it no longer loads from the pkl home dir; an embedder that relied on ~/.pkl/settings.pkl will now prefer ~/.config/pkl/settings.pkl when both exist. Happy to pick a different name if you'd prefer.

pkl-executor can't depend on pkl-core, so its defaultModuleCacheDir() replicates the same fallback (with a keep-in-sync comment).

Verified ./gradlew spotlessApply and the JVM suites plus pkl-cli/pkl-executor compile. I also built the macOS arm64 native image and ran it. The fresh-home XDG branch isn't directly exercisable on the native binary (macOS resolves user.home from the OS, not $HOME/-D), but it's the same code path the unit tests cover and GraalVM recomputes user.home at runtime.

The CLI placed its package cache in ~/.pkl/cache and read its settings
file from ~/.pkl/settings.pkl. Default these to the XDG-style locations
~/.cache/pkl and ~/.config/pkl/settings.pkl instead, so a default setup
no longer writes to $HOME/.pkl.

These are fixed defaults; no environment variables (XDG_CACHE_HOME,
XDG_CONFIG_HOME) are read, consistent with Pkl not configuring itself
from the environment. Existing setups keep working without migration: a
pre-existing ~/.pkl/cache is still used so packages aren't re-downloaded,
and ~/.pkl/settings.pkl is still read when ~/.config/pkl/settings.pkl is
absent.
Extend the XDG-style defaults to the two remaining ~/.pkl users: the CA
certificates directory now defaults to ~/.config/pkl/cacerts, and the
REPL history to ~/.local/state/pkl/repl-history (XDG state home, where
history files belong). Both fall back to their legacy ~/.pkl locations
when those exist, so a default setup writes nothing under ~/.pkl.

The one-time JLine2 ~/.pkl/repl-history.bin cleanup is left pointing at
the legacy path, since that file only ever existed there.
Rename the public PklSettings.loadFromPklHomeDir() to
loadFromDefaultLocation() (its behavior now prefers ~/.config/pkl), with
a deprecated-for-removal shim delegating from the old name, and record
the deprecation in the release notes.

Also fix a stale ~/.pkl/settings.pkl reference in the language tutorial,
and note in the release notes that Windows uses the XDG-style paths
literally rather than mapping to Known Folder locations.
Add path-value tests for the four default-location helpers (via new
package-private home-dir overloads) and a pkl-executor test pinning
ExecutorOptions.defaultModuleCacheDir() to IoUtils, closing the gap where
a wrong path segment would have passed CI.

Fix the CA-certs fallback so an empty ~/.config/pkl/cacerts no longer
shadows a populated legacy ~/.pkl/cacerts (a directory with no cert files
now counts as absent). Update the remaining stale ~/.pkl references in
stdlib settings.pkl and ModuleCache, scope the "nothing under ~/.pkl"
release note to new setups and qualify the no-re-download claim, add a
deprecation `since`, and rename the test-only settings loader.
@bioball

bioball commented Jun 10, 2026

Copy link
Copy Markdown
Member

I think I'm open to this, but some considerations:

  • We need to update pkl-intellij and pkl-lsp to look for sources in these new directories
  • Like you said: as a matter of principle, Pkl doesn't configure itself using environment variables. So, this doesn't follow the XDG specification. However, it does feel like an improvement that we write to XDG-style paths.
  • We use ~/.pkl for other stuff. for example, pkl-lsp writes to ~/.pkl/editor-support
  • These folders are standard on Unix (macOS/linux), but not on Windows. Windows has stuff like %APPDATA%. I think it'd be weird to write to XDG-style paths on Windows, although I'm not a Windows user so I defer to others (maybe @odenix has an opinion?)

Apply %APPDATA% (config) and %LOCALAPPDATA% (cache/state) on Windows for
the four default file locations, instead of using the XDG-style literal
paths under the user home. Adds env-injectable overloads so tests can
exercise the Windows code path on a Unix CI box.

Rename preferXdgLocation -> preferNewLocation; the helper is no longer
XDG-specific now that Windows uses Known Folders.
Add the Windows %APPDATA% / %LOCALAPPDATA% paths alongside the existing
Unix XDG paths in --cache-dir, --settings, ca-certificates, settings
module, and gradle moduleCacheDir documentation.
@florinungur

florinungur commented Jun 13, 2026

Copy link
Copy Markdown
Author

I made apple/pkl-intellij#216 and apple/pkl-lsp#208 to continue this effort. Both also migrate editor-support to the XDG data dir (~/.local/share/pkl/editor-support) with a legacy ~/.pkl/editor-support fallback.

Also took a shot at the Windows paths (%APPDATA% for config, %LOCALAPPDATA% for cache/data/state), but since I can't test it and don't have a strong opinion, I welcome feedback (cc @odenix).

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.

2 participants