mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-02 21:01:21 +00:00
Windows portability (the three red tests on the owner's machine at green CI):
- scripts/spawn-portable.mjs: isMainModule via pathToFileURL (the
`file://${argv[1]}` form gives file:///C:/C:/... and the CLI stays silent);
portableCommand — a shell only for npm/npx/.cmd, node and git run directly
(spawn via shell dropped the quotes of `node -e "…"`). Applied to
classify-changes, mutation-gate-report, review-doc-guard, check-inputs,
merge-candidate, gate-small, rebase-on-dev.
- the rebase-on-dev test pins core.autocrlf=false / core.eol=lf through
GIT_CONFIG_* for its temp repository instead of touching the user's git
config; test/windows-portability.test.mjs forbids both anti-patterns.
Pins: scripts/toolchain-pins.mjs reads Node/Python from validate.yml, the HA
stack from tests_backend/requirements.txt, Playwright/Chromium from the
lockfile — no second dictionary; `npm run toolchain:check` compares the
machine; .nvmrc/.python-version are derived and tested equal;
scripts/wsl-setup.sh provisions WSL/Linux with those pins.
scripts/task-packet.mjs: one derived view of an issue (status, rights, owner
decisions, branch vs dev, Validate on the tip, previous verdict with recorded
tree, AC → evidence, unwitnessed). scripts/wait-verdict.mjs: polls labels,
pipeline comments and optionally Validate, prints only on state change, exit
0/3/4, writes nothing.
gate:small --smokes: after build the browser phase runs bundle-sync and then
the directly matched and registered smokes, two at a time; broad matches stay
with the reviewer. package.json changed, so the bundle is rebuilt here.
Issue: #496
User-Visible: no
54 lines
3.4 KiB
JavaScript
54 lines
3.4 KiB
JavaScript
import test from 'node:test';
|
||
import assert from 'node:assert/strict';
|
||
import { readFileSync, readdirSync } from 'node:fs';
|
||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||
import { isMainModule, portableCommand } from '../scripts/spawn-portable.mjs';
|
||
|
||
// #496: на машине владельца (Windows) три инфраструктурных теста падали при
|
||
// зелёном Linux CI. Причины — не логика, а переносимость: разбор argv[1] через
|
||
// `file://`, запуск команд через оболочку, CRLF из глобального git config.
|
||
// Здесь закреплены правила, чтобы они не вернулись с очередным скриптом.
|
||
|
||
const SCRIPTS = fileURLToPath(new URL('../scripts/', import.meta.url));
|
||
const scriptSources = () => readdirSync(SCRIPTS)
|
||
.filter((name) => name.endsWith('.mjs'))
|
||
.map((name) => ({ name, text: readFileSync(new URL(name, `file://${SCRIPTS}`), 'utf8') }));
|
||
|
||
test('скрипты не собирают file-URL конкатенацией с argv (#496)', () => {
|
||
const offenders = scriptSources()
|
||
.filter(({ text }) => /new URL\(`file:\/\/\$\{process\.argv/.test(text))
|
||
.map(({ name }) => name);
|
||
assert.deepEqual(offenders, [], 'на Windows это даёт file:///C:/C:/… и CLI молчит; использовать isMainModule');
|
||
});
|
||
|
||
test('скрипты не включают оболочку по платформе — только portableCommand (#496)', () => {
|
||
const offenders = scriptSources()
|
||
.filter(({ name, text }) => name !== 'spawn-portable.mjs' && /shell:\s*process\.platform\s*===\s*'win32'/.test(text))
|
||
.map(({ name }) => name);
|
||
assert.deepEqual(offenders, [], 'оболочка нужна только npm.cmd; node/git через shell теряют кавычки');
|
||
});
|
||
|
||
test('portableCommand: оболочка только для npm/npx/.cmd на Windows (#496)', () => {
|
||
assert.deepEqual(portableCommand('npm', 'win32'), { cmd: 'npm.cmd', shell: true });
|
||
assert.deepEqual(portableCommand('npx', 'win32'), { cmd: 'npx.cmd', shell: true });
|
||
assert.deepEqual(portableCommand('tool.CMD', 'win32'), { cmd: 'tool.CMD', shell: true });
|
||
assert.deepEqual(portableCommand(process.execPath, 'win32'), { cmd: process.execPath, shell: false });
|
||
assert.deepEqual(portableCommand('git', 'win32'), { cmd: 'git', shell: false });
|
||
assert.deepEqual(portableCommand('npm', 'linux'), { cmd: 'npm', shell: false });
|
||
assert.deepEqual(portableCommand('npm', 'darwin'), { cmd: 'npm', shell: false });
|
||
});
|
||
|
||
test('isMainModule сравнивает через pathToFileURL и терпит мусор в argv (#496)', () => {
|
||
const here = fileURLToPath(import.meta.url);
|
||
assert.equal(isMainModule(import.meta.url, here), true);
|
||
assert.equal(isMainModule(pathToFileURL(here).href, here), true);
|
||
assert.equal(isMainModule(import.meta.url, `${here}.other`), false);
|
||
assert.equal(isMainModule(import.meta.url, null), false);
|
||
assert.equal(isMainModule(import.meta.url, ''), false);
|
||
});
|
||
|
||
test('тесты с временным git-репозиторием изолируют переводы строк от глобального конфига (#496)', () => {
|
||
const text = readFileSync(new URL('./rebase-on-dev.test.mjs', import.meta.url), 'utf8');
|
||
assert.match(text, /GIT_CONFIG_KEY_0: 'core\.autocrlf', GIT_CONFIG_VALUE_0: 'false'/);
|
||
});
|