mirror of
https://github.com/stablyai/orca.git
synced 2026-11-03 18:47:40 +00:00
- test:changed exits before starting Vitest when Git reports no changed files. - Unrecorded tests beyond 100 in scope use Vitest's static import graph instead of all running, so a fresh worktree's first changed run no longer runs the whole suite (patched RecordedModulesResolver; CI does not record, unchanged). - Bun's slow config load was the SQLite import scan statting every unit test; filtered runs now classify only the tests their filters can select, and the scan walks directories instead of fs.globSync. - Silence the native config loader notice on every run. - Zod's test bundle is content-addressed and reused, so a killed run leaks nothing; leaked per-run directories older than 6h are swept. - build:packages skips compiling when package inputs, compiler, and dist are unchanged; test scripts no longer nest pnpm invocations.
123 lines
3.8 KiB
JavaScript
123 lines
3.8 KiB
JavaScript
import { spawnSync } from 'node:child_process'
|
|
import { createHash } from 'node:crypto'
|
|
import {
|
|
mkdirSync,
|
|
readdirSync,
|
|
readFileSync,
|
|
realpathSync,
|
|
renameSync,
|
|
statSync,
|
|
writeFileSync
|
|
} from 'node:fs'
|
|
import { createRequire } from 'node:module'
|
|
import { dirname, join, relative, resolve } from 'node:path'
|
|
|
|
const root = resolve(import.meta.dirname, '../..')
|
|
const PACKAGES = [{ name: '@orca/process-host', dir: 'src/packages/process-host' }]
|
|
// Outputs and build scratch space, not inputs.
|
|
const NON_INPUTS = new Set(['dist', 'node_modules', '.dist-build.lock'])
|
|
const COMPILER_PACKAGES = ['typescript', 'tsx', '@types/node']
|
|
|
|
function listFiles(directory, base = directory) {
|
|
return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
if (directory === base && (NON_INPUTS.has(entry.name) || entry.name.startsWith('.dist-'))) {
|
|
return []
|
|
}
|
|
const path = join(directory, entry.name)
|
|
return entry.isDirectory()
|
|
? listFiles(path, base)
|
|
: [relative(base, path).replaceAll('\\', '/')]
|
|
})
|
|
}
|
|
|
|
/** Sources, manifest, build script, and the exact installed compiler and Node types. */
|
|
export function hashPackageInputs(packageDir) {
|
|
const hash = createHash('sha256')
|
|
for (const file of listFiles(packageDir).sort()) {
|
|
hash
|
|
.update(`${file}\0`)
|
|
.update(readFileSync(join(packageDir, file)))
|
|
.update('\0')
|
|
}
|
|
const require = createRequire(join(packageDir, 'package.json'))
|
|
for (const name of COMPILER_PACKAGES) {
|
|
const manifest = realpathSync(require.resolve(`${name}/package.json`))
|
|
hash.update(`${manifest}\0`).update(readFileSync(manifest)).update('\0')
|
|
}
|
|
return hash.digest('hex')
|
|
}
|
|
|
|
/** Size and mtime of every output, so a deleted or edited dist file forces a rebuild. */
|
|
function describeOutputs(packageDir) {
|
|
const dist = join(packageDir, 'dist')
|
|
try {
|
|
return listFiles(dist)
|
|
.sort()
|
|
.map((file) => {
|
|
const { size, mtimeMs } = statSync(join(dist, file))
|
|
return `${file}:${size}:${mtimeMs}`
|
|
})
|
|
} catch {
|
|
return []
|
|
}
|
|
}
|
|
|
|
function stampPath(packageDir) {
|
|
return join(packageDir, 'node_modules', '.cache', 'orca-dist-stamp.json')
|
|
}
|
|
|
|
export function isPackageBuildCurrent(packageDir, inputs) {
|
|
try {
|
|
const stamp = JSON.parse(readFileSync(stampPath(packageDir), 'utf8'))
|
|
const outputs = describeOutputs(packageDir)
|
|
return (
|
|
stamp.inputs === inputs &&
|
|
outputs.length > 0 &&
|
|
JSON.stringify(stamp.outputs) === JSON.stringify(outputs)
|
|
)
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
function writeStamp(packageDir, inputs) {
|
|
const path = stampPath(packageDir)
|
|
mkdirSync(dirname(path), { recursive: true })
|
|
const temporary = `${path}.${process.pid}`
|
|
writeFileSync(temporary, JSON.stringify({ inputs, outputs: describeOutputs(packageDir) }))
|
|
renameSync(temporary, path)
|
|
}
|
|
|
|
function runPackageBuild(name) {
|
|
// Why not @orca/process-host: this builds it, so its dist may not exist yet.
|
|
const pnpmEntry = process.env.npm_execpath
|
|
const [program, args] = /\.[cm]?js$/.test(pnpmEntry ?? '')
|
|
? [process.execPath, [pnpmEntry, '--filter', name, 'build']]
|
|
: ['pnpm', ['--filter', name, 'build']]
|
|
const result = spawnSync(program, args, {
|
|
cwd: root,
|
|
stdio: 'inherit',
|
|
shell: program === 'pnpm' && process.platform === 'win32'
|
|
})
|
|
if (result.error) {
|
|
throw result.error
|
|
}
|
|
return result.status ?? 1
|
|
}
|
|
|
|
if (process.argv[1] && realpathSync(process.argv[1]) === import.meta.filename) {
|
|
for (const { name, dir } of PACKAGES) {
|
|
const packageDir = join(root, dir)
|
|
// Hashed before building: an edit during the build leaves a stale stamp, never a skipped build.
|
|
const inputs = hashPackageInputs(packageDir)
|
|
if (isPackageBuildCurrent(packageDir, inputs)) {
|
|
continue
|
|
}
|
|
const status = runPackageBuild(name)
|
|
if (status !== 0) {
|
|
process.exit(status)
|
|
}
|
|
writeStamp(packageDir, inputs)
|
|
}
|
|
}
|