ci: Build tsc deps and surface vitest stderr in grind workflow (#31543)

Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
n8n-cat-bot[bot] 2026-06-02 10:05:57 +00:00 committed by GitHub
parent 790a8da6af
commit 0cb26bdea9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 44 additions and 5 deletions

View File

@ -112,12 +112,14 @@ for (const file of files) {
}
}
const stderr = res.stderr ?? '';
if (!parsed) {
rows.push({ file, status: 'no-result' });
rows.push({ file, status: 'no-result', stderr });
continue;
}
rows.push({ file, status: 'ran', passed: parsed.passed, total: parsed.total });
rows.push({ file, status: 'ran', passed: parsed.passed, total: parsed.total, stderr });
}
// --- Render markdown ---
@ -131,6 +133,28 @@ const renderRow = ({ file, status, passed, total }) => {
return `| \`${file}\` | ${fraction} ⚠️ flaky |`;
};
const STDERR_EXCERPT_LINES = 20;
const renderDiagnostic = ({ file, stderr }) => {
const lines = stderr.split('\n');
const truncated = lines.length > STDERR_EXCERPT_LINES;
const excerpt = lines.slice(0, STDERR_EXCERPT_LINES).join('\n');
const trailer = truncated ? `\n... (${lines.length - STDERR_EXCERPT_LINES} more lines)` : '';
return [
`<details><summary><code>${file}</code> — first failure stderr</summary>`,
'',
'```',
excerpt + trailer,
'```',
'',
'</details>',
].join('\n');
};
const diagnostics = rows
.filter((r) => r.stderr && r.stderr.trim() && (r.status === 'no-result' || (r.status === 'ran' && r.passed < r.total)))
.map(renderDiagnostic);
const body = [
'<!-- grind-results -->',
'## Grind results — pre-merge flake detection (N=' + n + ')',
@ -139,6 +163,7 @@ const body = [
'|---|---|',
...rows.map(renderRow),
'',
...(diagnostics.length ? ['### First-failure diagnostics', '', ...diagnostics, ''] : []),
'_Spawn-per-iteration mode. Catches post-teardown async flakes that `vitest --repeat` misses. See [DEVP-198](https://linear.app/n8n/issue/DEVP-198) for design notes._',
'',
].join('\n');

View File

@ -33,7 +33,11 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-nodejs
with:
build-command: ''
# Build editor-ui's dependencies (e.g. @n8n/api-types) but not
# editor-ui itself — Vitest transforms editor-ui sources on the fly,
# while tsc-built deps must exist on disk for import resolution.
# Turbo cache makes this near-instant on subsequent runs.
build-command: 'pnpm --filter=n8n-editor-ui^... build'
- name: Install .github/scripts dependencies
run: pnpm install --frozen-lockfile --dir ./.github/scripts --ignore-workspace

View File

@ -98,12 +98,22 @@ const runnerArgs =
: ['jest', fileRelToPkg, '--colors=false'];
let passed = 0;
let firstFailureLogged = false;
for (let i = 0; i < n; i++) {
const res = spawnSync('pnpm', runnerArgs, {
cwd: pkgRoot,
stdio: values.json ? ['ignore', 'ignore', 'ignore'] : ['ignore', 'inherit', 'inherit'],
stdio: values.json ? ['ignore', 'ignore', 'pipe'] : ['ignore', 'inherit', 'inherit'],
encoding: 'utf8',
});
if (res.status === 0) passed++;
if (res.status === 0) {
passed++;
} else if (values.json && !firstFailureLogged) {
// Without this, JSON-mode invocations (used in CI) swallow every
// vitest error message and leave authors with no diagnostic trail.
firstFailureLogged = true;
process.stderr.write(`\n[grind] first failing iteration for ${fileRelToPkg}:\n`);
if (res.stderr) process.stderr.write(res.stderr);
}
if (!values.json) process.stdout.write(res.status === 0 ? '.' : 'F');
}