mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
- .githooks/pre-push + scripts/pre-push-gate.mjs --hook: gate:small runs by default for issue/* branches with an executable diff; C/D-only diffs and non-issue refs are skipped, HP_PREPUSH_GATE=0 turns it off, =1 forces it. A non-HEAD push with an executable diff is rejected (the gate checks the working tree). The gate runs with every GIT_* variable removed: git gives hooks GIT_DIR (and GIT_CONFIG_PARAMETERS with the pusher's -c), and unit tests that `git init` temporary repositories otherwise write into the real one — seen on the first live run. The manual #343 mode is unchanged. - scripts/sandbox-bootstrap.sh: idempotent worktree / deps / chromium (npm @sparticuz/chromium@152.0.0 + Playwright shims) / bundle / check steps, no owner paths or credentials. - scripts/test-chunk.mjs, npm run test:chunk -- N/M: round-robin over test/*.test.mjs sorted by code point. - Tests, seven mutants, docs/DEVELOPMENT.md «Локальный контур за 5 минут», docs/TESTING.md. package.json changed → bundle rebuilt (fingerprint input). Issue: #633 User-Visible: no
56 lines
3.0 KiB
JavaScript
56 lines
3.0 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { spawnSync } from 'node:child_process';
|
|
import { readFileSync } from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
// #633. Локальный контур: bootstrap песочницы и хук pre-push. Сам bootstrap
|
|
// проверяется прогоном на песочнице (docs/DEVELOPMENT.md); здесь — то, что
|
|
// ломается молча: синтаксис, утечка путей владельца или учётных данных в
|
|
// публичный репозиторий и бит исполнения, без которого git пропускает хук.
|
|
|
|
const REPO = fileURLToPath(new URL('..', import.meta.url));
|
|
const BOOTSTRAP = 'scripts/sandbox-bootstrap.sh';
|
|
const source = readFileSync(new URL(`../${BOOTSTRAP}`, import.meta.url), 'utf8');
|
|
|
|
test('sandbox-bootstrap не знает путей владельца и учётных данных (#633)', () => {
|
|
for (const pattern of [/\/sessions\//, /\bMatysh\b/, /gh_pat/, /\.secrets/, /ghp_/, /credential/i, /[A-Z]:\\Users/, /\/home\/[a-z]/]) {
|
|
assert.doesNotMatch(source, pattern, String(pattern));
|
|
}
|
|
});
|
|
|
|
test('sandbox-bootstrap: шаги названы, Chromium — из npm, шим узнаётся по маркеру (#633)', () => {
|
|
for (const step of ['worktree', 'deps', 'chromium', 'bundle', 'check', 'all']) {
|
|
assert.match(source, new RegExp(`^ ${step}\\)`, 'm'), step);
|
|
}
|
|
assert.match(source, /@sparticuz\/chromium@152\.0\.0/);
|
|
assert.match(source, /ms-playwright|executablePath\(\)/);
|
|
assert.match(source, /npm run --silent bundle:sync/);
|
|
assert.match(source, /SHIM_MARK=/);
|
|
assert.match(source, /set -euo pipefail/);
|
|
});
|
|
|
|
test('sandbox-bootstrap.sh синтаксически корректен (#633)', (t) => {
|
|
if (process.platform === 'win32' || spawnSync('bash', ['--version']).status !== 0) {
|
|
t.skip('bash недоступен'); return;
|
|
}
|
|
const checked = spawnSync('bash', ['-n', BOOTSTRAP], { cwd: REPO, encoding: 'utf8' });
|
|
assert.equal(checked.status, 0, checked.stderr);
|
|
const help = spawnSync('bash', [BOOTSTRAP, 'help'], { cwd: REPO, encoding: 'utf8' });
|
|
assert.equal(help.status, 0, help.stderr);
|
|
assert.match(help.stdout, /HP_WORKTREE/);
|
|
const unknown = spawnSync('bash', [BOOTSTRAP, 'nope'], { cwd: REPO, encoding: 'utf8' });
|
|
assert.notEqual(unknown.status, 0);
|
|
});
|
|
|
|
test('хук pre-push и bootstrap исполняемы в индексе git (#633)', (t) => {
|
|
const listed = spawnSync('git', ['-C', REPO, 'ls-files', '-s', '.githooks/pre-push', BOOTSTRAP], { encoding: 'utf8' });
|
|
if (listed.status !== 0 || !listed.stdout.trim()) { t.skip('нет индекса git'); return; }
|
|
const modes = Object.fromEntries(listed.stdout.trim().split('\n').map((row) => {
|
|
const [meta, path] = row.split('\t');
|
|
return [path, meta.split(' ')[0]];
|
|
}));
|
|
assert.equal(modes['.githooks/pre-push'], '100755');
|
|
assert.equal(modes[BOOTSTRAP], '100755');
|
|
});
|