Skip to content

Commit b8b270d

Browse files
fix(daemon): add Nix Home Manager PATH support
Add Nix Home Manager profile bin directories to generated gateway service PATHs on macOS and Linux. Includes ~/.nix-profile/bin fallback when NIX_PROFILES is absent, honors NIX_PROFILES right-to-left precedence when present, and covers the service PATH resolver with focused unit tests. Closes #44402.
1 parent 33d5ebb commit b8b270d

2 files changed

Lines changed: 119 additions & 0 deletions

File tree

src/daemon/service-env.test.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,102 @@ describe("getMinimalServicePathParts - Linux user directories", () => {
172172
});
173173
});
174174

175+
describe("getMinimalServicePathParts - Nix Home Manager", () => {
176+
it("falls back to default Nix profile when NIX_PROFILES is absent on Linux", () => {
177+
const result = getMinimalServicePathParts({
178+
platform: "linux",
179+
home: "/home/testuser",
180+
});
181+
182+
expect(result).toContain("/home/testuser/.nix-profile/bin");
183+
});
184+
185+
it("falls back to default Nix profile when NIX_PROFILES is absent on macOS", () => {
186+
const result = getMinimalServicePathParts({
187+
platform: "darwin",
188+
home: "/Users/testuser",
189+
});
190+
191+
expect(result).toContain("/Users/testuser/.nix-profile/bin");
192+
});
193+
194+
it("places rightmost NIX_PROFILES entry before leftmost on Linux", () => {
195+
const result = getMinimalServicePathPartsFromEnv({
196+
platform: "linux",
197+
env: {
198+
HOME: "/home/testuser",
199+
NIX_PROFILES: "/nix/var/nix/profiles/default /home/testuser/.nix-profile",
200+
},
201+
});
202+
203+
const userIdx = result.indexOf("/home/testuser/.nix-profile/bin");
204+
const defaultIdx = result.indexOf("/nix/var/nix/profiles/default/bin");
205+
expect(userIdx).toBeGreaterThan(-1);
206+
expect(defaultIdx).toBeGreaterThan(-1);
207+
expect(userIdx).toBeLessThan(defaultIdx);
208+
});
209+
210+
it("places rightmost NIX_PROFILES entry before leftmost on macOS", () => {
211+
const result = getMinimalServicePathPartsFromEnv({
212+
platform: "darwin",
213+
env: {
214+
HOME: "/Users/testuser",
215+
NIX_PROFILES: "/nix/var/nix/profiles/default /Users/testuser/.nix-profile",
216+
},
217+
});
218+
219+
const userIdx = result.indexOf("/Users/testuser/.nix-profile/bin");
220+
const defaultIdx = result.indexOf("/nix/var/nix/profiles/default/bin");
221+
expect(userIdx).toBeGreaterThan(-1);
222+
expect(defaultIdx).toBeGreaterThan(-1);
223+
expect(userIdx).toBeLessThan(defaultIdx);
224+
});
225+
226+
it("includes single Nix profile from NIX_PROFILES on Linux", () => {
227+
const result = getMinimalServicePathPartsFromEnv({
228+
platform: "linux",
229+
env: {
230+
HOME: "/home/testuser",
231+
NIX_PROFILES: "/nix/var/nix/profiles/per-user/testuser/profile",
232+
},
233+
});
234+
235+
expect(result).toContain("/nix/var/nix/profiles/per-user/testuser/profile/bin");
236+
});
237+
238+
it("includes single Nix profile from NIX_PROFILES on macOS", () => {
239+
const result = getMinimalServicePathPartsFromEnv({
240+
platform: "darwin",
241+
env: {
242+
HOME: "/Users/testuser",
243+
NIX_PROFILES: "/nix/var/nix/profiles/per-user/testuser/profile",
244+
},
245+
});
246+
247+
expect(result).toContain("/nix/var/nix/profiles/per-user/testuser/profile/bin");
248+
});
249+
250+
it("preserves Nix precedence across three profiles", () => {
251+
const result = getMinimalServicePathPartsFromEnv({
252+
platform: "linux",
253+
env: {
254+
HOME: "/home/testuser",
255+
NIX_PROFILES:
256+
"/nix/var/nix/profiles/default /nix/var/nix/profiles/per-user/testuser/custom /home/testuser/.nix-profile",
257+
},
258+
});
259+
260+
const userIdx = result.indexOf("/home/testuser/.nix-profile/bin");
261+
const customIdx = result.indexOf("/nix/var/nix/profiles/per-user/testuser/custom/bin");
262+
const defaultIdx = result.indexOf("/nix/var/nix/profiles/default/bin");
263+
expect(userIdx).toBeGreaterThan(-1);
264+
expect(customIdx).toBeGreaterThan(-1);
265+
expect(defaultIdx).toBeGreaterThan(-1);
266+
expect(userIdx).toBeLessThan(customIdx);
267+
expect(customIdx).toBeLessThan(defaultIdx);
268+
});
269+
});
270+
175271
describe("buildMinimalServicePath", () => {
176272
const splitPath = (value: string, platform: NodeJS.Platform) =>
177273
value.split(platform === "win32" ? path.win32.delimiter : path.posix.delimiter);

src/daemon/service-env.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,23 @@ function addCommonEnvConfiguredBinDirs(
106106
addNonEmptyDir(dirs, appendSubdir(env?.ASDF_DATA_DIR, "shims"));
107107
}
108108

109+
// Nix shell precedence: rightmost profile in NIX_PROFILES = highest priority.
110+
// When NIX_PROFILES is absent, fall back to the default single-user profile.
111+
function addNixProfileBinDirs(
112+
dirs: string[],
113+
home: string,
114+
env: Record<string, string | undefined> | undefined,
115+
): void {
116+
const nixProfiles = env?.NIX_PROFILES?.trim();
117+
if (nixProfiles) {
118+
for (const profile of nixProfiles.split(/\s+/).toReversed()) {
119+
addNonEmptyDir(dirs, appendSubdir(profile, "bin"));
120+
}
121+
} else {
122+
dirs.push(`${home}/.nix-profile/bin`);
123+
}
124+
}
125+
109126
function resolveSystemPathDirs(platform: NodeJS.Platform): string[] {
110127
if (platform === "darwin") {
111128
return ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"];
@@ -148,6 +165,9 @@ export function resolveDarwinUserBinDirs(
148165
// Common user bin directories
149166
addCommonUserBinDirs(dirs, home);
150167

168+
// Nix Home Manager (cross-platform)
169+
addNixProfileBinDirs(dirs, home, env);
170+
151171
// Node version managers - macOS specific paths
152172
// nvm: no stable default path, depends on user's shell configuration
153173
// fnm: macOS default is ~/Library/Application Support/fnm, not ~/.fnm
@@ -182,6 +202,9 @@ export function resolveLinuxUserBinDirs(
182202
// Common user bin directories
183203
addCommonUserBinDirs(dirs, home);
184204

205+
// Nix Home Manager (cross-platform)
206+
addNixProfileBinDirs(dirs, home, env);
207+
185208
// Node version managers
186209
dirs.push(`${home}/.nvm/current/bin`); // nvm with current symlink
187210
dirs.push(`${home}/.fnm/current/bin`); // fnm

0 commit comments

Comments
 (0)