From 545bb46b824a3fbf4401062f03b72531d832ebb9 Mon Sep 17 00:00:00 2001 From: Seydi Charyyev Date: Mon, 27 Jul 2026 22:00:25 +0500 Subject: [PATCH] fix(desktop): make lint and unit-test gates work on Windows (#2943) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary On a Windows checkout the desktop quality gate does not work. This fixes four defects in it. Two checks report success without examining anything, one fails on every file, and one reports violations that its own allowlist already covers. **1. `pnpm test` finds no tests and still exits 0.** The `test` script quotes the glob with single quotes. On Windows pnpm runs scripts through `cmd.exe`, which does not strip single quotes, so node receives them as part of the pattern and matches nothing. The run prints `# tests 0` and exits 0 — a silent green. Double quotes are stripped by `cmd.exe` and by POSIX shells alike, so Linux CI behaviour is unchanged. **2. Every text file is checked out as CRLF.** There is no `.gitattributes`, and `core.autocrlf=true` is the Git for Windows default. Biome formats with LF, so `biome check .` fails on 1632 of 1633 files. `desktop/src/features/messages/ui/virtuaWheelModePatch.test.mjs` fails too, because it matches `patches/*.patch` with `\n`-joined patterns. The stored blobs are already LF, so `eol=lf` adds no renormalisation churn — `git status` stays clean after the change. **3. `check:px-text` never finds its own allowlist.** `scripts/check-px-text-core.mjs` builds the key from `path.relative`, which returns `\` separators on Windows, while the allowlist in `desktop/scripts/check-px-text.mjs` is written with `/`. Nothing matches, so the check reports 5 false violations on a clean tree. **4. `check:file-sizes` examines nothing at all.** `findRule` compares against `` `${rule.root}${path.sep}` ``. The roots are multi-segment (`src/app`, `src/features`, `src-tauri/src`), so on Windows `src/app\` never matches `src\app\...`. No rule matches any file: the check walks 0 of 1097 files and exits 0. `scripts/check-pubkey-truncation-core.mjs` already normalises paths this way (`relativePath.split(path.sep).join("/")`). This applies the same idiom to the other two. ### Related issue None found — no open issue covers this. The closest open PR is #2758, which fixes a fifth Windows defect in `desktop/test-loader-hooks.mjs`; it is required before the desktop unit tests can pass here, and it does not overlap with these files. I checked the changed-file list of every open PR: none touch `.gitattributes`, `desktop/package.json`, `scripts/check-px-text-core.mjs` or `scripts/check-file-sizes-core.mjs`. ### Testing Windows 11 (10.0.26200), node 22.17.1, pnpm 11.4.0, clean checkout with the default `core.autocrlf=true`. | Command | Before | After | | --- | --- | --- | | `pnpm test` | `# tests 0`, exit 0 | 374 test files discovered, exit 1 | | `biome check .` | 1632 of 1633 files fail | 1633 checked, 0 errors | | `pnpm check:px-text` | 5 false violations | passes | | `pnpm check:file-sizes` | 0 of 1097 files examined, exit 0 | 1097 examined | `check:file-sizes` now reports `src-tauri/src/managed_agents/runtime.rs: 2220 lines (limit 2216)`. That violation is pre-existing and not introduced here — `main` currently fails on the same line in CI (Desktop Core, run 30185213010, commit c2a4ee7). Before this change Windows reported success while CI was red; now the Windows result agrees with CI. Desktop unit tests still fail on Windows until #2758 lands. With #2758 applied on top of this branch the full suite passes: 3515 tests, 0 failures. This change stops hiding those failures rather than fixing them. Signed-off-by: Seydi Charyyev Co-authored-by: Will Pfleger --- .gitattributes | 8 ++++++++ desktop/package.json | 2 +- scripts/check-file-sizes-core.mjs | 21 +++++++++++++++------ scripts/check-px-text-core.mjs | 12 ++++++++---- 4 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..f36b1615d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +# Git for Windows defaults to core.autocrlf=true, so without this every text +# file lands in the working copy with CRLF. Biome formats with LF +# (biome.json sets no lineEnding override), which fails `biome check` on +# effectively every file, and +# desktop/src/features/messages/ui/virtuaWheelModePatch.test.mjs asserts on +# patches/*.patch with `\n`-joined patterns. Normalize to LF in the working +# copy on every platform; the stored blobs are already LF. +* text=auto eol=lf diff --git a/desktop/package.json b/desktop/package.json index 8c5795e35..6726bfdca 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -14,7 +14,7 @@ "lint": "biome lint .", "check": "biome check . && pnpm check:file-sizes && pnpm check:px-text && pnpm check:pubkey-truncation", "format": "biome format --write .", - "test": "node --import ./test-loader.mjs --experimental-strip-types --test 'src/**/*.test.mjs'", + "test": "node --import ./test-loader.mjs --experimental-strip-types --test \"src/**/*.test.mjs\"", "preview": "vite preview", "tauri": "tauri", "test:e2e": "pnpm build:e2e && playwright test", diff --git a/scripts/check-file-sizes-core.mjs b/scripts/check-file-sizes-core.mjs index 196630ee7..0da799475 100644 --- a/scripts/check-file-sizes-core.mjs +++ b/scripts/check-file-sizes-core.mjs @@ -10,6 +10,13 @@ import path from "node:path"; * the two apps can never drift. */ +// `rules[].root` and the `overrides` keys are authored with `/`, but +// path.relative yields `\` on Windows — so every comparison against them has +// to happen in posix form or it silently matches nothing. +function toPosixPath(relativePath) { + return relativePath.split(path.sep).join("/"); +} + async function walkFiles(directory) { const entries = await fs.readdir(directory, { withFileTypes: true }); const files = await Promise.all( @@ -27,10 +34,8 @@ async function walkFiles(directory) { } function findRule(rules, relativePath) { - return rules.find((rule) => { - const normalizedRoot = `${rule.root}${path.sep}`; - return relativePath.startsWith(normalizedRoot); - }); + const posixPath = toPosixPath(relativePath); + return rules.find((rule) => posixPath.startsWith(`${rule.root}/`)); } function countLines(content) { @@ -82,11 +87,15 @@ export async function runFileSizeCheck({ continue; } - const limit = overrides.get(relativePath) ?? rule.maxLines; + const limit = overrides.get(toPosixPath(relativePath)) ?? rule.maxLines; const content = await fs.readFile(filePath, "utf8"); const lineCount = countLines(content); if (lineCount > limit) { - violations.push({ limit, lineCount, relativePath }); + violations.push({ + limit, + lineCount, + relativePath: toPosixPath(relativePath), + }); } } diff --git a/scripts/check-px-text-core.mjs b/scripts/check-px-text-core.mjs index b488fe262..3f98aa540 100644 --- a/scripts/check-px-text-core.mjs +++ b/scripts/check-px-text-core.mjs @@ -75,10 +75,14 @@ export async function runPxTextCheck({ const violations = []; for (const filePath of candidateFiles) { - const relativePath = path.relative(projectRoot, filePath); - const rule = rules.find((r) => - relativePath.startsWith(`${r.root}${path.sep}`), - ); + // `rules[].root` and the `overrides` keys are authored with `/`, but + // path.relative yields `\` on Windows — so every comparison against them + // has to happen in posix form or it silently matches nothing. + const relativePath = path + .relative(projectRoot, filePath) + .split(path.sep) + .join("/"); + const rule = rules.find((r) => relativePath.startsWith(`${r.root}/`)); if (!rule) { continue; }