mirror of
https://github.com/stablyai/orca.git
synced 2026-09-21 16:02:20 +00:00
* ci: balance existing unit and E2E shards using recorded timings * ci: fix timing refresh units and deferred-menu test traversal * ci: preserve isolated E2E window launch policy * ci: keep diagnostic artifact outages from failing tests
67 lines
2.3 KiB
JavaScript
67 lines
2.3 KiB
JavaScript
import { createHash } from 'node:crypto'
|
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
import { dirname } from 'node:path'
|
|
|
|
export const compareIds = (a, b) => (a < b ? -1 : a > b ? 1 : 0)
|
|
|
|
export function balanceFiles(files, count, timings, overheadMs = 0) {
|
|
if (!Number.isInteger(count) || count < 1) {
|
|
throw new Error('Invalid shard count')
|
|
}
|
|
if (new Set(files).size !== files.length) {
|
|
throw new Error('Duplicate discovered file')
|
|
}
|
|
const known = Object.values(timings).filter((value) => Number.isFinite(value) && value > 0)
|
|
known.sort((a, b) => a - b)
|
|
const fallbackMs = known[Math.floor(known.length / 2)] ?? 1000
|
|
const weighted = files.map((file) => ({
|
|
file,
|
|
durationMs:
|
|
(Number.isFinite(timings[file]) && timings[file] > 0 ? timings[file] : fallbackMs) +
|
|
overheadMs
|
|
}))
|
|
weighted.sort((a, b) => b.durationMs - a.durationMs || compareIds(a.file, b.file))
|
|
const shards = Array.from({ length: count }, () => ({ files: [], durationMs: 0 }))
|
|
for (const entry of weighted) {
|
|
const target = shards.reduce((best, shard) =>
|
|
shard.durationMs < best.durationMs ||
|
|
(shard.durationMs === best.durationMs && shard.files.length < best.files.length)
|
|
? shard
|
|
: best
|
|
)
|
|
target.files.push(entry.file)
|
|
target.durationMs += entry.durationMs
|
|
}
|
|
for (const shard of shards) {
|
|
shard.files.sort(compareIds)
|
|
}
|
|
const assigned = shards.flatMap((shard) => shard.files).sort(compareIds)
|
|
if (JSON.stringify(assigned) !== JSON.stringify([...files].sort(compareIds))) {
|
|
throw new Error('Shard coverage differs from discovery')
|
|
}
|
|
return { algorithm: 'file-lpt-v1', fallbackMs, overheadMs, shards }
|
|
}
|
|
|
|
export function readTimingBaseline(suite) {
|
|
const bytes = readFileSync(new URL('./ci-shard-timings.json', import.meta.url), 'utf8')
|
|
const baseline = JSON.parse(bytes)
|
|
return { ...baseline[suite], baselineSha256: createHash('sha256').update(bytes).digest('hex') }
|
|
}
|
|
|
|
export function writeAssignment(path, assignment) {
|
|
mkdirSync(dirname(path), { recursive: true })
|
|
writeFileSync(
|
|
path,
|
|
`${JSON.stringify(
|
|
{
|
|
sourceSha: process.env.ORCA_SHARD_SOURCE_SHA ?? process.env.GITHUB_SHA ?? null,
|
|
runId: process.env.GITHUB_RUN_ID ?? null,
|
|
runAttempt: process.env.GITHUB_RUN_ATTEMPT ?? null,
|
|
...assignment
|
|
},
|
|
null,
|
|
2
|
|
)}\n`
|
|
)
|
|
}
|