@kb-labs/qa-core 2.89.0 → 2.94.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/baseline/index.js +99 -23
- package/dist/baseline/index.js.map +1 -1
- package/dist/history/index.d.ts +15 -4
- package/dist/history/index.js +43 -6
- package/dist/history/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +178 -36
- package/dist/index.js.map +1 -1
- package/dist/{last-run-store-CVsUqnU0.d.ts → last-run-store-C1AkskWI.d.ts} +5 -2
- package/dist/report/index.d.ts +4 -3
- package/dist/report/index.js +35 -6
- package/dist/report/index.js.map +1 -1
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/index.js +102 -26
- package/dist/runner/index.js.map +1 -1
- package/package.json +3 -3
package/dist/history/index.js
CHANGED
|
@@ -1,7 +1,36 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
2
|
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
|
|
3
|
-
import {
|
|
4
|
-
import { TRENDS_WINDOW,
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
import { TRENDS_WINDOW, HISTORY_MAX_ENTRIES, PATHS, getCheckIcon, getCheckLabel, QA_DATA_DIR } from '@kb-labs/qa-contracts';
|
|
5
|
+
|
|
6
|
+
// src/history/history-store.ts
|
|
7
|
+
function trendPath(rootDir, checkId) {
|
|
8
|
+
return join(rootDir, QA_DATA_DIR, "trends", `${checkId}.json`);
|
|
9
|
+
}
|
|
10
|
+
function loadTrendHistory(rootDir, checkId) {
|
|
11
|
+
const path = trendPath(rootDir, checkId);
|
|
12
|
+
if (!existsSync(path)) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
17
|
+
} catch {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function appendTrendEntry(rootDir, checkId, entry) {
|
|
22
|
+
const path = trendPath(rootDir, checkId);
|
|
23
|
+
const dir = dirname(path);
|
|
24
|
+
if (!existsSync(dir)) {
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
const entries = loadTrendHistory(rootDir, checkId);
|
|
28
|
+
entries.push(entry);
|
|
29
|
+
while (entries.length > HISTORY_MAX_ENTRIES) {
|
|
30
|
+
entries.shift();
|
|
31
|
+
}
|
|
32
|
+
writeFileSync(path, JSON.stringify(entries, null, 2));
|
|
33
|
+
}
|
|
5
34
|
|
|
6
35
|
// src/history/history-store.ts
|
|
7
36
|
function loadHistory(rootDir) {
|
|
@@ -23,7 +52,7 @@ function saveHistory(rootDir, entries) {
|
|
|
23
52
|
}
|
|
24
53
|
writeFileSync(path, JSON.stringify(entries, null, 2));
|
|
25
54
|
}
|
|
26
|
-
function createHistoryEntry(results, rootDir, packages) {
|
|
55
|
+
function createHistoryEntry(results, rootDir, packages, runContext) {
|
|
27
56
|
let commit = "unknown";
|
|
28
57
|
let branch = "unknown";
|
|
29
58
|
let message = "";
|
|
@@ -63,16 +92,24 @@ function createHistoryEntry(results, rootDir, packages) {
|
|
|
63
92
|
submodules,
|
|
64
93
|
status: hasFailures ? "failed" : "passed",
|
|
65
94
|
summary,
|
|
66
|
-
failedPackages
|
|
95
|
+
failedPackages,
|
|
96
|
+
...runContext ? { runContext } : {}
|
|
67
97
|
};
|
|
68
98
|
}
|
|
69
|
-
function appendEntry(rootDir, entry) {
|
|
99
|
+
function appendEntry(rootDir, entry, checks) {
|
|
70
100
|
const history = loadHistory(rootDir);
|
|
71
101
|
history.push(entry);
|
|
72
102
|
while (history.length > HISTORY_MAX_ENTRIES) {
|
|
73
103
|
history.shift();
|
|
74
104
|
}
|
|
75
105
|
saveHistory(rootDir, history);
|
|
106
|
+
if (checks) {
|
|
107
|
+
for (const check of checks) {
|
|
108
|
+
if (check.trending && entry.summary[check.id] !== void 0) {
|
|
109
|
+
appendTrendEntry(rootDir, check.id, entry);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
76
113
|
}
|
|
77
114
|
function collectCheckTypes(entries) {
|
|
78
115
|
const s = /* @__PURE__ */ new Set();
|
|
@@ -319,6 +356,6 @@ function getPackageTimeline(history, packageName) {
|
|
|
319
356
|
};
|
|
320
357
|
}
|
|
321
358
|
|
|
322
|
-
export { analyzeEnrichedTrends, analyzeTrends, appendEntry, createHistoryEntry, detectRegressions, getPackageTimeline, loadHistory, saveHistory };
|
|
359
|
+
export { analyzeEnrichedTrends, analyzeTrends, appendEntry, appendTrendEntry, createHistoryEntry, detectRegressions, getPackageTimeline, loadHistory, loadTrendHistory, saveHistory };
|
|
323
360
|
//# sourceMappingURL=index.js.map
|
|
324
361
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/history/history-store.ts","../../src/history/trend-analyzer.ts","../../src/history/regression-detector.ts","../../src/history/package-timeline.ts"],"names":["delta"],"mappings":"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","file":"index.js","sourcesContent":["import { execSync } from 'node:child_process';\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { PATHS, HISTORY_MAX_ENTRIES } from '@kb-labs/qa-contracts';\nimport type { HistoryEntry, QAResults, SubmoduleInfo, WorkspacePackage } from '@kb-labs/qa-contracts';\n\n/**\n * Load history entries from disk.\n */\nexport function loadHistory(rootDir: string): HistoryEntry[] {\n const path = join(rootDir, PATHS.HISTORY);\n if (!existsSync(path)) {return [];}\n try {\n return JSON.parse(readFileSync(path, 'utf-8'));\n } catch {\n return [];\n }\n}\n\n/**\n * Save history entries to disk.\n */\nexport function saveHistory(rootDir: string, entries: HistoryEntry[]): void {\n const path = join(rootDir, PATHS.HISTORY);\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(path, JSON.stringify(entries, null, 2));\n}\n\n/**\n * Create a history entry from QA results.\n */\nexport function createHistoryEntry(\n results: QAResults,\n rootDir: string,\n packages?: WorkspacePackage[],\n): HistoryEntry {\n let commit = 'unknown';\n let branch = 'unknown';\n let message = '';\n\n try {\n commit = execSync('git rev-parse --short HEAD', { cwd: rootDir, encoding: 'utf-8' }).trim();\n branch = execSync('git rev-parse --abbrev-ref HEAD', { cwd: rootDir, encoding: 'utf-8' }).trim();\n message = execSync('git log -1 --format=%s', { cwd: rootDir, encoding: 'utf-8' }).trim();\n } catch {\n // git not available\n }\n\n const hasFailures = Object.values(results).some(r => r.failed.length > 0);\n\n const summary = {} as HistoryEntry['summary'];\n const failedPackages = {} as HistoryEntry['failedPackages'];\n\n for (const ct of Object.keys(results)) {\n const r = results[ct]!;\n summary[ct] = {\n passed: r.passed.length,\n failed: r.failed.length,\n skipped: r.skipped.length,\n };\n failedPackages[ct] = [...r.failed];\n }\n\n // Collect submodule info from packages (deduplicated by repo)\n let submodules: Record<string, SubmoduleInfo> | undefined;\n if (packages) {\n const subs: Record<string, SubmoduleInfo> = {};\n for (const pkg of packages) {\n if (pkg.submodule && !subs[pkg.repo]) {\n subs[pkg.repo] = pkg.submodule;\n }\n }\n if (Object.keys(subs).length > 0) {\n submodules = subs;\n }\n }\n\n return {\n timestamp: new Date().toISOString(),\n git: { commit, branch, message },\n submodules,\n status: hasFailures ? 'failed' : 'passed',\n summary,\n failedPackages,\n };\n}\n\n/**\n * Append a history entry, keeping max HISTORY_MAX_ENTRIES.\n */\nexport function appendEntry(rootDir: string, entry: HistoryEntry): void {\n const history = loadHistory(rootDir);\n history.push(entry);\n\n // Trim to max entries\n while (history.length > HISTORY_MAX_ENTRIES) {\n history.shift();\n }\n\n saveHistory(rootDir, history);\n}\n","import type { HistoryEntry, TrendResult, EnrichedTrendResult, TrendChangelogEntry } from '@kb-labs/qa-contracts';\nimport { TRENDS_WINDOW, getCheckLabel, getCheckIcon } from '@kb-labs/qa-contracts';\n\n/**\n * Collect all unique check type keys across a set of history entries.\n */\nfunction collectCheckTypes(entries: HistoryEntry[]): string[] {\n const s = new Set<string>();\n for (const e of entries) {\n for (const k of Object.keys(e.summary)) {s.add(k);}\n }\n return [...s];\n}\n\n/**\n * Analyze trends over a window of history entries.\n * Compares failure counts between first and last entry in the window.\n */\nexport function analyzeTrends(\n history: HistoryEntry[],\n window: number = TRENDS_WINDOW,\n): TrendResult[] {\n if (history.length < 2) {return [];}\n\n const windowEntries = history.slice(-window);\n const first = windowEntries[0]!;\n const last = windowEntries[windowEntries.length - 1]!;\n\n const results: TrendResult[] = [];\n\n for (const ct of collectCheckTypes([first, last])) {\n const previous = first.summary[ct]?.failed ?? 0;\n const current = last.summary[ct]?.failed ?? 0;\n const delta = current - previous;\n\n let trend: TrendResult['trend'];\n if (delta > 0) {trend = 'regression';}\n else if (delta < 0) {trend = 'improvement';}\n else {trend = 'no-change';}\n\n results.push({ checkType: ct, label: getCheckLabel(ct), icon: getCheckIcon(ct), previous, current, delta, trend });\n }\n\n return results;\n}\n\n/**\n * Enriched trend analysis — full time-series, per-entry changelog, velocity.\n *\n * For each check type, provides:\n * - timeSeries: all data points in the window (for charts)\n * - changelog: what changed between consecutive entries (new failures / fixes)\n * - velocity: average delta per entry (rate of change)\n */\nexport function analyzeEnrichedTrends(\n history: HistoryEntry[],\n window: number = TRENDS_WINDOW,\n): EnrichedTrendResult[] {\n if (history.length < 2) {return [];}\n\n const windowEntries = history.slice(-window);\n const first = windowEntries[0]!;\n const last = windowEntries[windowEntries.length - 1]!;\n\n const results: EnrichedTrendResult[] = [];\n\n for (const ct of collectCheckTypes(windowEntries)) {\n // Build time-series: map each entry to a data point\n const timeSeries = windowEntries.map((entry) => ({\n timestamp: entry.timestamp,\n gitCommit: entry.git.commit,\n gitBranch: entry.git.branch,\n gitMessage: entry.git.message,\n passed: entry.summary[ct]?.passed ?? 0,\n failed: entry.summary[ct]?.failed ?? 0,\n skipped: entry.summary[ct]?.skipped ?? 0,\n }));\n\n // Build changelog: diff failedPackages between consecutive entries\n const changelog: TrendChangelogEntry[] = [];\n const deltas: number[] = [];\n\n for (let i = 1; i < windowEntries.length; i++) {\n const prev = windowEntries[i - 1]!;\n const curr = windowEntries[i]!;\n\n const prevFailed = new Set(prev.failedPackages[ct] ?? []);\n const currFailed = curr.failedPackages[ct] ?? [];\n const currFailedSet = new Set(currFailed);\n\n const newFailures = currFailed.filter((p) => !prevFailed.has(p));\n const fixed = [...prevFailed].filter((p) => !currFailedSet.has(p));\n const delta = currFailed.length - prevFailed.size;\n\n deltas.push(delta);\n\n // Only include entries that actually had changes\n if (newFailures.length > 0 || fixed.length > 0) {\n changelog.push({\n timestamp: curr.timestamp,\n gitCommit: curr.git.commit,\n gitMessage: curr.git.message,\n newFailures,\n fixed,\n delta,\n });\n }\n }\n\n // Summary metrics\n const previous = first.summary[ct]?.failed ?? 0;\n const current = last.summary[ct]?.failed ?? 0;\n const delta = current - previous;\n\n let trend: EnrichedTrendResult['trend'];\n if (delta > 0) {trend = 'regression';}\n else if (delta < 0) {trend = 'improvement';}\n else {trend = 'no-change';}\n\n // Velocity: average delta per entry transition\n const velocity = deltas.length > 0\n ? deltas.reduce((sum, d) => sum + d, 0) / deltas.length\n : 0;\n\n results.push({\n checkType: ct,\n label: getCheckLabel(ct),\n icon: getCheckIcon(ct),\n timeSeries,\n changelog,\n current,\n previous,\n delta,\n trend,\n velocity: Math.round(velocity * 100) / 100,\n });\n }\n\n return results;\n}\n","import type { HistoryEntry, RegressionResult } from '@kb-labs/qa-contracts';\n\n/**\n * Detect regressions by comparing the last 2 history entries.\n * A regression = new packages failing that weren't failing before.\n */\nexport function detectRegressions(history: HistoryEntry[]): RegressionResult {\n if (history.length < 2) {\n return { hasRegressions: false, regressions: [] };\n }\n\n const previous = history[history.length - 2]!;\n const current = history[history.length - 1]!;\n\n const regressions: RegressionResult['regressions'] = [];\n\n const checkTypes = [...new Set([...Object.keys(previous.failedPackages), ...Object.keys(current.failedPackages)])];\n\n for (const ct of checkTypes) {\n const prevFailed = new Set(previous.failedPackages[ct] ?? []);\n const currFailed = current.failedPackages[ct] ?? [];\n\n const newFailures = currFailed.filter((p) => !prevFailed.has(p));\n const delta = currFailed.length - prevFailed.size;\n\n if (newFailures.length > 0) {\n regressions.push({\n checkType: ct,\n delta,\n newFailures,\n });\n }\n }\n\n return {\n hasRegressions: regressions.length > 0,\n regressions,\n };\n}\n","import type { HistoryEntry, CheckType } from '@kb-labs/qa-contracts';\nimport type { PackageTimelineEntry, PackageTimelineResponse } from '@kb-labs/qa-contracts';\n\nfunction buildEntries(history: HistoryEntry[], packageName: string): { entries: PackageTimelineEntry[]; repo: string } {\n let repo = 'unknown';\n const entries: PackageTimelineEntry[] = [];\n\n for (let i = history.length - 1; i >= 0; i--) {\n const h = history[i]!;\n const checks: Record<string, 'passed' | 'failed' | 'skipped'> = {};\n let found = false;\n\n for (const ct of Object.keys(h.summary)) {\n const failedList = h.failedPackages[ct] ?? [];\n const summaryEntry = h.summary[ct];\n if (failedList.includes(packageName)) {\n checks[ct] = 'failed'; found = true;\n } else if (summaryEntry && (summaryEntry.passed > 0 || summaryEntry.failed > 0)) {\n checks[ct] = 'passed'; found = true;\n } else {\n checks[ct] = 'skipped';\n }\n }\n\n if (!found) { continue; }\n\n let submoduleCommit: string | undefined;\n if (h.submodules) {\n for (const [repoName, info] of Object.entries(h.submodules)) {\n if (repoName === repo || repo === 'unknown') {\n submoduleCommit = info.commit;\n if (repo === 'unknown') { repo = repoName; }\n }\n }\n }\n\n entries.push({\n timestamp: h.timestamp,\n git: h.git,\n submoduleCommit,\n checks: checks as Record<CheckType, 'passed' | 'failed' | 'skipped'>,\n });\n }\n\n return { entries, repo };\n}\n\nfunction computeFlakyScore(entries: PackageTimelineEntry[]): { flakyScore: number; flakyChecks: CheckType[] } {\n const allCheckTypes = new Set<string>();\n for (const entry of entries) {\n for (const k of Object.keys(entry.checks)) { allCheckTypes.add(k); }\n }\n\n const flakyChecks: CheckType[] = [];\n let totalFlips = 0;\n let totalTransitions = 0;\n\n for (const ct of allCheckTypes) {\n let flips = 0;\n let transitions = 0;\n for (let i = 1; i < entries.length; i++) {\n const prev = entries[i - 1]!.checks[ct];\n const curr = entries[i]!.checks[ct];\n if (prev === 'skipped' || curr === 'skipped' || !prev || !curr) { continue; }\n transitions++;\n if (prev !== curr) { flips++; }\n }\n if (transitions > 0 && flips / transitions > 0.3) { flakyChecks.push(ct as CheckType); }\n totalFlips += flips;\n totalTransitions += transitions;\n }\n\n return {\n flakyScore: totalTransitions > 0 ? Math.min(1, totalFlips / totalTransitions) : 0,\n flakyChecks,\n };\n}\n\nfunction computeStreak(entries: PackageTimelineEntry[]): { status: 'passing' | 'failing'; count: number } {\n const latest = entries[0];\n if (!latest) { return { status: 'passing', count: 0 }; }\n\n const streakStatus: 'passing' | 'failing' = Object.values(latest.checks).some((v) => v === 'failed') ? 'failing' : 'passing';\n let count = 1;\n for (let i = 1; i < entries.length; i++) {\n const eFail = Object.values(entries[i]!.checks).some((v) => v === 'failed');\n if ((eFail ? 'failing' : 'passing') !== streakStatus) { break; }\n count++;\n }\n return { status: streakStatus, count };\n}\n\n/**\n * Build a timeline for a specific package across QA history.\n * Computes flaky score, current streak, and first failure timestamp.\n */\nexport function getPackageTimeline(\n history: HistoryEntry[],\n packageName: string,\n): PackageTimelineResponse {\n const { entries, repo } = buildEntries(history, packageName);\n const { flakyScore, flakyChecks } = computeFlakyScore(entries);\n\n let firstFailure: string | undefined;\n for (let i = entries.length - 1; i >= 0; i--) {\n if (Object.values(entries[i]!.checks).some((v) => v === 'failed')) {\n firstFailure = entries[i]!.timestamp;\n }\n }\n\n return {\n packageName,\n repo,\n entries,\n flakyScore: Math.round(flakyScore * 100) / 100,\n flakyChecks,\n firstFailure,\n currentStreak: computeStreak(entries),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/history/trend-store.ts","../../src/history/history-store.ts","../../src/history/trend-analyzer.ts","../../src/history/regression-detector.ts","../../src/history/package-timeline.ts"],"names":["join","existsSync","readFileSync","dirname","mkdirSync","writeFileSync","HISTORY_MAX_ENTRIES","delta"],"mappings":"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","file":"index.js","sourcesContent":["import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { QA_DATA_DIR, HISTORY_MAX_ENTRIES } from '@kb-labs/qa-contracts';\nimport type { HistoryEntry } from '@kb-labs/qa-contracts';\n\nfunction trendPath(rootDir: string, checkId: string): string {\n return join(rootDir, QA_DATA_DIR, 'trends', `${checkId}.json`);\n}\n\n/**\n * Load per-check trend history from disk.\n */\nexport function loadTrendHistory(rootDir: string, checkId: string): HistoryEntry[] {\n const path = trendPath(rootDir, checkId);\n if (!existsSync(path)) { return []; }\n try {\n return JSON.parse(readFileSync(path, 'utf-8'));\n } catch {\n return [];\n }\n}\n\n/**\n * Append a history entry to the per-check trend store.\n * Keeps max HISTORY_MAX_ENTRIES entries per check.\n */\nexport function appendTrendEntry(rootDir: string, checkId: string, entry: HistoryEntry): void {\n const path = trendPath(rootDir, checkId);\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const entries = loadTrendHistory(rootDir, checkId);\n entries.push(entry);\n while (entries.length > HISTORY_MAX_ENTRIES) {\n entries.shift();\n }\n writeFileSync(path, JSON.stringify(entries, null, 2));\n}\n","import { execSync } from 'node:child_process';\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { PATHS, HISTORY_MAX_ENTRIES } from '@kb-labs/qa-contracts';\nimport type { HistoryEntry, QAResults, SubmoduleInfo, WorkspacePackage, QACheckConfig } from '@kb-labs/qa-contracts';\nimport { appendTrendEntry } from './trend-store.js';\n\n/**\n * Load history entries from disk.\n */\nexport function loadHistory(rootDir: string): HistoryEntry[] {\n const path = join(rootDir, PATHS.HISTORY);\n if (!existsSync(path)) {return [];}\n try {\n return JSON.parse(readFileSync(path, 'utf-8'));\n } catch {\n return [];\n }\n}\n\n/**\n * Save history entries to disk.\n */\nexport function saveHistory(rootDir: string, entries: HistoryEntry[]): void {\n const path = join(rootDir, PATHS.HISTORY);\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(path, JSON.stringify(entries, null, 2));\n}\n\n/**\n * Create a history entry from QA results.\n */\nexport function createHistoryEntry(\n results: QAResults,\n rootDir: string,\n packages?: WorkspacePackage[],\n runContext?: HistoryEntry['runContext'],\n): HistoryEntry {\n let commit = 'unknown';\n let branch = 'unknown';\n let message = '';\n\n try {\n commit = execSync('git rev-parse --short HEAD', { cwd: rootDir, encoding: 'utf-8' }).trim();\n branch = execSync('git rev-parse --abbrev-ref HEAD', { cwd: rootDir, encoding: 'utf-8' }).trim();\n message = execSync('git log -1 --format=%s', { cwd: rootDir, encoding: 'utf-8' }).trim();\n } catch {\n // git not available\n }\n\n const hasFailures = Object.values(results).some(r => r.failed.length > 0);\n\n const summary = {} as HistoryEntry['summary'];\n const failedPackages = {} as HistoryEntry['failedPackages'];\n\n for (const ct of Object.keys(results)) {\n const r = results[ct]!;\n summary[ct] = {\n passed: r.passed.length,\n failed: r.failed.length,\n skipped: r.skipped.length,\n };\n failedPackages[ct] = [...r.failed];\n }\n\n // Collect submodule info from packages (deduplicated by repo)\n let submodules: Record<string, SubmoduleInfo> | undefined;\n if (packages) {\n const subs: Record<string, SubmoduleInfo> = {};\n for (const pkg of packages) {\n if (pkg.submodule && !subs[pkg.repo]) {\n subs[pkg.repo] = pkg.submodule;\n }\n }\n if (Object.keys(subs).length > 0) {\n submodules = subs;\n }\n }\n\n return {\n timestamp: new Date().toISOString(),\n git: { commit, branch, message },\n submodules,\n status: hasFailures ? 'failed' : 'passed',\n summary,\n failedPackages,\n ...(runContext ? { runContext } : {}),\n };\n}\n\n/**\n * Append a history entry, keeping max HISTORY_MAX_ENTRIES.\n * If checks config is provided, also writes to per-check trend store for checks with trending:true.\n */\nexport function appendEntry(rootDir: string, entry: HistoryEntry, checks?: QACheckConfig[]): void {\n const history = loadHistory(rootDir);\n history.push(entry);\n\n // Trim to max entries\n while (history.length > HISTORY_MAX_ENTRIES) {\n history.shift();\n }\n\n saveHistory(rootDir, history);\n\n // Write to per-check trend store for trending checks\n if (checks) {\n for (const check of checks) {\n if (check.trending && entry.summary[check.id] !== undefined) {\n appendTrendEntry(rootDir, check.id, entry);\n }\n }\n }\n}\n","import type { HistoryEntry, TrendResult, EnrichedTrendResult, TrendChangelogEntry } from '@kb-labs/qa-contracts';\nimport { TRENDS_WINDOW, getCheckLabel, getCheckIcon } from '@kb-labs/qa-contracts';\n\n/**\n * Collect all unique check type keys across a set of history entries.\n */\nfunction collectCheckTypes(entries: HistoryEntry[]): string[] {\n const s = new Set<string>();\n for (const e of entries) {\n for (const k of Object.keys(e.summary)) {s.add(k);}\n }\n return [...s];\n}\n\n/**\n * Analyze trends over a window of history entries.\n * Compares failure counts between first and last entry in the window.\n */\nexport function analyzeTrends(\n history: HistoryEntry[],\n window: number = TRENDS_WINDOW,\n): TrendResult[] {\n if (history.length < 2) {return [];}\n\n const windowEntries = history.slice(-window);\n const first = windowEntries[0]!;\n const last = windowEntries[windowEntries.length - 1]!;\n\n const results: TrendResult[] = [];\n\n for (const ct of collectCheckTypes([first, last])) {\n const previous = first.summary[ct]?.failed ?? 0;\n const current = last.summary[ct]?.failed ?? 0;\n const delta = current - previous;\n\n let trend: TrendResult['trend'];\n if (delta > 0) {trend = 'regression';}\n else if (delta < 0) {trend = 'improvement';}\n else {trend = 'no-change';}\n\n results.push({ checkType: ct, label: getCheckLabel(ct), icon: getCheckIcon(ct), previous, current, delta, trend });\n }\n\n return results;\n}\n\n/**\n * Enriched trend analysis — full time-series, per-entry changelog, velocity.\n *\n * For each check type, provides:\n * - timeSeries: all data points in the window (for charts)\n * - changelog: what changed between consecutive entries (new failures / fixes)\n * - velocity: average delta per entry (rate of change)\n */\nexport function analyzeEnrichedTrends(\n history: HistoryEntry[],\n window: number = TRENDS_WINDOW,\n): EnrichedTrendResult[] {\n if (history.length < 2) {return [];}\n\n const windowEntries = history.slice(-window);\n const first = windowEntries[0]!;\n const last = windowEntries[windowEntries.length - 1]!;\n\n const results: EnrichedTrendResult[] = [];\n\n for (const ct of collectCheckTypes(windowEntries)) {\n // Build time-series: map each entry to a data point\n const timeSeries = windowEntries.map((entry) => ({\n timestamp: entry.timestamp,\n gitCommit: entry.git.commit,\n gitBranch: entry.git.branch,\n gitMessage: entry.git.message,\n passed: entry.summary[ct]?.passed ?? 0,\n failed: entry.summary[ct]?.failed ?? 0,\n skipped: entry.summary[ct]?.skipped ?? 0,\n }));\n\n // Build changelog: diff failedPackages between consecutive entries\n const changelog: TrendChangelogEntry[] = [];\n const deltas: number[] = [];\n\n for (let i = 1; i < windowEntries.length; i++) {\n const prev = windowEntries[i - 1]!;\n const curr = windowEntries[i]!;\n\n const prevFailed = new Set(prev.failedPackages[ct] ?? []);\n const currFailed = curr.failedPackages[ct] ?? [];\n const currFailedSet = new Set(currFailed);\n\n const newFailures = currFailed.filter((p) => !prevFailed.has(p));\n const fixed = [...prevFailed].filter((p) => !currFailedSet.has(p));\n const delta = currFailed.length - prevFailed.size;\n\n deltas.push(delta);\n\n // Only include entries that actually had changes\n if (newFailures.length > 0 || fixed.length > 0) {\n changelog.push({\n timestamp: curr.timestamp,\n gitCommit: curr.git.commit,\n gitMessage: curr.git.message,\n newFailures,\n fixed,\n delta,\n });\n }\n }\n\n // Summary metrics\n const previous = first.summary[ct]?.failed ?? 0;\n const current = last.summary[ct]?.failed ?? 0;\n const delta = current - previous;\n\n let trend: EnrichedTrendResult['trend'];\n if (delta > 0) {trend = 'regression';}\n else if (delta < 0) {trend = 'improvement';}\n else {trend = 'no-change';}\n\n // Velocity: average delta per entry transition\n const velocity = deltas.length > 0\n ? deltas.reduce((sum, d) => sum + d, 0) / deltas.length\n : 0;\n\n results.push({\n checkType: ct,\n label: getCheckLabel(ct),\n icon: getCheckIcon(ct),\n timeSeries,\n changelog,\n current,\n previous,\n delta,\n trend,\n velocity: Math.round(velocity * 100) / 100,\n });\n }\n\n return results;\n}\n","import type { HistoryEntry, RegressionResult } from '@kb-labs/qa-contracts';\n\n/**\n * Detect regressions by comparing the last 2 history entries.\n * A regression = new packages failing that weren't failing before.\n */\nexport function detectRegressions(history: HistoryEntry[]): RegressionResult {\n if (history.length < 2) {\n return { hasRegressions: false, regressions: [] };\n }\n\n const previous = history[history.length - 2]!;\n const current = history[history.length - 1]!;\n\n const regressions: RegressionResult['regressions'] = [];\n\n const checkTypes = [...new Set([...Object.keys(previous.failedPackages), ...Object.keys(current.failedPackages)])];\n\n for (const ct of checkTypes) {\n const prevFailed = new Set(previous.failedPackages[ct] ?? []);\n const currFailed = current.failedPackages[ct] ?? [];\n\n const newFailures = currFailed.filter((p) => !prevFailed.has(p));\n const delta = currFailed.length - prevFailed.size;\n\n if (newFailures.length > 0) {\n regressions.push({\n checkType: ct,\n delta,\n newFailures,\n });\n }\n }\n\n return {\n hasRegressions: regressions.length > 0,\n regressions,\n };\n}\n","import type { HistoryEntry, CheckType } from '@kb-labs/qa-contracts';\nimport type { PackageTimelineEntry, PackageTimelineResponse } from '@kb-labs/qa-contracts';\n\nfunction buildEntries(history: HistoryEntry[], packageName: string): { entries: PackageTimelineEntry[]; repo: string } {\n let repo = 'unknown';\n const entries: PackageTimelineEntry[] = [];\n\n for (let i = history.length - 1; i >= 0; i--) {\n const h = history[i]!;\n const checks: Record<string, 'passed' | 'failed' | 'skipped'> = {};\n let found = false;\n\n for (const ct of Object.keys(h.summary)) {\n const failedList = h.failedPackages[ct] ?? [];\n const summaryEntry = h.summary[ct];\n if (failedList.includes(packageName)) {\n checks[ct] = 'failed'; found = true;\n } else if (summaryEntry && (summaryEntry.passed > 0 || summaryEntry.failed > 0)) {\n checks[ct] = 'passed'; found = true;\n } else {\n checks[ct] = 'skipped';\n }\n }\n\n if (!found) { continue; }\n\n let submoduleCommit: string | undefined;\n if (h.submodules) {\n for (const [repoName, info] of Object.entries(h.submodules)) {\n if (repoName === repo || repo === 'unknown') {\n submoduleCommit = info.commit;\n if (repo === 'unknown') { repo = repoName; }\n }\n }\n }\n\n entries.push({\n timestamp: h.timestamp,\n git: h.git,\n submoduleCommit,\n checks: checks as Record<CheckType, 'passed' | 'failed' | 'skipped'>,\n });\n }\n\n return { entries, repo };\n}\n\nfunction computeFlakyScore(entries: PackageTimelineEntry[]): { flakyScore: number; flakyChecks: CheckType[] } {\n const allCheckTypes = new Set<string>();\n for (const entry of entries) {\n for (const k of Object.keys(entry.checks)) { allCheckTypes.add(k); }\n }\n\n const flakyChecks: CheckType[] = [];\n let totalFlips = 0;\n let totalTransitions = 0;\n\n for (const ct of allCheckTypes) {\n let flips = 0;\n let transitions = 0;\n for (let i = 1; i < entries.length; i++) {\n const prev = entries[i - 1]!.checks[ct];\n const curr = entries[i]!.checks[ct];\n if (prev === 'skipped' || curr === 'skipped' || !prev || !curr) { continue; }\n transitions++;\n if (prev !== curr) { flips++; }\n }\n if (transitions > 0 && flips / transitions > 0.3) { flakyChecks.push(ct as CheckType); }\n totalFlips += flips;\n totalTransitions += transitions;\n }\n\n return {\n flakyScore: totalTransitions > 0 ? Math.min(1, totalFlips / totalTransitions) : 0,\n flakyChecks,\n };\n}\n\nfunction computeStreak(entries: PackageTimelineEntry[]): { status: 'passing' | 'failing'; count: number } {\n const latest = entries[0];\n if (!latest) { return { status: 'passing', count: 0 }; }\n\n const streakStatus: 'passing' | 'failing' = Object.values(latest.checks).some((v) => v === 'failed') ? 'failing' : 'passing';\n let count = 1;\n for (let i = 1; i < entries.length; i++) {\n const eFail = Object.values(entries[i]!.checks).some((v) => v === 'failed');\n if ((eFail ? 'failing' : 'passing') !== streakStatus) { break; }\n count++;\n }\n return { status: streakStatus, count };\n}\n\n/**\n * Build a timeline for a specific package across QA history.\n * Computes flaky score, current streak, and first failure timestamp.\n */\nexport function getPackageTimeline(\n history: HistoryEntry[],\n packageName: string,\n): PackageTimelineResponse {\n const { entries, repo } = buildEntries(history, packageName);\n const { flakyScore, flakyChecks } = computeFlakyScore(entries);\n\n let firstFailure: string | undefined;\n for (let i = entries.length - 1; i >= 0; i--) {\n if (Object.values(entries[i]!.checks).some((v) => v === 'failed')) {\n firstFailure = entries[i]!.timestamp;\n }\n }\n\n return {\n packageName,\n repo,\n entries,\n flakyScore: Math.round(flakyScore * 100) / 100,\n flakyChecks,\n firstFailure,\n currentStreak: computeStreak(entries),\n };\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { L as LastRunData, c as collectSubmoduleInfo, g as getSubmoduleInfo, a as getWorkspacePackages, l as loadLastRun, r as
|
|
1
|
+
export { L as LastRunData, c as collectSubmoduleInfo, g as getSubmoduleInfo, a as getWorkspacePackages, l as loadLastRun, r as runCustomChecks, b as runLintCheck, d as runQA, e as runTestCheck, f as runTypeCheck, s as saveLastRun } from './last-run-store-C1AkskWI.js';
|
|
2
2
|
export { captureBaseline, compareWithBaseline, createBaselineFromResults, loadBaseline, saveBaseline } from './baseline/index.js';
|
|
3
|
-
export { analyzeEnrichedTrends, analyzeTrends, appendEntry, createHistoryEntry, detectRegressions, getPackageTimeline, loadHistory, saveHistory } from './history/index.js';
|
|
3
|
+
export { analyzeEnrichedTrends, analyzeTrends, appendEntry, appendTrendEntry, createHistoryEntry, detectRegressions, getPackageTimeline, loadHistory, loadTrendHistory, saveHistory } from './history/index.js';
|
|
4
4
|
export { ReportSection, buildBaselineReport, buildDetailedJsonReport, buildDetailedRunReport, buildHistoryTable, buildJsonReport, buildRegressionsReport, buildRunReport, buildTrendsReport, groupErrors, groupResults } from './report/index.js';
|
|
5
5
|
export { resolveCategories } from './categories/index.js';
|
|
6
6
|
import '@kb-labs/qa-contracts';
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync, statSy
|
|
|
2
2
|
import { join, relative, dirname, resolve } from 'path';
|
|
3
3
|
import { execSync, spawnSync } from 'child_process';
|
|
4
4
|
import { createHash } from 'crypto';
|
|
5
|
-
import { TRENDS_WINDOW, PATHS, HISTORY_MAX_ENTRIES, getCheckIcon, getCheckLabel } from '@kb-labs/qa-contracts';
|
|
5
|
+
import { TRENDS_WINDOW, PATHS, HISTORY_MAX_ENTRIES, getCheckIcon, getCheckLabel, QA_DATA_DIR } from '@kb-labs/qa-contracts';
|
|
6
6
|
|
|
7
7
|
// src/runner/workspace.ts
|
|
8
8
|
function getSubmoduleInfo(repoDir, repoName) {
|
|
@@ -279,21 +279,39 @@ var ID_MAP = {
|
|
|
279
279
|
tests: "test"
|
|
280
280
|
};
|
|
281
281
|
function emptyResult() {
|
|
282
|
-
return { passed: [], failed: [], skipped: [], errors: {} };
|
|
282
|
+
return { passed: [], failed: [], skipped: [], errors: {}, details: {} };
|
|
283
283
|
}
|
|
284
|
-
function runCommand(command, args, cwd, timeoutMs) {
|
|
284
|
+
function runCommand(command, args, cwd, timeoutMs, extraEnv) {
|
|
285
285
|
try {
|
|
286
|
-
const
|
|
286
|
+
const env = extraEnv ? { ...process.env, ...extraEnv } : void 0;
|
|
287
|
+
const result = spawnSync(command, args, { cwd, timeout: timeoutMs, encoding: "utf-8", shell: false, env });
|
|
287
288
|
const stdout = result.stdout ?? "";
|
|
288
289
|
const stderr = result.stderr ?? "";
|
|
289
290
|
const exitCode = result.status ?? 1;
|
|
290
291
|
return { ok: exitCode === 0, stdout, stderr, exitCode };
|
|
291
292
|
} catch (e) {
|
|
292
|
-
return { ok: false, stdout: "", stderr: e.message
|
|
293
|
+
return { ok: false, stdout: "", stderr: e instanceof Error ? e.message : String(e), exitCode: 1 };
|
|
293
294
|
}
|
|
294
295
|
}
|
|
296
|
+
function parseTypedOutput(stdout) {
|
|
297
|
+
try {
|
|
298
|
+
const parsed = JSON.parse(stdout);
|
|
299
|
+
if (typeof parsed === "object" && parsed !== null && "ok" in parsed) {
|
|
300
|
+
return {
|
|
301
|
+
ok: parsed.ok === true,
|
|
302
|
+
items: Array.isArray(parsed.items) ? parsed.items : []
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
} catch {
|
|
306
|
+
}
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
295
309
|
function evaluate(check, stdout, stderr, exitCode) {
|
|
296
310
|
if ((check.parser ?? "exitcode") === "json") {
|
|
311
|
+
const typed = parseTypedOutput(stdout);
|
|
312
|
+
if (typed !== null) {
|
|
313
|
+
return typed.ok;
|
|
314
|
+
}
|
|
297
315
|
try {
|
|
298
316
|
const parsed = JSON.parse(stdout);
|
|
299
317
|
return parsed.ok === true || parsed.success === true || parsed.status === "ok";
|
|
@@ -303,23 +321,54 @@ function evaluate(check, stdout, stderr, exitCode) {
|
|
|
303
321
|
}
|
|
304
322
|
return exitCode === 0;
|
|
305
323
|
}
|
|
306
|
-
function recordResult(bucket, key, passed, stdout, stderr, exitCode, canonicalId, onProgress, durationMs) {
|
|
324
|
+
function recordResult(bucket, key, passed, stdout, stderr, exitCode, check, canonicalId, onProgress, durationMs) {
|
|
307
325
|
if (passed) {
|
|
308
326
|
bucket.passed.push(key);
|
|
309
327
|
onProgress?.(canonicalId, key, "pass", durationMs);
|
|
310
328
|
} else {
|
|
311
329
|
bucket.failed.push(key);
|
|
312
330
|
bucket.errors[key] = stderr || stdout || `Exit code ${exitCode}`;
|
|
331
|
+
if ((check.parser ?? "exitcode") === "json") {
|
|
332
|
+
const typed = parseTypedOutput(stdout);
|
|
333
|
+
if (typed && typed.items.length > 0) {
|
|
334
|
+
bucket.details[key] = typed.items;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
313
337
|
onProgress?.(canonicalId, key, "fail", durationMs);
|
|
314
338
|
}
|
|
315
339
|
}
|
|
316
|
-
function
|
|
340
|
+
function getDiffPackageDirs(rootDir, base, diffFilter) {
|
|
341
|
+
try {
|
|
342
|
+
const filter = diffFilter ? `--diff-filter=${diffFilter}` : "";
|
|
343
|
+
const args = ["diff", filter, `${base}...HEAD`, "--name-only"].filter(Boolean);
|
|
344
|
+
const output = execSync(`git ${args.join(" ")}`, { cwd: rootDir, encoding: "utf-8" }).trim();
|
|
345
|
+
if (!output) {
|
|
346
|
+
return /* @__PURE__ */ new Set();
|
|
347
|
+
}
|
|
348
|
+
return new Set(output.split("\n").map((f) => f.split("/")[0]).filter((s) => Boolean(s)));
|
|
349
|
+
} catch {
|
|
350
|
+
return /* @__PURE__ */ new Set();
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
function filterPackagesByDiff(packages, rootDir, diffFilter, base) {
|
|
354
|
+
const touchedDirs = getDiffPackageDirs(rootDir, base, diffFilter);
|
|
355
|
+
if (touchedDirs.size === 0) {
|
|
356
|
+
return [];
|
|
357
|
+
}
|
|
358
|
+
return packages.filter((pkg) => {
|
|
359
|
+
const relParts = pkg.relativePath.split("/");
|
|
360
|
+
return relParts.some((_, i) => touchedDirs.has(relParts.slice(0, i + 1).join("/")));
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
function runInRepoRoot(check, canonicalId, resolvedArgs, rootDir, bucket, onProgress, diffBase) {
|
|
317
364
|
const startMs = Date.now();
|
|
318
|
-
const
|
|
319
|
-
|
|
365
|
+
const env = diffBase ? { KB_QA_BASE: diffBase } : void 0;
|
|
366
|
+
const { stderr, exitCode, stdout } = runCommand(check.command, resolvedArgs, rootDir, check.timeoutMs ?? 12e4, env);
|
|
367
|
+
recordResult(bucket, rootDir, evaluate(check, stdout, stderr, exitCode), stdout, stderr, exitCode, check, canonicalId, onProgress, Date.now() - startMs);
|
|
320
368
|
}
|
|
321
|
-
function runInScopePath(check, canonicalId, resolvedArgs, packages, rootDir, bucket, onProgress) {
|
|
369
|
+
function runInScopePath(check, canonicalId, resolvedArgs, packages, rootDir, bucket, onProgress, diffBase) {
|
|
322
370
|
const seen = /* @__PURE__ */ new Set();
|
|
371
|
+
const env = diffBase ? { KB_QA_BASE: diffBase } : void 0;
|
|
323
372
|
for (const pkg of packages) {
|
|
324
373
|
if (seen.has(pkg.repo)) {
|
|
325
374
|
continue;
|
|
@@ -330,8 +379,8 @@ function runInScopePath(check, canonicalId, resolvedArgs, packages, rootDir, buc
|
|
|
330
379
|
continue;
|
|
331
380
|
}
|
|
332
381
|
const startMs = Date.now();
|
|
333
|
-
const { stderr, exitCode, stdout } = runCommand(check.command, resolvedArgs, scopeDir, check.timeoutMs ?? 12e4);
|
|
334
|
-
recordResult(bucket, pkg.repo, evaluate(check, stdout, stderr, exitCode), stdout, stderr, exitCode, canonicalId, onProgress, Date.now() - startMs);
|
|
382
|
+
const { stderr, exitCode, stdout } = runCommand(check.command, resolvedArgs, scopeDir, check.timeoutMs ?? 12e4, env);
|
|
383
|
+
recordResult(bucket, pkg.repo, evaluate(check, stdout, stderr, exitCode), stdout, stderr, exitCode, check, canonicalId, onProgress, Date.now() - startMs);
|
|
335
384
|
}
|
|
336
385
|
}
|
|
337
386
|
function getPnpmScriptName(check) {
|
|
@@ -352,9 +401,10 @@ function hasNpmScript(pkgDir, scriptName) {
|
|
|
352
401
|
return false;
|
|
353
402
|
}
|
|
354
403
|
}
|
|
355
|
-
function runPerPackage(check, canonicalId, resolvedArgs, packages, bucket, onProgress) {
|
|
404
|
+
function runPerPackage(check, canonicalId, resolvedArgs, packages, bucket, onProgress, diffBase) {
|
|
356
405
|
const sortedPackages = check.ordered ? sortByBuildLayers(packages) : packages;
|
|
357
406
|
const scriptName = getPnpmScriptName(check);
|
|
407
|
+
const env = diffBase ? { KB_QA_BASE: diffBase } : void 0;
|
|
358
408
|
for (const pkg of sortedPackages) {
|
|
359
409
|
if (scriptName && !hasNpmScript(pkg.dir, scriptName)) {
|
|
360
410
|
bucket.skipped.push(pkg.name);
|
|
@@ -362,11 +412,11 @@ function runPerPackage(check, canonicalId, resolvedArgs, packages, bucket, onPro
|
|
|
362
412
|
continue;
|
|
363
413
|
}
|
|
364
414
|
const startMs = Date.now();
|
|
365
|
-
const { stderr, exitCode, stdout } = runCommand(check.command, resolvedArgs, pkg.dir, check.timeoutMs ?? 12e4);
|
|
366
|
-
recordResult(bucket, pkg.name, evaluate(check, stdout, stderr, exitCode), stdout, stderr, exitCode, canonicalId, onProgress, Date.now() - startMs);
|
|
415
|
+
const { stderr, exitCode, stdout } = runCommand(check.command, resolvedArgs, pkg.dir, check.timeoutMs ?? 12e4, env);
|
|
416
|
+
recordResult(bucket, pkg.name, evaluate(check, stdout, stderr, exitCode), stdout, stderr, exitCode, check, canonicalId, onProgress, Date.now() - startMs);
|
|
367
417
|
}
|
|
368
418
|
}
|
|
369
|
-
function runCustomChecks(checks, packages, rootDir, onProgress) {
|
|
419
|
+
function runCustomChecks(checks, packages, rootDir, onProgress, diffBase) {
|
|
370
420
|
const results = {};
|
|
371
421
|
for (const check of checks) {
|
|
372
422
|
const canonicalId = ID_MAP[check.id.toLowerCase()] ?? check.id;
|
|
@@ -380,12 +430,34 @@ function runCustomChecks(checks, packages, rootDir, onProgress) {
|
|
|
380
430
|
);
|
|
381
431
|
const runIn = check.runIn ?? "perPackage";
|
|
382
432
|
if (runIn === "repoRoot") {
|
|
383
|
-
runInRepoRoot(check, canonicalId, resolvedArgs, rootDir, bucket, onProgress);
|
|
433
|
+
runInRepoRoot(check, canonicalId, resolvedArgs, rootDir, bucket, onProgress, diffBase);
|
|
384
434
|
} else if (runIn === "scopePath") {
|
|
385
|
-
runInScopePath(check, canonicalId, resolvedArgs, packages, rootDir, bucket, onProgress);
|
|
435
|
+
runInScopePath(check, canonicalId, resolvedArgs, packages, rootDir, bucket, onProgress, diffBase);
|
|
436
|
+
} else if (runIn === "diffOnly" || runIn === "newFiles") {
|
|
437
|
+
const base = diffBase ?? "main";
|
|
438
|
+
const diffFilter = runIn === "newFiles" ? "A" : "";
|
|
439
|
+
const filteredPackages = filterPackagesByDiff(packages, rootDir, diffFilter, base);
|
|
440
|
+
if (filteredPackages.length === 0) {
|
|
441
|
+
bucket.skipped.push(...packages.map((p) => p.name));
|
|
442
|
+
for (const pkg of packages) {
|
|
443
|
+
onProgress?.(canonicalId, pkg.name, "skip");
|
|
444
|
+
}
|
|
445
|
+
} else {
|
|
446
|
+
const filteredNames = new Set(filteredPackages.map((p) => p.name));
|
|
447
|
+
for (const pkg of packages) {
|
|
448
|
+
if (!filteredNames.has(pkg.name)) {
|
|
449
|
+
bucket.skipped.push(pkg.name);
|
|
450
|
+
onProgress?.(canonicalId, pkg.name, "skip");
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
runPerPackage(check, canonicalId, resolvedArgs, filteredPackages, bucket, onProgress, base);
|
|
454
|
+
}
|
|
386
455
|
} else {
|
|
387
456
|
runPerPackage(check, canonicalId, resolvedArgs, packages, bucket, onProgress);
|
|
388
457
|
}
|
|
458
|
+
if (Object.keys(bucket.details).length > 0) {
|
|
459
|
+
results[canonicalId].details = bucket.details;
|
|
460
|
+
}
|
|
389
461
|
}
|
|
390
462
|
return results;
|
|
391
463
|
}
|
|
@@ -493,8 +565,9 @@ function runBuildCheck(options) {
|
|
|
493
565
|
} catch (err) {
|
|
494
566
|
const durationMs = Date.now() - startMs;
|
|
495
567
|
result.failed.push(pkg.name);
|
|
496
|
-
const
|
|
497
|
-
|
|
568
|
+
const spawnErr = err;
|
|
569
|
+
const rawErr = (spawnErr.stderr || spawnErr.stdout || spawnErr.message || "").trim();
|
|
570
|
+
result.errors[pkg.name] = rawErr.slice(0, 2e3) || `Build failed (exit code ${spawnErr.status ?? 1})`;
|
|
498
571
|
onProgress?.(pkg.name, "fail", durationMs);
|
|
499
572
|
}
|
|
500
573
|
}
|
|
@@ -522,8 +595,9 @@ function runLintCheck(options) {
|
|
|
522
595
|
onProgress?.(pkg.name, "pass", Date.now() - startMs);
|
|
523
596
|
} catch (err) {
|
|
524
597
|
result.failed.push(pkg.name);
|
|
525
|
-
const
|
|
526
|
-
|
|
598
|
+
const spawnErr = err;
|
|
599
|
+
const rawErr = (spawnErr.stdout || spawnErr.stderr || spawnErr.message || "").trim();
|
|
600
|
+
result.errors[pkg.name] = rawErr.slice(0, 2e3) || `Lint failed (exit code ${spawnErr.status ?? 1})`;
|
|
527
601
|
onProgress?.(pkg.name, "fail", Date.now() - startMs);
|
|
528
602
|
}
|
|
529
603
|
}
|
|
@@ -551,8 +625,9 @@ function runTypeCheck(options) {
|
|
|
551
625
|
onProgress?.(pkg.name, "pass", Date.now() - startMs);
|
|
552
626
|
} catch (err) {
|
|
553
627
|
result.failed.push(pkg.name);
|
|
554
|
-
const
|
|
555
|
-
|
|
628
|
+
const spawnErr = err;
|
|
629
|
+
const rawErr = (spawnErr.stdout || spawnErr.stderr || spawnErr.message || "").trim();
|
|
630
|
+
result.errors[pkg.name] = rawErr.slice(0, 2e3) || `Type check failed (exit code ${spawnErr.status ?? 1})`;
|
|
556
631
|
onProgress?.(pkg.name, "fail", Date.now() - startMs);
|
|
557
632
|
}
|
|
558
633
|
}
|
|
@@ -589,8 +664,9 @@ function runTestCheck(options) {
|
|
|
589
664
|
onProgress?.(pkg.name, "pass", Date.now() - startMs);
|
|
590
665
|
} catch (err) {
|
|
591
666
|
result.failed.push(pkg.name);
|
|
592
|
-
const
|
|
593
|
-
|
|
667
|
+
const spawnErr = err;
|
|
668
|
+
const rawErr = (spawnErr.stdout || spawnErr.stderr || spawnErr.message || "").trim();
|
|
669
|
+
result.errors[pkg.name] = rawErr.slice(0, 2e3) || `Test failed (exit code ${spawnErr.status ?? 1})`;
|
|
594
670
|
onProgress?.(pkg.name, "fail", Date.now() - startMs);
|
|
595
671
|
}
|
|
596
672
|
}
|
|
@@ -776,6 +852,35 @@ function compareWithBaseline(results, baseline) {
|
|
|
776
852
|
}
|
|
777
853
|
return diff;
|
|
778
854
|
}
|
|
855
|
+
function trendPath(rootDir, checkId) {
|
|
856
|
+
return join(rootDir, QA_DATA_DIR, "trends", `${checkId}.json`);
|
|
857
|
+
}
|
|
858
|
+
function loadTrendHistory(rootDir, checkId) {
|
|
859
|
+
const path = trendPath(rootDir, checkId);
|
|
860
|
+
if (!existsSync(path)) {
|
|
861
|
+
return [];
|
|
862
|
+
}
|
|
863
|
+
try {
|
|
864
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
865
|
+
} catch {
|
|
866
|
+
return [];
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
function appendTrendEntry(rootDir, checkId, entry) {
|
|
870
|
+
const path = trendPath(rootDir, checkId);
|
|
871
|
+
const dir = dirname(path);
|
|
872
|
+
if (!existsSync(dir)) {
|
|
873
|
+
mkdirSync(dir, { recursive: true });
|
|
874
|
+
}
|
|
875
|
+
const entries = loadTrendHistory(rootDir, checkId);
|
|
876
|
+
entries.push(entry);
|
|
877
|
+
while (entries.length > HISTORY_MAX_ENTRIES) {
|
|
878
|
+
entries.shift();
|
|
879
|
+
}
|
|
880
|
+
writeFileSync(path, JSON.stringify(entries, null, 2));
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// src/history/history-store.ts
|
|
779
884
|
function loadHistory(rootDir) {
|
|
780
885
|
const path = join(rootDir, PATHS.HISTORY);
|
|
781
886
|
if (!existsSync(path)) {
|
|
@@ -795,7 +900,7 @@ function saveHistory(rootDir, entries) {
|
|
|
795
900
|
}
|
|
796
901
|
writeFileSync(path, JSON.stringify(entries, null, 2));
|
|
797
902
|
}
|
|
798
|
-
function createHistoryEntry(results, rootDir, packages) {
|
|
903
|
+
function createHistoryEntry(results, rootDir, packages, runContext) {
|
|
799
904
|
let commit = "unknown";
|
|
800
905
|
let branch = "unknown";
|
|
801
906
|
let message = "";
|
|
@@ -835,16 +940,24 @@ function createHistoryEntry(results, rootDir, packages) {
|
|
|
835
940
|
submodules,
|
|
836
941
|
status: hasFailures ? "failed" : "passed",
|
|
837
942
|
summary,
|
|
838
|
-
failedPackages
|
|
943
|
+
failedPackages,
|
|
944
|
+
...runContext ? { runContext } : {}
|
|
839
945
|
};
|
|
840
946
|
}
|
|
841
|
-
function appendEntry(rootDir, entry) {
|
|
947
|
+
function appendEntry(rootDir, entry, checks) {
|
|
842
948
|
const history = loadHistory(rootDir);
|
|
843
949
|
history.push(entry);
|
|
844
950
|
while (history.length > HISTORY_MAX_ENTRIES) {
|
|
845
951
|
history.shift();
|
|
846
952
|
}
|
|
847
953
|
saveHistory(rootDir, history);
|
|
954
|
+
if (checks) {
|
|
955
|
+
for (const check of checks) {
|
|
956
|
+
if (check.trending && entry.summary[check.id] !== void 0) {
|
|
957
|
+
appendTrendEntry(rootDir, check.id, entry);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
848
961
|
}
|
|
849
962
|
function collectCheckTypes(entries) {
|
|
850
963
|
const s = /* @__PURE__ */ new Set();
|
|
@@ -1092,11 +1205,22 @@ function getPackageTimeline(history, packageName) {
|
|
|
1092
1205
|
}
|
|
1093
1206
|
|
|
1094
1207
|
// src/report/json-reporter.ts
|
|
1095
|
-
function buildJsonReport(results, diff) {
|
|
1096
|
-
const
|
|
1208
|
+
function buildJsonReport(results, diff, checks) {
|
|
1209
|
+
const severityMap = {};
|
|
1210
|
+
if (checks) {
|
|
1211
|
+
for (const c of checks) {
|
|
1212
|
+
severityMap[c.id] = c.severity ?? "blocker";
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
const hasBlockerFailures = Object.entries(results).some(([ct, r]) => {
|
|
1216
|
+
const sev = severityMap[ct] ?? "blocker";
|
|
1217
|
+
return sev !== "info" && sev !== "warning" && r.failed.length > 0;
|
|
1218
|
+
});
|
|
1097
1219
|
const summary = {};
|
|
1098
1220
|
const failures = {};
|
|
1099
1221
|
const errors = {};
|
|
1222
|
+
const blockers = [];
|
|
1223
|
+
const warnings = [];
|
|
1100
1224
|
for (const ct of Object.keys(results)) {
|
|
1101
1225
|
const r = results[ct];
|
|
1102
1226
|
const total = r.passed.length + r.failed.length + r.skipped.length;
|
|
@@ -1108,18 +1232,36 @@ function buildJsonReport(results, diff) {
|
|
|
1108
1232
|
};
|
|
1109
1233
|
failures[ct] = [...r.failed];
|
|
1110
1234
|
errors[ct] = { ...r.errors };
|
|
1235
|
+
if (r.failed.length > 0) {
|
|
1236
|
+
const sev = severityMap[ct] ?? "blocker";
|
|
1237
|
+
const items = r.failed.flatMap((target) => {
|
|
1238
|
+
const detailItems = r.details?.[target];
|
|
1239
|
+
if (detailItems && detailItems.length > 0) {
|
|
1240
|
+
return detailItems;
|
|
1241
|
+
}
|
|
1242
|
+
const msg = r.errors[target];
|
|
1243
|
+
return msg ? [{ target, message: msg }] : [{ target, message: `check ${ct} failed` }];
|
|
1244
|
+
});
|
|
1245
|
+
if (sev === "blocker") {
|
|
1246
|
+
blockers.push({ check: ct, items });
|
|
1247
|
+
} else if (sev === "warning") {
|
|
1248
|
+
warnings.push({ check: ct, items });
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1111
1251
|
}
|
|
1112
1252
|
return {
|
|
1113
|
-
status:
|
|
1253
|
+
status: hasBlockerFailures ? "failed" : "passed",
|
|
1114
1254
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1115
1255
|
summary,
|
|
1116
1256
|
failures,
|
|
1117
1257
|
errors,
|
|
1118
|
-
baseline: diff ?? null
|
|
1258
|
+
baseline: diff ?? null,
|
|
1259
|
+
blockers,
|
|
1260
|
+
warnings
|
|
1119
1261
|
};
|
|
1120
1262
|
}
|
|
1121
|
-
function buildDetailedJsonReport(results, grouped, diff) {
|
|
1122
|
-
const base = buildJsonReport(results, diff);
|
|
1263
|
+
function buildDetailedJsonReport(results, grouped, diff, checks) {
|
|
1264
|
+
const base = buildJsonReport(results, diff, checks);
|
|
1123
1265
|
return { ...base, grouped };
|
|
1124
1266
|
}
|
|
1125
1267
|
function icon(ct) {
|
|
@@ -1545,6 +1687,6 @@ function resolveCategories(packages, config) {
|
|
|
1545
1687
|
return map;
|
|
1546
1688
|
}
|
|
1547
1689
|
|
|
1548
|
-
export { analyzeEnrichedTrends, analyzeTrends, appendEntry, buildBaselineReport, buildDetailedJsonReport, buildDetailedRunReport, buildHistoryTable, buildJsonReport, buildRegressionsReport, buildRunReport, buildTrendsReport, captureBaseline, collectSubmoduleInfo, compareWithBaseline, createBaselineFromResults, createHistoryEntry, detectRegressions, getPackageTimeline, getSubmoduleInfo, getWorkspacePackages, groupErrors, groupResults, loadBaseline, loadHistory, loadLastRun, resolveCategories, runLintCheck, runQA, runTestCheck, runTypeCheck, saveBaseline, saveHistory, saveLastRun };
|
|
1690
|
+
export { analyzeEnrichedTrends, analyzeTrends, appendEntry, appendTrendEntry, buildBaselineReport, buildDetailedJsonReport, buildDetailedRunReport, buildHistoryTable, buildJsonReport, buildRegressionsReport, buildRunReport, buildTrendsReport, captureBaseline, collectSubmoduleInfo, compareWithBaseline, createBaselineFromResults, createHistoryEntry, detectRegressions, getPackageTimeline, getSubmoduleInfo, getWorkspacePackages, groupErrors, groupResults, loadBaseline, loadHistory, loadLastRun, loadTrendHistory, resolveCategories, runCustomChecks, runLintCheck, runQA, runTestCheck, runTypeCheck, saveBaseline, saveHistory, saveLastRun };
|
|
1549
1691
|
//# sourceMappingURL=index.js.map
|
|
1550
1692
|
//# sourceMappingURL=index.js.map
|