diff --git a/src/workspace-package-manager.test.ts b/src/workspace-package-manager.test.ts index 85a4fba..d8c0eda 100644 --- a/src/workspace-package-manager.test.ts +++ b/src/workspace-package-manager.test.ts @@ -23,6 +23,14 @@ import { let tempRoot: string; +function expectedBunExecutable(): string { + if (path.basename(process.execPath) === 'bun') { + return process.execPath; + } + const homeBun = path.join(os.homedir(), '.bun', 'bin', 'bun'); + return fs.existsSync(homeBun) ? homeBun : 'bun'; +} + beforeEach(() => { tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ejclaw-pkgmgr-')); execFileSyncMock.mockReset(); @@ -105,11 +113,35 @@ describe('workspace package manager detection', () => { commandText: 'corepack pnpm install --frozen-lockfile', }); expect(buildWorkspaceScriptCommand(bunRepo, 'build')).toMatchObject({ - file: 'bun', + file: expectedBunExecutable(), args: ['run', 'build'], commandText: 'bun run build', }); }); + + it('uses an absolute Bun executable when building Bun workspace commands', () => { + const repoDir = path.join(tempRoot, 'bun-path'); + fs.mkdirSync(repoDir, { recursive: true }); + fs.writeFileSync( + path.join(repoDir, 'package.json'), + JSON.stringify({ + packageManager: 'bun@1.3.13', + scripts: { build: 'tsc' }, + }), + ); + fs.writeFileSync(path.join(repoDir, 'bun.lock'), ''); + + const scriptCommand = buildWorkspaceScriptCommand(repoDir, 'build'); + const installCommand = resolveWorkspaceInstallCommand(repoDir); + + expect(path.isAbsolute(scriptCommand.file)).toBe(true); + expect(scriptCommand.file).toBe(expectedBunExecutable()); + expect(installCommand).toMatchObject({ + file: expectedBunExecutable(), + args: ['install', '--frozen-lockfile'], + commandText: 'bun install --frozen-lockfile', + }); + }); }); describe('workspace dependency installs', () => { diff --git a/src/workspace-package-manager.ts b/src/workspace-package-manager.ts index af67e75..2e53189 100644 --- a/src/workspace-package-manager.ts +++ b/src/workspace-package-manager.ts @@ -1,6 +1,7 @@ import { execFileSync } from 'child_process'; import { createHash } from 'crypto'; import fs from 'fs'; +import os from 'os'; import path from 'path'; export type WorkspacePackageManager = 'bun' | 'pnpm' | 'npm' | 'yarn'; @@ -118,6 +119,23 @@ function buildCorepackCommand( }; } +function resolveBunExecutable(): string { + const currentExecutable = process.execPath; + if ( + path.basename(currentExecutable) === 'bun' && + fs.existsSync(currentExecutable) + ) { + return currentExecutable; + } + + const homeBun = path.join(os.homedir(), '.bun', 'bin', 'bun'); + if (fs.existsSync(homeBun)) { + return homeBun; + } + + return 'bun'; +} + function findNearestAncestorPackageManager( repoDir: string, ): WorkspacePackageManager | null { @@ -350,7 +368,7 @@ export function buildWorkspaceScriptCommand( case 'bun': return { packageManager, - file: 'bun', + file: resolveBunExecutable(), args: ['run', scriptName], commandText: buildCommandText('bun', ['run', scriptName]), }; @@ -389,7 +407,7 @@ export function resolveWorkspaceInstallCommand( case 'bun': return { packageManager, - file: 'bun', + file: resolveBunExecutable(), args: ['install', '--frozen-lockfile'], commandText: buildCommandText('bun', ['install', '--frozen-lockfile']), };