Merge remote-tracking branch 'origin/main' into feat/assistant-revamp

# Conflicts:
#	web/package-lock.json
#	web/package.json
#	web/src/components/dashboards/PanelContainer.vue
This commit is contained in:
Harsh Mahajan
2026-11-03 01:53:28 +05:30
86 changed files with 6449 additions and 383 deletions
File diff suppressed because it is too large Load Diff
+16
View File
@@ -0,0 +1,16 @@
// Regenerate ui-shard-durations.json: download the ui-unit-blob-* artifacts of a green CI run (they expire after 1 day), then `node scripts/ui-shard-durations.mjs <dir-with-blob-json-files>`.
import { readFileSync, readdirSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import { parse } from 'flatted'
const dir = process.argv[2] ?? '.vitest-reports'
const out = {}
for (const f of readdirSync(dir, { recursive: true }).filter((n) => n.endsWith('.json'))) {
for (const t of parse(readFileSync(join(dir, f), 'utf8'))[1]) {
const ms = (t.result?.duration ?? 0) + (t.collectDuration ?? 0) + (t.setupDuration ?? 0) + (t.environmentLoad ?? 0) + (t.prepareDuration ?? 0)
out[t.filepath.slice(t.filepath.indexOf('/web/') + 5)] = Math.round(ms / 100) / 10
}
}
const sorted = Object.fromEntries(Object.entries(out).sort(([a], [b]) => (a < b ? -1 : 1)))
writeFileSync(new URL('./ui-shard-durations.json', import.meta.url), JSON.stringify(sorted, null, 0).replace(/,"/g, ',\n"') + '\n')
console.log(Object.keys(sorted).length, 'files')
+34
View File
@@ -0,0 +1,34 @@
// Balances --shard=i/N by recorded per-file seconds (greedy longest-first); refresh with scripts/ui-shard-durations.mjs.
import { readFileSync } from 'node:fs'
import { relative } from 'node:path'
import { BaseSequencer } from 'vitest/node'
import type { TestSpecification } from 'vitest/node'
const durations: Record<string, number> = JSON.parse(
readFileSync(new URL('./ui-shard-durations.json', import.meta.url), 'utf8'),
)
export function partition(paths: string[], count: number): string[][] {
const known = Object.values(durations).sort((a, b) => a - b)
const median = known[Math.floor(known.length / 2)] ?? 1
const secs = (p: string) => durations[p] ?? median
const bins: string[][] = Array.from({ length: count }, () => [])
const load = new Array(count).fill(0)
for (const p of [...paths].sort((a, b) => secs(b) - secs(a) || (a < b ? -1 : 1))) {
const i = load.indexOf(Math.min(...load))
bins[i].push(p)
load[i] += secs(p)
}
return bins
}
export default class UiShardSequencer extends BaseSequencer {
async shard(files: TestSpecification[]) {
const { root, shard } = this.ctx.config
if (!shard) return files
const rel = (f: TestSpecification) => relative(root, f.moduleId)
const bins = partition(files.map(rel), shard.count)
const mine = new Set(bins[shard.index - 1])
return files.filter((f) => mine.has(rel(f)))
}
}