Skip to content
This repository was archived by the owner on Jun 20, 2026. It is now read-only.

Repository files navigation

⚠️ ВНИМАНИЕ ⚠️ Репозиторий архивирован. Утилита доступна в составе пакета: fs-tools.

fs-checker

CLI-утилита для проверки структуры каталогов по правилам из файла .fs-check. Синтаксис правил близок к .gitignore, но смысл инвертирован: строки описывают папки и файлы, которые обязаны существовать. Утилита рекурсивно сверяет дерево с правилами и печатает список отсутствующих путей.

Утилита не создаёт, не переименовывает и не удаляет папки и файлы — структуру проверяемого дерева она не меняет. Единственная запись на диск — журнал нарушений .fs-log в проверяемом каталоге (универсальный формат журнала), и только при обнаружении отсутствующих путей. При нарушениях утилита также может отправить веб-хук-уведомление (см. «Запуск по таймеру и уведомления»).

Возможности

  • Интерактивный выбор корня проверки: нативный проводник Windows (на Windows и в WSL), стандартный диалог macOS либо ввод пути в терминале (на обычном Linux), с откатом в терминал при любом сбое диалога.
  • Чтение правил из .fs-check в выбранном каталоге (кодировка utf-8-sig). Нет файла — понятная ошибка и ненулевой код возврата.
  • Положительные правила с литералами, * (один уровень) и ** (рекурсивно), разворачиваемые через pathlib.Path.glob по уже существующим папкам.
  • Мандат может быть папкой или файлом: без завершающего / проверка exists() (…/Data/project.md), с завершающим / — строго is_dir() (…/Addl/).
  • Негативы ! исключают служебные каталоги из подстановок */** (узкий момент _Archive), не влияя на литералы.
  • Скрытые каталоги (имя на .) не обходятся; симлинки при ** не разыменовываются.
  • Отсортированный дедуплицированный список отсутствующих путей + итоговая сводка; ненулевой код возврата при наличии нарушений.
  • Запуск с аргументом-каталогом (без диалога) — для cron/планировщика на всех ОС.
  • При нарушениях — журнал .fs-log в проверяемом каталоге и fire-and-forget веб-хук-уведомление (адрес и Bearer-токен — в .env).

Структура проекта

fs-checker/
├── check_fs.py                 # точка входа
├── check.sh                    # обёртка для Linux/macOS (терминал)
├── check.command               # обёртка для macOS (двойной клик в Finder)
├── check.bat                   # обёртка для Windows
├── checker/
│   ├── __init__.py             # публичное API
│   ├── cli.py                  # разбор аргументов и сценарий запуска
│   ├── picker.py               # выбор каталога (диалоги Windows/WSL/macOS, ввод в терминале)
│   ├── pick_folder.ps1         # нативный диалог выбора папки Windows (IFileOpenDialog)
│   ├── rule.py                 # разбор .fs-check (правила + PathSpec негативов)
│   ├── pathspec_compat.py      # совместимость версий pathspec (выбор фабрики паттернов)
│   ├── engine.py               # разворачивание правил и сбор отсутствующих путей
│   ├── report.py               # формат вывода и сводки
│   ├── log.py                  # журнал .fs-log (универсальный формат)
│   └── notify.py               # веб-хук-уведомление о нарушениях (.env, fire-and-forget)
├── tests/                      # тесты (pytest)
├── examples/                   # песочница-фикстура для ручного прогона
└── requirements.txt

Требования

  • Python 3.10+ (используется синтаксис X | None).
  • Рантайм-зависимости (requirements.txt): pathspec — gitignore-семантика негативных правил (!...); requests — отправка веб-хука; python-dotenv — чтение .env.

Стандартной библиотеки достаточно для выбора папки: на Windows и в WSL вызывается powershell.exe (нативный диалог IFileOpenDialog), на macOS — osascript (AppleScript choose folder), на обычном Linux — ввод пути в терминале. Дополнительных GUI-пакетов не требуется.

Установка

Через обёртки установка не нужна: при первом запуске они сами создают .venv и ставят зависимости (требуется интернет один раз). Этот раздел — для прямого запуска python3 check_fs.py или разработки:

cd fs-checker
python3 -m venv .venv
source .venv/bin/activate    # Windows: .venv\Scripts\activate
pip install -r requirements.txt

Использование

Проще всего — через обёртку для своей ОС. При первом запуске она автоматически подготовит окружение (.venv + зависимости), затем запустит утилиту:

./check.bat    # Windows
./check.sh     # Linux/macOS (терминал)

На macOS для запуска двойным кликом в Finder используйте check.command (один раз сделайте его исполняемым: chmod +x check.command).

Альтернатива — прямой запуск (требует заранее подготовленного окружения):

python3 check_fs.py

Без аргумента каталог выбирается интерактивно (по умолчанию предлагается рабочий каталог). На Windows/WSL открывается нативный диалог IFileOpenDialog (в WSL путь конвертируется через wslpath, переменные пробрасываются через WSLENV); на macOS — osascript; на обычном Linux — ввод пути в терминале. При любом сбое диалога — откат на ввод в терминале.

Каталог можно передать аргументом — тогда диалог не открывается (режим запуска по таймеру, см. ниже). Обёртки пробрасывают аргумент в утилиту:

./check.sh /path/to/dir       # Linux/macOS
./check.bat C:\path\to\dir    # Windows
python3 check_fs.py /path/to/dir

Быстрый запуск через алиас

Чтобы не указывать полный путь к обёртке каждый раз, добавьте алиас в конфиг своей оболочки.

bash/zsh — в ~/.bashrc или ~/.zshrc:

alias fs-check="$HOME/Projects/Work/fs-checker/check.sh"

Примените изменения (source ~/.bashrc) — и запускайте командой fs-check.

PowerShell — в файле профиля ($PROFILE). Используется функция-обёртка, чтобы корректно пробрасывать аргументы:

function fs-check { & "C:\path\to\fs-checker\check.bat" @args }

Перечитайте профиль (. $PROFILE) — и запускайте командой fs-check.

Запуск по таймеру и уведомления

Для периодической проверки (cron на Linux/macOS, «Планировщик заданий» на Windows, cron внутри WSL) запускайте утилиту с аргументом-каталогом — так диалог не открывается, а проверяется именно заданная папка:

# crontab -e: ежедневно в 9:00 проверять хранилище
0 9 * * * /path/to/fs-checker/check.sh /mnt/disk/Home

При обнаружении отсутствующих путей утилита:

  1. дописывает журнал .fs-log в проверяемом каталоге (метка времени + список путей). Формат журнала универсален: если над одним каталогом запускается несколько утилит, они дополняют один и тот же файл как единая система;
  2. шлёт веб-хук на адрес из .env — чтобы узнать о проблеме без просмотра логов. Текст уведомления только сигнализирует о нарушениях; сами пути — в .fs-log.

Настройка веб-хука (.env)

Адрес и токен хранятся в .env рядом со скриптом (в корне fs-checker, не в проверяемом каталоге) — путь фиксирован, поэтому таймер может стартовать из любого рабочего каталога. Шаблон — в .env.example; скопируйте его в .env (сам .env в репозиторий не попадает, .gitignore):

FSCHK_WEBHOOK_URL=https://example.com/hook
FSCHK_WEBHOOK_TOK=секретный-токен

Без FSCHK_WEBHOOK_URL уведомления отключены (интерактивный режим не меняется). Значения можно задать и через переменные окружения процесса — .env приоритетнее.

Запрос — POST с телом {"text": "<сообщение>"} и заголовком Authorization: Bearer <FSCHK_WEBHOOK_TOK> (если токен задан). Отправка работает по принципам UDP — «выстрелил и забыл»: минимальный таймаут, ответ не проверяется, любые сетевые ошибки гасятся и на код возврата проверки не влияют.

Формат .fs-check

Файл лежит в корне проверки (выбранном каталоге) и читается оттуда — как .gitignore в корне репозитория. Пути в нём якорятся к этому корню.

  • Кодировка utf-8-sig (BOM проглатывается). Разделитель сегментов — POSIX /.
  • Комментарий — только ведущий #; срединный/инлайновый # литерален (C#Notes валиден). Пустые строки игнорируются. Конечные пробелы обрезаются, значимый можно экранировать \ .
  • Якорение ведущим / — относительно корня правил, как в .gitignore. Правила без ведущего / тоже считаются якорными.
  • Сегменты: литерал (Projects), * (ровно один уровень), ** (ноль и более уровней), глоб внутри сегмента (*.ru, _Archive*).
  • Мандат — последний сегмент. Завершающий / → строго каталог (is_dir()); без него → существование (exists(), файл ИЛИ папка). Мандат сверяется литерально (без glob в последнем сегменте).
  • Негатив ! — исключение из подстановок */** (см. ниже).

Компактный пример:

# Фиксированные каталоги: обязаны существовать сами по себе
/Activities
/Activities/Web/Projects

# В каждом занятии — Projects и Resources
/Activities/*/Projects
/Activities/*/Resources

# Категории Web — строго каталоги (завершающий /)
/Activities/Web/Projects/Addl/

# Back/Data в проектах; обязательный файл Data/project.md (мандат-файл)
/Activities/Web/Projects/Work/*/*/Back
/Activities/Web/Projects/Work/*/*/Data/project.md

# Архивные проекты на любом уровне
/Activities/Web/Projects/**/_Archive/*/Back

# Служебный _Archive не попадает под обобщённые * и **
!_Archive

Семантика проверки

Каждое положительное правило делится на две части:

  • префикс (все сегменты, кроме последнего) — множество уже существующих каталогов-якорей. */** перечисляют существующие папки; отсутствующий литерал префикса — это не нарушение, просто нет якоря;
  • последний сегментмандат: для каждого найденного якоря он обязан существовать, иначе нарушение.

Отсюда два важных следствия:

  • Обязательность фиксированных каталогов. Если фиксированный (литеральный) каталог в начале цепочки отсутствует целиком, правила, чей префикс через него проходит, молча не дают нарушений (нет якоря). Поэтому каждый обязательный фиксированный каталог описывается отдельным правилом-литералом, где он — последний сегмент (/Activities, /Activities/Web, /Activities/Web/Projects). Правило из одного сегмента берёт якорем сам корень проверки.
  • Узкий момент _Archive. Обобщённое Addl/*/Back своим * перечислило бы и служебную _Archive, ошибочно требуя Addl/_Archive/Back. Негатив !_Archive исключает каталог с этим именем из подстановок */** (матч по имени одной компоненты, а не по полному пути), не влияя на литералы. Прунинг применяется к каждой */**-выбранной компоненте якоря, а не только к листу — поэтому _Archive отсекается и на промежуточных *-позициях (Work/*/_Archive/Data не требует project.md). А правило с литеральным _Archive (**/_Archive/*/Back) продолжает проверять архивные проекты.

Формат вывода и коды возврата

В stdout печатается заголовок с каталогом, отсортированный список отсутствующих путей (термин «пути», т.к. это и папки, и файлы) и сводка:

Каталог: /mnt/disk/Home
Отсутствуют пути (3):
  Activities/3D/Resources
  Activities/Web/Projects/Self/personal.example/Back
  Activities/Web/Projects/Work/Fabrikam/widgets.example/Data/project.md
Проверено правил: 17. Найдено каталогов-кандидатов: 57. Отсутствует: 3.

Если нарушений нет:

Каталог: /mnt/disk/Home
Все требуемые пути на месте.
Проверено правил: 17. Найдено каталогов-кандидатов: 60. Отсутствует: 0.

Коды возврата:

Код Условие
0 нарушений нет
1 ошибка запуска: каталог не выбран/не найден/не каталог, нет .fs-check
2 проверка выполнена, найдены отсутствующие пути

Публичное API

Импортируйте из пакета checker, а не из подмодулей напрямую:

from pathlib import Path

from checker import FsChecker, format_report, load_fs_rule

root = Path("/path/to/dir")
fs_rule = load_fs_rule(root)          # положительные правила + PathSpec негативов
result = FsChecker(fs_rule).check(root)
print(format_report(root, result))    # result.missing — отсортированный список

Экспортируется: main, FsChecker, CheckResult, format_report, load_fs_rule, FsRule, Rule, Negation, FsRuleError, FS_LOG, write_fs_log, load_webhook_config, send_webhook.

Песочница

В examples/ — запускаемое дерево-фикстура с .fs-check и намеренно отсутствующими путями (включая файл project.md). Прогон утилиты на нём показывает ровно 7 нарушений; разбор — в examples/README.md.

Разработка

pytest                               # тесты
ruff check checker tests             # линтер
mypy --strict checker check_fs.py    # проверка типов

Код проходит ruff и mypy --strict без замечаний; покрытие парсинга, движка, CLI и picker — в tests/.

About

CLI-утилита для проверки структуры каталогов по правилам из файла .fs-rule. Синтаксис правил близок к .gitignore, но смысл инвертирован: строки описывают папки и файлы, которые обязаны существовать. Утилита рекурсивно сверяет дерево с правилами и печатает список отсутствующих путей.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages