mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
Test runner uses the repo's pinned Bun, and refuses an older one instead of silently skipping tests (#31981)
* chore(test): run-vitest launches the pinned Bun, and refuses a Bun older than config/.bun-version An older Bun silently runs no tests in files that reach newer node builtins, so a local comparison could skip them on both sides. Claude-Session: W5b * chore(test): an older Bun ends run-vitest by exit code, so its message is not cut off Claude-Session: W5b
This commit is contained in:
@@ -6,6 +6,23 @@ export function readPinnedBunVersion(root) {
|
||||
return readFileSync(path.join(root, 'config', '.bun-version'), 'utf8').trim()
|
||||
}
|
||||
|
||||
/** `bun --version` of `program`, or null when it does not run. */
|
||||
export function probeBunVersion(program, root, runProcessSync) {
|
||||
try {
|
||||
const probe = runProcessSync({
|
||||
program,
|
||||
args: ['--version'],
|
||||
cwd: root,
|
||||
env: process.env,
|
||||
timeoutMs: 2000,
|
||||
maxOutputBytes: 4096
|
||||
})
|
||||
return probe.code === 0 && !probe.signal && !probe.timedOut ? probe.stdout.trim() : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute path of the `bun` devDependency when it runs and is exactly the version in
|
||||
* config/.bun-version, otherwise null. Its binary is only real once the postinstall ran.
|
||||
@@ -15,21 +32,40 @@ export function resolvePinnedBunExecutable(root, runProcessSync) {
|
||||
const manifestPath = createRequire(path.join(root, 'package.json')).resolve('bun/package.json')
|
||||
const { bin } = JSON.parse(readFileSync(manifestPath, 'utf8'))
|
||||
const executable = path.join(path.dirname(manifestPath), bin.bun)
|
||||
const probe = runProcessSync({
|
||||
program: executable,
|
||||
args: ['--version'],
|
||||
cwd: root,
|
||||
env: process.env,
|
||||
timeoutMs: 2000,
|
||||
maxOutputBytes: 4096
|
||||
})
|
||||
return probe.code === 0 &&
|
||||
!probe.signal &&
|
||||
!probe.timedOut &&
|
||||
probe.stdout.trim() === readPinnedBunVersion(root)
|
||||
return probeBunVersion(executable, root, runProcessSync) === readPinnedBunVersion(root)
|
||||
? executable
|
||||
: null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const VERSION = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/
|
||||
|
||||
// Why not src/shared/app-version.ts: importing .ts from here warns on every test run and CLI build.
|
||||
function compareVersionCores(left, right) {
|
||||
const [a, b] = [VERSION.exec(left), VERSION.exec(right)]
|
||||
return Number(a[1]) - Number(b[1]) || Number(a[2]) - Number(b[2]) || Number(a[3]) - Number(b[3])
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a Bun of version `found` may run the tests pinned to `pinned`. Only an older Bun is
|
||||
* refused (it silently runs no tests in files that reach newer builtins); a newer one passes.
|
||||
*/
|
||||
export function checkBunVersion(found, pinned) {
|
||||
if (!found || !VERSION.test(found) || !VERSION.test(pinned)) {
|
||||
return {
|
||||
ok: true,
|
||||
message: `Could not read the Bun version (found "${found ?? ''}", pinned ${pinned}); continuing.`
|
||||
}
|
||||
}
|
||||
if (compareVersionCores(found, pinned) >= 0) {
|
||||
return { ok: true }
|
||||
}
|
||||
return {
|
||||
ok: false,
|
||||
message:
|
||||
`Bun ${found} is older than the pinned ${pinned} (config/.bun-version). ` +
|
||||
`Run \`pnpm install\` to install the pinned Bun, or \`bun upgrade\`.`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { checkBunVersion } from './pinned-bun-executable.mjs'
|
||||
|
||||
describe('checkBunVersion', () => {
|
||||
it('refuses an older Bun and says how to get the pinned one', () => {
|
||||
const result = checkBunVersion('1.3.14', '1.4.3')
|
||||
expect(result.ok).toBe(false)
|
||||
expect(result.message).toContain('Bun 1.3.14 is older than the pinned 1.4.3')
|
||||
expect(result.message).toContain('pnpm install')
|
||||
})
|
||||
|
||||
it('compares each part as a number', () => {
|
||||
expect(checkBunVersion('1.9.0', '1.10.0').ok).toBe(false)
|
||||
expect(checkBunVersion('1.10.0', '1.9.0')).toEqual({ ok: true })
|
||||
})
|
||||
|
||||
it('passes an equal or newer Bun', () => {
|
||||
expect(checkBunVersion('1.4.3', '1.4.3')).toEqual({ ok: true })
|
||||
expect(checkBunVersion('1.4.4', '1.4.3')).toEqual({ ok: true })
|
||||
expect(checkBunVersion('2.0.0', '1.4.3')).toEqual({ ok: true })
|
||||
})
|
||||
|
||||
it('warns and continues when a version cannot be read', () => {
|
||||
for (const found of [null, '', 'bun: command not found', '1.4']) {
|
||||
const result = checkBunVersion(found, '1.4.3')
|
||||
expect(result.ok).toBe(true)
|
||||
expect(result.message).toContain('Could not read the Bun version')
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1,24 +1,41 @@
|
||||
import { resolve } from 'node:path'
|
||||
import { runProcessSync } from '@orca/process-host'
|
||||
import {
|
||||
checkBunVersion,
|
||||
probeBunVersion,
|
||||
readPinnedBunVersion,
|
||||
resolvePinnedBunExecutable
|
||||
} from './pinned-bun-executable.mjs'
|
||||
|
||||
const root = resolve(import.meta.dirname, '../..')
|
||||
try {
|
||||
const result = runProcessSync({
|
||||
program: 'bun',
|
||||
args: ['--bun', resolve(root, 'node_modules/vitest/vitest.mjs'), ...process.argv.slice(2)],
|
||||
cwd: root,
|
||||
env: {
|
||||
...process.env,
|
||||
ORCA_BACKGROUND_LAUNCH: '1',
|
||||
ORCA_TEST_NODE_EXECUTABLE: process.execPath,
|
||||
ORCA_TEST_NODE_VERSION: process.versions.node
|
||||
},
|
||||
stdio: 'inherit',
|
||||
timeoutMs: null
|
||||
})
|
||||
const pinnedBun = resolvePinnedBunExecutable(root, runProcessSync)
|
||||
const bunCheck = pinnedBun
|
||||
? { ok: true }
|
||||
: checkBunVersion(probeBunVersion('bun', root, runProcessSync), readPinnedBunVersion(root))
|
||||
if (bunCheck.message) {
|
||||
console.error(bunCheck.message)
|
||||
}
|
||||
if (!bunCheck.ok) {
|
||||
process.exitCode = 1
|
||||
} else {
|
||||
try {
|
||||
const result = runProcessSync({
|
||||
program: pinnedBun ?? 'bun',
|
||||
args: ['--bun', resolve(root, 'node_modules/vitest/vitest.mjs'), ...process.argv.slice(2)],
|
||||
cwd: root,
|
||||
env: {
|
||||
...process.env,
|
||||
ORCA_BACKGROUND_LAUNCH: '1',
|
||||
ORCA_TEST_NODE_EXECUTABLE: process.execPath,
|
||||
ORCA_TEST_NODE_VERSION: process.versions.node
|
||||
},
|
||||
stdio: 'inherit',
|
||||
timeoutMs: null
|
||||
})
|
||||
|
||||
process.exitCode = result.code ?? 1
|
||||
} catch (error) {
|
||||
console.error('Could not start Vitest. Install the Bun version in config/.bun-version.', error)
|
||||
process.exitCode = 1
|
||||
process.exitCode = result.code ?? 1
|
||||
} catch (error) {
|
||||
console.error('Could not start Vitest. Install the Bun version in config/.bun-version.', error)
|
||||
process.exitCode = 1
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user