Skip to content

fix(env): expose Devbox profile share dir via XDG_DATA_DIRS so package completions work - #2926

Merged
mikeland73 merged 2 commits into
mainfrom
claude/focused-goldberg-3z42ky
Sep 15, 2026
Merged

mikeland73 merged 2 commits into
mainfrom
claude/focused-goldberg-3z42ky

Conversation

@mikeland73

Copy link
Copy Markdown
Collaborator

Summary

Fixes #2776.

Package-provided shell completions were never active inside a Devbox shell. For
example, on a fresh Ubuntu + bash setup, kubectl <TAB> does nothing even though
the kubectl Nix package ships a completion script.

Root cause

Nix packages install their bash completions under
share/bash-completion/completions/<cmd>, and bash-completion's dynamic loader
discovers them by scanning the bash-completion/completions subdirectory of
every entry in XDG_DATA_DIRS at completion time.

When computing the environment (internal/devbox/devbox.go), Devbox only
preserved the host's original XDG_DATA_DIRS:

if !envOpts.Pure {
    // preserve the original XDG_DATA_DIRS by prepending to it
    env["XDG_DATA_DIRS"] = envpath.JoinPathLists(env["XDG_DATA_DIRS"], os.Getenv("XDG_DATA_DIRS"))
}

It never added the Devbox profile's own share directory
(.devbox/nix/profile/default/share), which is where all of a project's
installed packages aggregate their data files. As a result the completions — and
other XDG data such as man pages and icons — shipped by those packages were
invisible to the shell.

Fix

Prepend the profile's share directory to XDG_DATA_DIRS while computing the
environment:

profileShareDir := filepath.Join(d.projectDir, nix.ProfilePath, "share")
env["XDG_DATA_DIRS"] = envpath.JoinPathLists(profileShareDir, env["XDG_DATA_DIRS"])
  • Done for both regular and --pure environments, so completions keep working
    in pure shells too.
  • envpath.JoinPathLists cleans, dedupes, and drops empty/relative segments, so
    this is a no-op if the entry is already present and it never introduces a
    duplicate or an unsafe relative path.
  • This mirrors how nix-env / home-manager expose the same data via
    XDG_DATA_DIRS.

How was it tested?

  • go build ./... and go vet ./internal/devbox/ — clean.
  • Added testscripts/run/xdg_data_dirs.test.txt, which asserts that
    devbox run echo '$XDG_DATA_DIRS' includes
    .devbox/nix/profile/default/share.

Manual verification of the underlying mechanism: with the profile's share
directory on XDG_DATA_DIRS, bash-completion's __load_completion finds
share/bash-completion/completions/kubectl in the Devbox profile and activates
kubectl <TAB> completion; the change is additive and does not alter any other
environment variable.

/cc @gberche-orange (issue reporter) — thanks for the clear report and the
upstream package links.

Community Contribution License

All community contributions in this pull request are licensed to the project
maintainers under the terms of the
Apache 2 License.

By creating this pull request, I represent that I have the right to license the
contributions to the project maintainers under the Apache 2 License as stated in
the
Community Contribution License.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EuAos6xjPuVdUWNEVvCYh1


Generated by Claude Code

…etions

Package-provided shell completions were never active inside a Devbox shell
(e.g. `kubectl <TAB>` did nothing on a fresh Ubuntu + bash setup). Nix
packages install their bash completions under
`share/bash-completion/completions/`, and bash-completion's dynamic loader
discovers them by scanning the `bash-completion/completions` subdirectory of
every entry in `XDG_DATA_DIRS`.

Devbox only preserved the host's original `XDG_DATA_DIRS` and never added the
Devbox profile's own `share` directory (`.devbox/nix/profile/default/share`),
so the completions (and other XDG data like man pages and icons) shipped by
installed packages were invisible.

Prepend the profile's `share` directory to `XDG_DATA_DIRS` when computing the
environment. This is done even in `--pure` mode so completions keep working
there too, and it mirrors how nix-env / home-manager expose the same data.

Fixes #2776

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EuAos6xjPuVdUWNEVvCYh1
Copilot AI review requested due to automatic review settings July 18, 2026 14:22

Copilot AI 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.

Pull request overview

This PR fixes missing package-provided shell completions (and other XDG data like man pages/icons) inside Devbox environments by ensuring the project profile’s share/ directory is discoverable via XDG_DATA_DIRS.

Changes:

  • Prepend .devbox/nix/profile/default/share to XDG_DATA_DIRS when computing the Devbox environment (including --pure).
  • Add a run testscript that asserts XDG_DATA_DIRS includes the profile share/ directory.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
internal/devbox/devbox.go Prepends the Devbox profile share dir to XDG_DATA_DIRS during environment computation.
testscripts/run/xdg_data_dirs.test.txt Adds coverage to ensure XDG_DATA_DIRS exposes the profile share directory.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +7 to +9
exec devbox run echo '$XDG_DATA_DIRS'
stdout '\.devbox/nix/profile/default/share'

…e too

The fix sets XDG_DATA_DIRS for both regular and --pure environments; add a
--pure assertion so the pure-shell path is guarded against regressions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EuAos6xjPuVdUWNEVvCYh1
@savil
savil marked this pull request as draft September 2, 2026 22:13
@mikeland73

Copy link
Copy Markdown
Collaborator Author

@claude please review this PR for correctness, and flag anything that would block merging.

@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown

Claude finished @mikeland73's task in 2m 36s —— View job


Review of PR #2926

  • Gather context (diff, related code)
  • Understand the request (review for correctness / merge blockers)
  • Review internal/devbox/devbox.go changes
  • Review testscripts/run/xdg_data_dirs.test.txt
  • Check Copilot's review comment about --pure mode coverage
  • Post final review

Summary

I reviewed the diff (internal/devbox/devbox.go +12, testscripts/run/xdg_data_dirs.test.txt +18/new) against the PR description. I don't see anything that should block merging.

Correctness

  • The fix at internal/devbox/devbox.go:832-833 prepends filepath.Join(d.projectDir, nix.ProfilePath, "share") to env["XDG_DATA_DIRS"]. This mirrors the existing pattern already used for PATH (nix.ProfileBinPath, devbox.go:745) and DEVBOX_PACKAGES_DIR (devbox.go:758), so it's consistent with the codebase's conventions rather than a one-off hack.
  • Placement is correct: the new lines run before the if !envOpts.Pure { ... } block that appends the host's XDG_DATA_DIRS, so the profile share dir is added in both pure and non-pure modes, matching the stated intent.
  • envpath.JoinPathLists (internal/devbox/envpath/pathlists.go:16) cleans, drops empty/relative entries, and dedupes — so this is safe even if env["XDG_DATA_DIRS"] is unset (missing map key returns "", handled gracefully) and won't introduce duplicate or unsafe relative entries.
  • No new imports were needed since filepath and nix are already imported/used elsewhere in devbox.go.
  • nix.ProfilePath (internal/nix/nix.go:33) is .devbox/nix/profile/default, which matches the string the test asserts against.

Test coverage

  • Copilot's earlier comment flagged that the original test only checked the non-pure path. That's already been addressed — the current version of testscripts/run/xdg_data_dirs.test.txt (lines 10-13) adds a second assertion for devbox run --pure, so that concern is resolved.
  • The test format/style (comment header, exec devbox run ... echo '$VAR', stdout regex assertion, inline devbox.json) matches existing tests like testscripts/run/path.test.txt and pure.test.txt.
  • testscripts/testscripts_test.go's TestScripts recursively runs all *.test.txt files under testscripts/, so this new test is automatically wired into CI without any extra registration.

Minor, non-blocking notes

  • The in-code comment block (lines 823-831) is fairly long for a single feature addition, but it clearly documents the why (XDG spec lookup behavior, issue link), which is reasonable given this fixes a non-obvious cross-cutting behavior.
  • Nothing else in the diff looks risky — the change is additive to a single env var and doesn't touch PATH or other computed environment logic.

Overall: the implementation is correct, consistent with existing patterns, and the test coverage now includes both pure and non-pure modes. I did not run go build/go test in this sandbox (tool restrictions prevented executing generic shell commands beyond git), but the PR description states these were already run cleanly by the author, and the code changes are small and low-risk enough to verify by inspection.

Copy link
Copy Markdown
Collaborator Author

@mikeland73 Reviewed for correctness and merge-readiness. Summary: the change is correct and I found no blocking bugs. The only thing stopping a merge is that the PR is currently a draft.

Correctness ✅

The mechanism is right. bash-completion's dynamic loader resolves $XDG_DATA_DIRS at completion time and scans <dir>/bash-completion/completions/<cmd> for each entry; Nix packages install completions under share/bash-completion/completions/, and the Devbox profile aggregates them at .devbox/nix/profile/default/share. That directory was never on XDG_DATA_DIRS, so the completions were invisible — the fix adds it.

Things I verified specifically:

  • d.projectDir is absolute (filepath.Dir(cfg.Root.AbsRootPath)), which matters because envpath.JoinPathLists silently drops relative paths — so the profile-share entry is actually retained, not discarded.
  • Applies uniformly via computeEnv, so devbox shell, devbox run, devbox shellenv, and global all get it consistently.
  • --pure is handled — the entry is added before the !Pure host-append, so pure shells keep completions (covered by the added --pure test assertion).
  • Idempotent/safeJoinPathLists cleans and dedupes, so no duplicates and no-op if already present.
  • Builds & merges — merges cleanly into current main (no conflicts; #2941's devbox.go edits don't overlap), and go build ./... + go vet are clean on the merged tree.

Merge blockers

  1. Draft status (the actual blocker). The PR was converted to draft by @savil, so it can't merge as-is. I have not marked it ready, since another maintainer set it and I don't want to override that unprompted — say the word and I'll mark it ready for review.
  2. mergeable_state: blocked otherwise just reflects required review approval; CI is fully green (all 25 checks incl. the test-result gate, every test/test-nix-versions shard, lint, builds, flake build) and there are no merge conflicts.

Two non-blocking notes for your call

  • Edge case — empty XDG_DATA_DIRS. Per the XDG spec, once XDG_DATA_DIRS is set to a non-empty value the implicit defaults (/usr/local/share:/usr/share) no longer apply. On a host where it was previously unset/empty (e.g. a minimal container), we now set it to the profile share, so consumers like bash-completion would search only that and stop falling back to /usr/share. On normal desktop distros this is a non-issue (the session sets XDG_DATA_DIRS), but if you want to be defensive I can append the XDG default dirs when the resulting value would otherwise be empty. Happy to add that if you'd prefer.
  • Slight broadening. Exposing the profile share/ also surfaces other XDG data from installed packages (desktop entries, GSettings schemas, icons, mime). This is standard nix-profile behavior and generally what you want, just noting it's a touch beyond completions.

Net: correct and ready to go once it's out of draft. Let me know if you'd like me to (a) mark it ready and/or (b) add the empty-XDG_DATA_DIRS fallback.


Generated by Claude Code

@mikeland73 mikeland73 added the ready for human review Automated review passed; needs a maintainer to review and merge label Sep 14, 2026
@mikeland73
mikeland73 marked this pull request as ready for review September 14, 2026 16:19
@mikeland73
mikeland73 merged commit 74778c9 into main Sep 15, 2026
26 checks passed
@mikeland73
mikeland73 deleted the claude/focused-goldberg-3z42ky branch September 15, 2026 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready for human review Automated review passed; needs a maintainer to review and merge

Development

Successfully merging this pull request may close these issues.

package completions not active in devbox shells (e.g. kubectl )

3 participants