Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/__tests__/shared/pathUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import * as os from 'os';
import * as fs from 'fs';
import * as path from 'path';
import {
expandTilde,
parseVersion,
Expand Down Expand Up @@ -276,6 +278,35 @@ describe('buildExpandedPath', () => {
expect(homebrewIndex).toBeLessThan(customIndex);
});

it('should prepend detected Node version manager bin paths', () => {
process.env.PATH = '/usr/bin';
const originalNvmDir = process.env.NVM_DIR;
const tempNvmDir = fs.mkdtempSync('/tmp/maestro-nvm-');
process.env.NVM_DIR = tempNvmDir;
fs.mkdirSync(path.join(tempNvmDir, 'current', 'bin'), { recursive: true });
fs.mkdirSync(path.join(tempNvmDir, 'versions', 'node', 'v22.10.0', 'bin'), {
recursive: true,
});

try {
const result = buildExpandedPath();
const pathParts = result.split(':');
const currentBin = path.join(tempNvmDir, 'current', 'bin');
const versionedBin = path.join(tempNvmDir, 'versions', 'node', 'v22.10.0', 'bin');

expect(pathParts[0]).toBe(currentBin);
expect(pathParts).toContain(versionedBin);
expect(pathParts.indexOf(currentBin)).toBeLessThan(pathParts.indexOf(versionedBin));
} finally {
if (originalNvmDir === undefined) {
delete process.env.NVM_DIR;
} else {
process.env.NVM_DIR = originalNvmDir;
}
fs.rmSync(tempNvmDir, { recursive: true, force: true });
}
});

it('should accept custom paths that are prepended first', () => {
process.env.PATH = '/usr/bin';
const result = buildExpandedPath(['/my/custom/bin', '/another/path']);
Expand Down
29 changes: 29 additions & 0 deletions src/main/process-manager/utils/__tests__/envBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*/

import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { buildChildProcessEnv, buildPtyTerminalEnv } from '../envBuilder';
Expand Down Expand Up @@ -304,6 +305,34 @@ describe('envBuilder - Global Environment Variables', () => {
// The actual value depends on the system, but it should exist
expect((env.PATH as string).length).toBeGreaterThan(0);
});

it('should include detected Node version manager bins in PATH', () => {
const originalNvmDir = process.env.NVM_DIR;
const tempNvmDir = fs.mkdtempSync(path.join(os.tmpdir(), 'maestro-nvm-'));
process.env.NVM_DIR = tempNvmDir;
fs.mkdirSync(path.join(tempNvmDir, 'current', 'bin'), { recursive: true });
fs.mkdirSync(path.join(tempNvmDir, 'versions', 'node', 'v22.10.0', 'bin'), {
recursive: true,
});

try {
const env = buildChildProcessEnv();
const pathParts = env.PATH?.split(path.delimiter) || [];
const currentBin = path.join(tempNvmDir, 'current', 'bin');
const versionedBin = path.join(tempNvmDir, 'versions', 'node', 'v22.10.0', 'bin');

expect(pathParts[0]).toBe(currentBin);
expect(pathParts).toContain(versionedBin);
expect(pathParts.indexOf(currentBin)).toBeLessThan(pathParts.indexOf(versionedBin));
} finally {
if (originalNvmDir === undefined) {
delete process.env.NVM_DIR;
} else {
process.env.NVM_DIR = originalNvmDir;
}
fs.rmSync(tempNvmDir, { recursive: true, force: true });
}
});
});

describe('Test 2.6: Complex Precedence Chain', () => {
Expand Down
8 changes: 6 additions & 2 deletions src/shared/pathUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ export function detectNodeVersionManagerBinPaths(): string[] {
export function buildExpandedPath(customPaths?: string[]): string {
const delimiter = path.delimiter;
const home = os.homedir();
const versionManagerPaths = detectNodeVersionManagerBinPaths();

// Start with current PATH
const currentPath = process.env.PATH || '';
Expand Down Expand Up @@ -370,6 +371,7 @@ export function buildExpandedPath(customPaths?: string[]): string {
} else {
// Unix-like paths (macOS/Linux)
additionalPaths = [
...versionManagerPaths,
'/opt/homebrew/bin', // Homebrew on Apple Silicon
'/opt/homebrew/sbin',
'/usr/local/bin', // Homebrew on Intel, common install location
Expand All @@ -388,15 +390,17 @@ export function buildExpandedPath(customPaths?: string[]): string {

// Add custom paths first (if provided)
if (customPaths && customPaths.length > 0) {
for (const p of customPaths) {
for (let i = customPaths.length - 1; i >= 0; i--) {
const p = customPaths[i];
if (!pathParts.includes(p)) {
pathParts.unshift(p);
}
}
}

// Add standard additional paths
for (const p of additionalPaths) {
for (let i = additionalPaths.length - 1; i >= 0; i--) {
const p = additionalPaths[i];
if (!pathParts.includes(p)) {
pathParts.unshift(p);
}
Expand Down