mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-08-28 05:25:25 +00:00
fix(setup): prefer .cmd/.bat wrapper from Windows where output (#1299)
* Initial plan * fix(setup): prefer .cmd wrapper from Windows `where` output On Windows, `where gitnexus` returns multiple entries including the POSIX shell script and the .cmd wrapper. The code previously took the first line (shell script), which cannot be spawned directly by Node.js child_process on Windows. Now we prefer the .cmd entry when available. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/e6b54037-87fb-4195-b157-4cfcafce5f5d Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix(setup): also handle .bat wrappers and add fallback test Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/e6b54037-87fb-4195-b157-4cfcafce5f5d Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * revert package-lock.json and add CRLF/.bat/.CMD test variants - Revert package-lock.json to match base (no dependency changes needed) - Add CRLF line ending test (Windows `where` produces \r\n) - Add .bat wrapper test - Add uppercase .CMD extension test (case-insensitive regex) Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/7ed71368-b3e8-44de-9f13-85af4effaf25 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * chore: format code --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
This commit is contained in:
parent
342721f06d
commit
1272774ec2
2 changed files with 109 additions and 6 deletions
|
|
@ -32,15 +32,27 @@ interface SetupResult {
|
|||
*/
|
||||
function resolveGitnexusBin(): string | null {
|
||||
try {
|
||||
const cmd = process.platform === 'win32' ? 'where' : 'which';
|
||||
const resolved = execFileSync(cmd, ['gitnexus'], {
|
||||
const isWin = process.platform === 'win32';
|
||||
const cmd = isWin ? 'where' : 'which';
|
||||
const output = execFileSync(cmd, ['gitnexus'], {
|
||||
encoding: 'utf-8',
|
||||
timeout: 5000,
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
})
|
||||
.split('\n')[0]
|
||||
.trim();
|
||||
return resolved || null;
|
||||
});
|
||||
const lines = output
|
||||
.split('\n')
|
||||
.map((l) => l.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
if (isWin) {
|
||||
// On Windows, `where` returns multiple entries (e.g. the POSIX shell
|
||||
// script AND the .cmd/.bat wrapper). Prefer the wrapper because
|
||||
// child_process.spawn() cannot execute a shell script directly.
|
||||
const cmdLine = lines.find((l) => /\.(cmd|bat)$/i.test(l));
|
||||
return cmdLine || lines[0] || null;
|
||||
}
|
||||
|
||||
return lines[0] || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,4 +185,95 @@ describe('setupClaudeCode', () => {
|
|||
args: ['-y', 'gitnexus@latest', 'mcp'],
|
||||
});
|
||||
});
|
||||
|
||||
it('picks .cmd wrapper from Windows where output (multiple lines)', async () => {
|
||||
setPlatform('win32');
|
||||
// `where gitnexus` on Windows returns the POSIX script first, then .cmd
|
||||
execFileSyncMock.mockReturnValueOnce(
|
||||
'C:\\Users\\dev\\AppData\\Roaming\\npm\\gitnexus\nC:\\Users\\dev\\AppData\\Roaming\\npm\\gitnexus.cmd\n',
|
||||
);
|
||||
|
||||
const { setupCommand } = await import('../../src/cli/setup.js');
|
||||
await setupCommand();
|
||||
|
||||
const raw = await fs.readFile(path.join(tempHome, '.claude.json'), 'utf-8');
|
||||
const config = JSON.parse(raw);
|
||||
|
||||
expect(config.mcpServers.gitnexus).toEqual({
|
||||
command: 'C:\\Users\\dev\\AppData\\Roaming\\npm\\gitnexus.cmd',
|
||||
args: ['mcp'],
|
||||
});
|
||||
});
|
||||
|
||||
it('handles CRLF line endings from Windows where output', async () => {
|
||||
setPlatform('win32');
|
||||
// Windows `where` produces CRLF line endings
|
||||
execFileSyncMock.mockReturnValueOnce(
|
||||
'C:\\Users\\dev\\AppData\\Roaming\\npm\\gitnexus\r\nC:\\Users\\dev\\AppData\\Roaming\\npm\\gitnexus.cmd\r\n',
|
||||
);
|
||||
|
||||
const { setupCommand } = await import('../../src/cli/setup.js');
|
||||
await setupCommand();
|
||||
|
||||
const raw = await fs.readFile(path.join(tempHome, '.claude.json'), 'utf-8');
|
||||
const config = JSON.parse(raw);
|
||||
|
||||
expect(config.mcpServers.gitnexus).toEqual({
|
||||
command: 'C:\\Users\\dev\\AppData\\Roaming\\npm\\gitnexus.cmd',
|
||||
args: ['mcp'],
|
||||
});
|
||||
});
|
||||
|
||||
it('picks .bat wrapper when .cmd is not present', async () => {
|
||||
setPlatform('win32');
|
||||
execFileSyncMock.mockReturnValueOnce(
|
||||
'C:\\Users\\dev\\AppData\\Roaming\\npm\\gitnexus\nC:\\Users\\dev\\AppData\\Roaming\\npm\\gitnexus.bat\n',
|
||||
);
|
||||
|
||||
const { setupCommand } = await import('../../src/cli/setup.js');
|
||||
await setupCommand();
|
||||
|
||||
const raw = await fs.readFile(path.join(tempHome, '.claude.json'), 'utf-8');
|
||||
const config = JSON.parse(raw);
|
||||
|
||||
expect(config.mcpServers.gitnexus).toEqual({
|
||||
command: 'C:\\Users\\dev\\AppData\\Roaming\\npm\\gitnexus.bat',
|
||||
args: ['mcp'],
|
||||
});
|
||||
});
|
||||
|
||||
it('handles uppercase .CMD extension (case-insensitive match)', async () => {
|
||||
setPlatform('win32');
|
||||
execFileSyncMock.mockReturnValueOnce(
|
||||
'C:\\Users\\dev\\AppData\\Roaming\\npm\\gitnexus\nC:\\Users\\dev\\AppData\\Roaming\\npm\\gitnexus.CMD\n',
|
||||
);
|
||||
|
||||
const { setupCommand } = await import('../../src/cli/setup.js');
|
||||
await setupCommand();
|
||||
|
||||
const raw = await fs.readFile(path.join(tempHome, '.claude.json'), 'utf-8');
|
||||
const config = JSON.parse(raw);
|
||||
|
||||
expect(config.mcpServers.gitnexus).toEqual({
|
||||
command: 'C:\\Users\\dev\\AppData\\Roaming\\npm\\gitnexus.CMD',
|
||||
args: ['mcp'],
|
||||
});
|
||||
});
|
||||
|
||||
it('falls back to first line on Windows when no .cmd/.bat wrapper found', async () => {
|
||||
setPlatform('win32');
|
||||
// Edge case: where returns only the POSIX script (no .cmd wrapper)
|
||||
execFileSyncMock.mockReturnValueOnce('C:\\Users\\dev\\AppData\\Roaming\\npm\\gitnexus\n');
|
||||
|
||||
const { setupCommand } = await import('../../src/cli/setup.js');
|
||||
await setupCommand();
|
||||
|
||||
const raw = await fs.readFile(path.join(tempHome, '.claude.json'), 'utf-8');
|
||||
const config = JSON.parse(raw);
|
||||
|
||||
expect(config.mcpServers.gitnexus).toEqual({
|
||||
command: 'C:\\Users\\dev\\AppData\\Roaming\\npm\\gitnexus',
|
||||
args: ['mcp'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue