From 06441fd79b0cdd827098b75e8067e77626f672ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Sun, 21 Jun 2026 10:02:47 +0300 Subject: [PATCH] =?UTF-8?q?fix(hooks):=20gitleaks-full-history=20=D1=87?= =?UTF-8?q?=D0=B8=D1=81=D1=82=D0=BE=20=D0=BF=D1=80=D0=BE=D0=BF=D1=83=D1=81?= =?UTF-8?q?=D0=BA=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20=D0=B1=D0=B5=D0=B7=20?= =?UTF-8?q?=D0=B1=D0=B8=D0=BD=D0=B0=D1=80=D1=8F=20(=D1=88=D0=B5=D0=BB?= =?UTF-8?q?=D0=BB-=D0=B0=D0=B3=D0=BD=D0=BE=D1=81=D1=82=D0=B8=D1=87=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20guard,=20=D0=B1=D0=B5=D0=B7=20exit-127)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Хрупкий guard `test -f ./bin/gitleaks.exe || exit 0` падал exit-127, когда команда test недоступна в шелле git-хука → шаг pre-push падал вместо чистого пропуска. Новый tools/run-if-exists.mjs (node, всегда доступен в контексте хука): бинарь отсутствует → skip exit 0; присутствует → запуск с пробросом кода. lefthook gitleaks-full-history переведён на него. Разведка: bin/gitleaks.exe и .gitleaks.toml в репо отсутствуют → реальный скан невозможен, наблюдался именно exit-127, не настоящая находка; .gitleaksignore без бинаря/конфига/отпечатков не составить. gitleaks #1 и lychee #14 — тот же паттерн, отдельный инфра-долг. Эпик роутер-реестр этап 3, item 6. Co-Authored-By: Claude Opus 4.8 (1M context) --- lefthook.yml | 2 +- tools/run-if-exists.mjs | 34 ++++++++++++++++++++++++++++++++++ tools/run-if-exists.test.mjs | 18 ++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tools/run-if-exists.mjs create mode 100644 tools/run-if-exists.test.mjs diff --git a/lefthook.yml b/lefthook.yml index 4cf6470..baab47f 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -115,7 +115,7 @@ pre-push: jobs: # 13. Полный gitleaks-скан всей истории. - name: gitleaks-full-history - run: test -f ./bin/gitleaks.exe || exit 0; ./bin/gitleaks.exe detect --source . --no-banner --config .gitleaks.toml --redact + run: node tools/run-if-exists.mjs bin/gitleaks.exe -- detect --source . --no-banner --config .gitleaks.toml --redact fail_text: | gitleaks нашёл утечки в истории коммитов. diff --git a/tools/run-if-exists.mjs b/tools/run-if-exists.mjs new file mode 100644 index 0000000..3656df0 --- /dev/null +++ b/tools/run-if-exists.mjs @@ -0,0 +1,34 @@ +#!/usr/bin/env node +/** + * run-if-exists — шелл-агностичный guard для lefthook: запустить бинарь, ТОЛЬКО если он есть. + * Заменяет хрупкий `test -f ./bin/X.exe || exit 0; ./bin/X.exe …` (падал exit-127, когда команда + * `test` недоступна в шелле хука). Бинарь отсутствует → чистый пропуск (exit 0). Присутствует → + * запуск с пробросом кода выхода. Использование: node tools/run-if-exists.mjs -- . + */ +import { existsSync } from 'node:fs'; +import { execFileSync } from 'node:child_process'; + +export function shouldRun(binaryPath, fsImpl = { existsSync }) { + return !!binaryPath && fsImpl.existsSync(binaryPath); +} + +function main() { + const argv = process.argv.slice(2); + const sep = argv.indexOf('--'); + const binaryPath = argv[0]; + const args = sep >= 0 ? argv.slice(sep + 1) : argv.slice(1); + if (!shouldRun(binaryPath)) { + console.log(`[run-if-exists] ${binaryPath || '(пусто)'} отсутствует — пропуск`); + process.exit(0); + } + try { + execFileSync(binaryPath, args, { stdio: 'inherit' }); + process.exit(0); + } catch (e) { + process.exit(typeof e.status === 'number' ? e.status : 1); + } +} + +import { fileURLToPath } from 'node:url'; +const isCli = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]; +if (isCli) main(); diff --git a/tools/run-if-exists.test.mjs b/tools/run-if-exists.test.mjs new file mode 100644 index 0000000..b1b9c8d --- /dev/null +++ b/tools/run-if-exists.test.mjs @@ -0,0 +1,18 @@ +// tools/run-if-exists.test.mjs +import { describe, it, expect } from 'vitest'; +import { shouldRun } from './run-if-exists.mjs'; + +describe('shouldRun (D1) — шелл-агностичный guard', () => { + it('несуществующий путь → false (пропуск)', () => { + expect(shouldRun('bin/nope.exe', { existsSync: () => false })).toBe(false); + }); + it('существующий путь → true (запуск)', () => { + expect(shouldRun('bin/gitleaks.exe', { existsSync: () => true })).toBe(true); + }); + it('пустой путь → false', () => { + expect(shouldRun('', { existsSync: () => true })).toBe(false); + }); + it('undefined путь → false', () => { + expect(shouldRun(undefined, { existsSync: () => true })).toBe(false); + }); +});