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.
This commit is contained in:
Jérôme Benoit
2026-04-25 08:12:10 +02:00
committed by GitHub
parent 33d5ebbff7
commit b8b270d5b8
2 changed files with 119 additions and 0 deletions

View File

@@ -172,6 +172,102 @@ describe("getMinimalServicePathParts - Linux user directories", () => {
});
});
describe("getMinimalServicePathParts - Nix Home Manager", () => {
it("falls back to default Nix profile when NIX_PROFILES is absent on Linux", () => {
const result = getMinimalServicePathParts({
platform: "linux",
home: "/home/testuser",
});
expect(result).toContain("/home/testuser/.nix-profile/bin");
});
it("falls back to default Nix profile when NIX_PROFILES is absent on macOS", () => {
const result = getMinimalServicePathParts({
platform: "darwin",
home: "/Users/testuser",
});
expect(result).toContain("/Users/testuser/.nix-profile/bin");
});
it("places rightmost NIX_PROFILES entry before leftmost on Linux", () => {
const result = getMinimalServicePathPartsFromEnv({
platform: "linux",
env: {
HOME: "/home/testuser",
NIX_PROFILES: "/nix/var/nix/profiles/default /home/testuser/.nix-profile",
},
});
const userIdx = result.indexOf("/home/testuser/.nix-profile/bin");
const defaultIdx = result.indexOf("/nix/var/nix/profiles/default/bin");
expect(userIdx).toBeGreaterThan(-1);
expect(defaultIdx).toBeGreaterThan(-1);
expect(userIdx).toBeLessThan(defaultIdx);
});
it("places rightmost NIX_PROFILES entry before leftmost on macOS", () => {
const result = getMinimalServicePathPartsFromEnv({
platform: "darwin",
env: {
HOME: "/Users/testuser",
NIX_PROFILES: "/nix/var/nix/profiles/default /Users/testuser/.nix-profile",
},
});
const userIdx = result.indexOf("/Users/testuser/.nix-profile/bin");
const defaultIdx = result.indexOf("/nix/var/nix/profiles/default/bin");
expect(userIdx).toBeGreaterThan(-1);
expect(defaultIdx).toBeGreaterThan(-1);
expect(userIdx).toBeLessThan(defaultIdx);
});
it("includes single Nix profile from NIX_PROFILES on Linux", () => {
const result = getMinimalServicePathPartsFromEnv({
platform: "linux",
env: {
HOME: "/home/testuser",
NIX_PROFILES: "/nix/var/nix/profiles/per-user/testuser/profile",
},
});
expect(result).toContain("/nix/var/nix/profiles/per-user/testuser/profile/bin");
});
it("includes single Nix profile from NIX_PROFILES on macOS", () => {
const result = getMinimalServicePathPartsFromEnv({
platform: "darwin",
env: {
HOME: "/Users/testuser",
NIX_PROFILES: "/nix/var/nix/profiles/per-user/testuser/profile",
},
});
expect(result).toContain("/nix/var/nix/profiles/per-user/testuser/profile/bin");
});
it("preserves Nix precedence across three profiles", () => {
const result = getMinimalServicePathPartsFromEnv({
platform: "linux",
env: {
HOME: "/home/testuser",
NIX_PROFILES:
"/nix/var/nix/profiles/default /nix/var/nix/profiles/per-user/testuser/custom /home/testuser/.nix-profile",
},
});
const userIdx = result.indexOf("/home/testuser/.nix-profile/bin");
const customIdx = result.indexOf("/nix/var/nix/profiles/per-user/testuser/custom/bin");
const defaultIdx = result.indexOf("/nix/var/nix/profiles/default/bin");
expect(userIdx).toBeGreaterThan(-1);
expect(customIdx).toBeGreaterThan(-1);
expect(defaultIdx).toBeGreaterThan(-1);
expect(userIdx).toBeLessThan(customIdx);
expect(customIdx).toBeLessThan(defaultIdx);
});
});
describe("buildMinimalServicePath", () => {
const splitPath = (value: string, platform: NodeJS.Platform) =>
value.split(platform === "win32" ? path.win32.delimiter : path.posix.delimiter);

View File

@@ -106,6 +106,23 @@ function addCommonEnvConfiguredBinDirs(
addNonEmptyDir(dirs, appendSubdir(env?.ASDF_DATA_DIR, "shims"));
}
// Nix shell precedence: rightmost profile in NIX_PROFILES = highest priority.
// When NIX_PROFILES is absent, fall back to the default single-user profile.
function addNixProfileBinDirs(
dirs: string[],
home: string,
env: Record<string, string | undefined> | undefined,
): void {
const nixProfiles = env?.NIX_PROFILES?.trim();
if (nixProfiles) {
for (const profile of nixProfiles.split(/\s+/).toReversed()) {
addNonEmptyDir(dirs, appendSubdir(profile, "bin"));
}
} else {
dirs.push(`${home}/.nix-profile/bin`);
}
}
function resolveSystemPathDirs(platform: NodeJS.Platform): string[] {
if (platform === "darwin") {
return ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"];
@@ -148,6 +165,9 @@ export function resolveDarwinUserBinDirs(
// Common user bin directories
addCommonUserBinDirs(dirs, home);
// Nix Home Manager (cross-platform)
addNixProfileBinDirs(dirs, home, env);
// Node version managers - macOS specific paths
// nvm: no stable default path, depends on user's shell configuration
// fnm: macOS default is ~/Library/Application Support/fnm, not ~/.fnm
@@ -182,6 +202,9 @@ export function resolveLinuxUserBinDirs(
// Common user bin directories
addCommonUserBinDirs(dirs, home);
// Nix Home Manager (cross-platform)
addNixProfileBinDirs(dirs, home, env);
// Node version managers
dirs.push(`${home}/.nvm/current/bin`); // nvm with current symlink
dirs.push(`${home}/.fnm/current/bin`); // fnm