Вердикт no-broken-repo-paths зависит от неверсионируемых настроек git; асимметрия пробелов в todo-needs-issue #27

Closed
opened 2026-08-27 13:37:01 +07:00 by claude-secretary · 1 comment

Два смежных пункта из тела #11, которые не входили в основную часть задачи (там был режим комментариев для разметки и стилей — сделан в #26). Оба про другие хуки.

1. no-broken-repo-paths: вердикт зависит от того, чего нет в репозитории

Хук резолвит пути через git check-ignore, а тот учитывает .git/info/exclude и глобальный core.excludesFile. Значит добавление пути туда переводит хук с FAIL на pass не оставляя следа в MR: у разработчика зелено, в CI красно (или наоборот), и разница не видна ни в диффе, ни в конфиге.

Направление отказа безопасное — CI строже локальной машины, — но свойство неочевидное. Два варианта:

  • записать в README рядом с абзацем про .gitignore (дёшево, но проблему не убирает);
  • прибить окружением на время вызова: GIT_CONFIG_GLOBAL=/dev/null и явный отказ от .git/info/exclude, чтобы вердикт зависел только от версионируемого состояния.

Второй честнее: гейт, который у двух людей отвечает по-разному на один и тот же коммит, — это не гейт.

2. todo-needs-issue: TODO (#456) принимается, TODO( #456 ) — нет

Regex TODO\b(?!\s*\(#\d+(?:\s*,\s*#\d+)*\)) допускает пробелы перед скобкой и вокруг запятой, но не сразу после открывающей скобки и не перед закрывающей. Асимметрия нигде не описана: разработчик, поставивший пробел внутри скобок, получает FAIL на формате, который выглядит легальным.

Решить, что именно правильно, и привести regex с документацией в соответствие: либо разрешить пробелы внутри скобок, либо запретить их и перед скобкой тоже — чтобы правило было «формат ровно такой», а не «такой, но местами можно».

Приёмка

  • Вердикт no-broken-repo-paths не зависит от .git/info/exclude и core.excludesFile — либо это явно записано в README как известное свойство
  • Пробелы в TODO(...) трактуются одинаково со всех сторон; README и описание хука говорят то же, что делает regex
  • Кейсы в smoke на обе формы
Два смежных пункта из тела [#11](https://git.homedevlab.ru/senokosov/pre-commit-hooks/issues/11), которые не входили в основную часть задачи (там был режим комментариев для разметки и стилей — сделан в [#26](https://git.homedevlab.ru/senokosov/pre-commit-hooks/pulls/26)). Оба про другие хуки. ## 1. `no-broken-repo-paths`: вердикт зависит от того, чего нет в репозитории Хук резолвит пути через `git check-ignore`, а тот учитывает `.git/info/exclude` и глобальный `core.excludesFile`. Значит добавление пути туда переводит хук с FAIL на pass **не оставляя следа в MR**: у разработчика зелено, в CI красно (или наоборот), и разница не видна ни в диффе, ни в конфиге. Направление отказа безопасное — CI строже локальной машины, — но свойство неочевидное. Два варианта: - записать в README рядом с абзацем про `.gitignore` (дёшево, но проблему не убирает); - прибить окружением на время вызова: `GIT_CONFIG_GLOBAL=/dev/null` и явный отказ от `.git/info/exclude`, чтобы вердикт зависел только от версионируемого состояния. Второй честнее: гейт, который у двух людей отвечает по-разному на один и тот же коммит, — это не гейт. ## 2. `todo-needs-issue`: `TODO (#456)` принимается, `TODO( #456 )` — нет Regex `TODO\b(?!\s*\(#\d+(?:\s*,\s*#\d+)*\))` допускает пробелы перед скобкой и вокруг запятой, но не сразу после открывающей скобки и не перед закрывающей. Асимметрия нигде не описана: разработчик, поставивший пробел внутри скобок, получает FAIL на формате, который выглядит легальным. Решить, что именно правильно, и привести regex с документацией в соответствие: либо разрешить пробелы внутри скобок, либо запретить их и перед скобкой тоже — чтобы правило было «формат ровно такой», а не «такой, но местами можно». ## Приёмка - [ ] Вердикт `no-broken-repo-paths` не зависит от `.git/info/exclude` и `core.excludesFile` — либо это явно записано в README как известное свойство - [ ] Пробелы в `TODO(...)` трактуются одинаково со всех сторон; README и описание хука говорят то же, что делает regex - [ ] Кейсы в smoke на обе формы
Author
Owner

Закрываю: сделано в #30, выпущено тегом v0.23.0.

1. Вердикт no-broken-repo-paths больше не зависит от личных настроек git. Ручка гасится через git -c core.excludesFile=/dev/null — это перебивает все уровни конфига, включая локальный .git/config (о нём в задаче речи не было, но он ровно так же неверсионируем). Заодно git diff зовётся с --no-ext-diff: настроенный diff.external подменял вывод целиком, разбор hunk'ов не находил изменённых строк, и pre-push-сужение скоупа отдавало зелёный вхолостую.

Первая редакция сносила глобальный и системный конфиг целиком — ревью показало, что это забрало бы с собой safe.directory, и в контейнере с UID-mismatch любой вызов git отвечал бы 128; хук трактует ненулевой код как «путь не игнорируется», то есть я обменял бы одну недетерминированность на волну ложных FAIL'ов. Узкий -c этого не делает.

.git/info/exclude остаётся — git читает его всегда, отключить нельзя. Записано в README как известное свойство: файл локален для клона и, в отличие от настроек пользователя, никуда не приезжает.

2. Пробелы в TODO(...) трактуются одинаково со всех сторон. TODO (#456), TODO( #456 ), TODO(#12, #34) равноправны. Граница названа и закреплена кейсом: пробел внутри ссылки (TODO( # 456 )) её не образует — # 456 не ссылка. Паттерн синхронизирован в обоих местах, README и описания хуков в манифесте говорят одно и то же; заодно из примера конфига убран формат TODO(arg, #42, ...), который regex отвергает с v0.11.0 — тот самый случай «документация обещает то, что правило не принимает», с которого задача и начиналась.

Кейсы: оба уровня конфига, внешний diff, обе формы пробелов. Мутации, каждая роняет smoke: убрать -c, убрать core.excludesFile, убрать --no-ext-diff, вернуть запрет пробелов, разрешить пробел внутри ссылки.

Закрываю: сделано в [#30](https://git.homedevlab.ru/senokosov/pre-commit-hooks/pulls/30), выпущено тегом `v0.23.0`. **1. Вердикт `no-broken-repo-paths` больше не зависит от личных настроек git.** Ручка гасится через `git -c core.excludesFile=/dev/null` — это перебивает все уровни конфига, включая локальный `.git/config` (о нём в задаче речи не было, но он ровно так же неверсионируем). Заодно `git diff` зовётся с `--no-ext-diff`: настроенный `diff.external` подменял вывод целиком, разбор hunk'ов не находил изменённых строк, и pre-push-сужение скоупа отдавало зелёный вхолостую. Первая редакция сносила глобальный и системный конфиг целиком — ревью показало, что это забрало бы с собой `safe.directory`, и в контейнере с UID-mismatch любой вызов git отвечал бы 128; хук трактует ненулевой код как «путь не игнорируется», то есть я обменял бы одну недетерминированность на волну ложных FAIL'ов. Узкий `-c` этого не делает. `.git/info/exclude` остаётся — git читает его всегда, отключить нельзя. Записано в README как известное свойство: файл локален для клона и, в отличие от настроек пользователя, никуда не приезжает. **2. Пробелы в `TODO(...)` трактуются одинаково со всех сторон.** `TODO (#456)`, `TODO( #456 )`, `TODO(#12, #34)` равноправны. Граница названа и закреплена кейсом: пробел **внутри** ссылки (`TODO( # 456 )`) её не образует — `# 456` не ссылка. Паттерн синхронизирован в обоих местах, README и описания хуков в манифесте говорят одно и то же; заодно из примера конфига убран формат `TODO(arg, #42, ...)`, который regex отвергает с `v0.11.0` — тот самый случай «документация обещает то, что правило не принимает», с которого задача и начиналась. Кейсы: оба уровня конфига, внешний diff, обе формы пробелов. Мутации, каждая роняет smoke: убрать `-c`, убрать `core.excludesFile`, убрать `--no-ext-diff`, вернуть запрет пробелов, разрешить пробел внутри ссылки.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
senokosov/pre-commit-hooks#27
No description provided.