fix(cli): resolve Windows CI test failures across three platform issues

- Use Bun.which() with process.execPath fallback in run-cli helper to
  resolve bun executable on Windows (fixes 22 integration tests)
- Normalize paths in credentials-store test for Windows backslash compat
- Use shell: true on Windows in updater spawn for proper exe resolution
This commit is contained in:
dongmucat 2026-04-29 16:03:09 +08:00
parent a2df5f4eb4
commit 87dbf2686b
3 changed files with 19 additions and 10 deletions

View file

@ -1,4 +1,5 @@
import { spawn } from 'node:child_process'
import type { ChildProcess, SpawnOptions } from 'node:child_process'
export interface UpdaterRunResult {
success: boolean
@ -15,18 +16,22 @@ export async function runUpdateCommand(command: string): Promise<UpdaterRunResul
if (!program) {
return { success: false, output: 'empty update command' }
}
const executable = process.platform === 'win32' && !program.endsWith('.cmd') && !program.endsWith('.exe')
? `${program}.cmd`
: program
// On Windows, use shell: true to let the OS resolve the executable
// This handles both .exe and .cmd extensions automatically
const spawnOptions: SpawnOptions = {
stdio: ['ignore', 'pipe', 'pipe'],
...(process.platform === 'win32' && { shell: true })
}
return await new Promise<UpdaterRunResult>((resolve) => {
const proc = spawn(executable, args, { stdio: ['ignore', 'pipe', 'pipe'] })
const proc: ChildProcess = spawn(program, args, spawnOptions)
const chunks: Buffer[] = []
proc.stdout.on('data', chunk => chunks.push(Buffer.from(chunk)))
proc.stderr.on('data', chunk => chunks.push(Buffer.from(chunk)))
proc.on('error', error => resolve({ success: false, output: error.message }))
proc.on('close', code => resolve({
proc.stdout?.on('data', (chunk: Buffer) => chunks.push(Buffer.from(chunk)))
proc.stderr?.on('data', (chunk: Buffer) => chunks.push(Buffer.from(chunk)))
proc.on('error', (error: Error) => resolve({ success: false, output: error.message }))
proc.on('close', (code: number | null) => resolve({
success: code === 0,
output: Buffer.concat(chunks).toString('utf-8')
}))

View file

@ -1,6 +1,9 @@
export async function runCli(args: string[], env: Record<string, string> = {}) {
// Use Bun.which() to find bun in PATH, fallback to current executable
const bunPath = await Bun.which('bun') || process.execPath
const proc = Bun.spawn({
cmd: ['bun', 'src/index.ts', ...args],
cmd: [bunPath, 'src/index.ts', ...args],
cwd: new URL('../../', import.meta.url).pathname,
env: { ...process.env, ...env },
stdout: 'pipe',

View file

@ -1,4 +1,5 @@
import { describe, expect, test } from 'bun:test'
import { normalize } from 'path'
import { createTempHome } from '../../helpers/temp-env'
import { CredentialsStore } from '../../../src/stores/credentials-store'
@ -8,7 +9,7 @@ describe('CredentialsStore', () => {
const store = new CredentialsStore(env.home)
await store.setToken('https://registry.example.com', 'sk_test')
expect(await store.getToken('https://registry.example.com')).toBe('sk_test')
expect(store.path).toBe(`${env.home}/.skillhub/credentials.json`)
expect(normalize(store.path)).toBe(normalize(`${env.home}/.skillhub/credentials.json`))
expect(await Bun.file(`${env.cwd}/credentials.json`).exists()).toBe(false)
})
})