Цели make смешивают два уровня: корень репозитория и генератор #65
Notifications
Due Date
No due date set.
Blocks
#6 Этап 4: трансформации и витрины, сверка A+C
ddmitry/clickstream-data-platform
Reference: ddmitry/clickstream-data-platform#65
Reference in New Issue
Block a user
Цель
Привести имена и охват целей
makeв соответствие с принятым решением:корень — про стенд, генератор — за своей дверью.
Находка
В корневом
Makefile16 целей, и 5 из них начинаются сcd generator:lint,typecheck,test,docs,inventory. Корневой Makefile смешиваетдва уровня — стенд целиком и один его компонент, — и имена об этом молчат.
Это не только про слова. Питона в репозитории 34 файла, из них 31 в
generator/, а три лежат снаружи:make lintиmake typecheckне видят ни одного из них:ruffзапускаетсяиз
generator/и дальше своегоpyproject.tomlне смотрит (провереноruff check --show-files, 7 августа 2026 года). То есть цель, названнаяобщерепозиторной, охватывает 31 файл из 34 и молча пропускает ровно те,
которые на машине никто не запускает: даги проверяет только
make config-test,и то лишь на разбор.
Почему это не косметика
Три довода, по возрастанию цены.
проблема, а нынешняя.
make testобещает категорию, а отдаёт компонент. Сегодня это почтиправда — генератор единственный носитель тестов. С этапа 3 придут
трансформации и даги, и человек, набравший
make testи увидевшийзелёное, поверит в больше, чем случилось. Молча.
(
docs/architecture/testing.md).lint,typecheckиtestиз этой осивыпадают: они названы по инструменту, а охвачены по каталогу — и два
основания расходятся.
Повод нашёлся при исполнении #42: цель
make testвыросла с 58 до 71 секунды,разговор пошёл о её имени и упёрся в это.
Веер вариантов (пройдено)
Оставлен историей: принят B, достроенный корневым
lintпо коду стенда.Почему именно так и что отклонено — в комментарии с решением.
A. Корневая цель означает репозиторий и дорастает до него.
make lintлинтует иgenerator/, иdags/, иinfra/.make testлибопереименовывается в
test-generator, либо ждёт вторых тестов.За: совпадает с осью карты проверок, дыру закрывает по-настоящему.
Против:
tyна дагах требует Airflow (from airflow.sdk import ...), а его вокружении генератора нет и быть не должно — понадобится второе окружение либо
честная оговорка «типы проверяются только у генератора».
B. Корень — про стенд, у генератора свой
Makefile.Корень делегирует:
make -C generator lint. Стендовые цели остаются в корне.За: отражает настоящее устройство — генератор отдельный переносимый пакет со
своим
pyproject, локом и README.Против: две входные двери вместо одной; дыра с дагами при этом не
закрывается сама, ей всё равно нужен адрес.
C. Имена честно сужаются.
lint-generator,typecheck-generator,test-generator; общие заводятся,когда появится второй компонент с тестами.
За: самый дешёвый и ничего не ломает; враньё исчезает сразу.
Против: лечит имя, а не охват — три файла как были непокрытыми, так и
останутся.
Границы
утверждает
ruffилиty.make smoke,check-clickhouse,check-servicesне переименовывать —их имена уже стоят на оси «кого спрашивают».
(«деление целей ценно ровно до тех пор, пока оно не превратилось в „гонять
всегда всё"»).
tyне чинить — доводв решении. Их называют в теле PR и оставляют как есть.
check-generator, суффиксlint-standи отказот
generator/Makefileрассмотрены и отклонены.Сначала прочитать
мест, которые легко пропустить.
docs/architecture/testing.md— карта целей, ось «кого спрашивают»,правило про цену как замер.
Makefileиgenerator/pyproject.toml— нынешние цели и настройкаruff.параметризовать.
Критерии приёмки
Makefile: осталось 12 целей, сгруппированы по использованию;lintзовётruffпо явным путямdagsиinfra/superset.generator/Makefileс целямиlint,typecheck,test,docs,inventory; из корня они убраны.ruff: список правил тот же, что у генератора; версиязакреплена той же, что в
generator/uv.lock.ruffвdags/починены.отсутствии корневого
typecheckс условием возврата, сказано вслух продва разных
make lint; цена корневогоlintзамерена.AGENTS.md,schema_doc.pyс пересобранным описанием выгрузки,inventory.py, тексты ошибок в тестах свежести.Проверка
make config-testиmake lint— зелёные.generator/:make lint,make typecheck,make test— зелёные.ruff check --show-filesиз корня перечисляет ровно три файла стенда, изgenerator/— только файлы генератора.lintзамерена и вписана в карту проверок.Решение
Развилка пройдена в грилинге 9 августа 2026 года. Из трёх вариантов тикета
принят B с достроенной второй половиной. Запись правлена после двух
холодных ревью — их находки учтены ниже.
Корень — про стенд, генератор — за своей дверью. Цели
lint,typecheck,test,docs,inventoryуезжают из корня в новыйgenerator/Makefileисохраняют там короткие имена: дверь уже сказала, о ком речь, суффикс ей не
нужен. Различие «стенд или генератор» несёт каталог, а не приставка в имени.
Основание не только вкусовое: генератор и так отдельная сущность по всем
внешним признакам — свой
pyproject.toml,uv.lock,Dockerfile, README, — аспека генератора утверждает это словами: «генератор — отдельная и заменяемая
сущность».
Побочно этим снимается пункт 2 находки:
make testв корне не переименовывается,а исчезает. Стенд как единую сущность спрашивают
config-test,smoke,check-clickhouseиcheck-services. Тикет решается вычитанием — из корняуходят пять целей, приходит одна.
Корневой
lintберёт под себя код стенда —dags/иinfra/superset/.Иначе «корень про стенд» оказалось бы тем же враньём в новой одежде: цель
названа по стенду и не трогает ни одного его файла. Стоит это дёшево:
ruffокружения не требует, венв в корне не нужен.
Три условия, без которых эта цель не работает как задумано:
dagsиinfra/superset.ruff, запущенный изкорня без путей, видит все 34 файла репозитория, включая генератор, и две
двери перекрываются в первый же день.
ruff.toml, потому что пакета в корне нет инастраивать
ruffбольше неоткуда. Но список правил берётся тот же, что у генератора —E, F, I, UP, B. Замер холодного ревью: этот список проходит поdags/иinfra/superset/без единой претензии к идиомам Airflow, то есть расхожденияправил сегодня нет. Разойдутся — тогда и разойдутся, с настоящим поводом.
ruffв корне закрепляется той же, что в локе генератора (0.16.1).У генератора её держит лок, в корне лока нет, а форматтер между версиями
меняет вывод — иначе корневая проверка однажды покраснеет сама, без единой
правки в репозитории. Держит закрепление сам вызов:
uvx ruff@0.16.1.Равенство двух версий не сторожит никто — обновится лок генератора, версию
правят и здесь; сказать это вслух место в карте проверок.
Проверено и рисков не несёт: иерархия конфигов
ruffработает как задумано —корневой конфиг правила генератору не подменит.
Корневого
typecheckне будет, и это записывается доводом. Проверке типовмало прочитать файл: чтобы понять
from airflow.sdk import dag, ей нуженустановленный Airflow, а он живёт в образе, не на машине. Замер 9 августа
2026 года:
apache-airflow-task-sdkтянетapache-airflow-coreи дальшеполный
apache-airflow==3.3.0— 132 пакета ради двух файлов пробников,и это вместе с
clickhouse-connectиconfluent-kafka, которые образ ставитсверх ядра. Документация Airflow обещает обратное («import only the classes you
need, without installing the full Airflow core»), но метаданные пакета этого не
подтверждают — проверено
uv pip compile.Цена решения называется честно: две настоящие находки
tyвtest_kafka.pyпостоянного сторожа не получают. Вопрос вернётся на этапе 5, когда придут
настоящие даги и цена окружения станет оправданной; условию возврата место
в карте проверок рядом с доводом, а не в этом тикете — он после слияния
закроется.
Цели корневого Makefile группируются по использованию, менти сверху вниз:
жизнь стенда (
up,down,clean,ps,logs) — проверки стенда (smoke,check-clickhouse,check-services) — проверки без стенда (config-test,lint) — наполнение миром (generate-batch,generate-live).Отклонено
check-generatorпо образцуcheck-clickhouse. Приставкаcheck-стоитна целях, спрашивающих работающий стенд; генератор так не спросить — его
образ собирается с
--no-dev, ни pytest, ни ruff, ни ty в него не едут, атесты не копируются вовсе. Второй довод жёстче и записан в карте проверок:
агрегат из трёх проверок — это «гонять всегда всё», и цена подтверждает
(0,4 с + 0,5 с + 71 с), а
check-generatorтолько для pytest врал бы вдругую сторону — линт и типы тоже проверки генератора.
tyдля дагов — 132 пакета, которыеобязаны повторять версии образа и разъезжаются с ним молча.
test: make -C generator test) —граница нарисована и тут же стёрта. Корень про генератор молчит, адрес
называет README.
ruffу корня и генератора — обосновывались бырасхождением, которого замер не показывает.
help— дублирует README и разъезжается с ним молча.pyproject.toml, лок,.venv) ради удобства менти —не отклонено, а вынесено за границы тикета; вопрос открыт. Разбор 9 августа
2026 года: конфиг
ruffищется вверх по дереву, поэтому редакторуruff.tomlи
pyproject.tomlдают одни и те же правила — формат файла тут не решаетничего (проверено по докам Astral через Context7). Настоящую пользу —
подсветку и переходы по
airflow.sdk— даёт не файл, а установленный Airflow,и повторять в корневом локе версии образа сегодня не с чего: Python образа
в репозитории нигде не записан, он приезжает с тегом
apache/airflow:3.3.0.Вдобавок одно окружение на две нужды ставит самую дешёвую проверку за
шлагбаум тяжёлого
uv sync. Обратно решение стоит три строки: конфигпереезжает из
ruff.tomlв[tool.ruff].Что делать при исполнении
Помимо очевидного (
Makefile, новыйgenerator/Makefile, корневой конфигruff, таблица целей и цены вdocs/architecture/testing.md,README.md) —места, которые легко пропустить:
generator/README.md: список команд стоит под заголовком «Из корнярепозитория» — меняется заголовок, а не только строки. В списке к тому же
не хватает
make inventory.make docsживёт в трёх местах: докстрингschema_doc.py, текстпреамбулы, который модуль печатает внутрь
docs/formats/clickstream-event.md,и сообщение об ошибке в
tests/test_schema_doc.py. Адрес меняется — документпересобирается; не пересоберёшь, покраснеет
test_schema_doc.make inventory— такая же связка, но без сторожа. Живёт вinventory.py, в сообщении об ошибкеtests/test_inventory.pyи вgenerator/README.md. Сама опись текста команды не содержит, поэтомупротухнет молча — в отличие от описания выгрузки, которое краснеет само.
README.mdупоминает переезжающие цели в трёх разных местах: списоктребований, раздел про опись мира, раздел про описание выгрузки.
docs/specs/2026-08-01-generator.mdназывает хостовыйuvдляmake test,make lint,make typecheck.AGENTS.mdссылается наmake lintкак на проверку Python — теперьэто две разные цели с разным охватом.
docs/architecture/testing.md, строка 32: «стенд нужен трём целям изсеми» — целей станет восемь (пять корневых и три за дверью генератора).
ruffв дагах (UP017в обоих файлах и два файла подпереформат) чинятся обязательно, иначе новый корневой
lintкрасный срождения. Находки эти следуют из выбранного списка правил: в наборе
ruffпо умолчанию
UP017нет, и безselectпроверка зелёная. Формат при этомсклеит три руками перенесённые строки в двух пробниках — ожидаемо, ширину
они не превышают.
tyвtest_kafka.pyне чинятся, а называются в теле PR:адрес записи едет через XCom словарём
dict[str, str | int], аTopicPartitionждётint. На исполнении безвредно — JSON возвращаетцелые, — а править аннотацию ради проверки, которую решение сознательно
выключило, значит оставить читателю конструкцию без объяснимого повода.
Карта целей остаётся картой всего репозитория, а не одного корня. Цели
генератора из таблицы не уходят — восемь строк, и у каждой видно, откуда её
звать. Довод простой: карта и есть то место, куда за проверкой идут; выкинув
оттуда цели за второй дверью, мы прячем их и от человека, и от агента — он
их просто не найдёт. Заодно в таблице остаётся цена 71 с, на которую
ссылается правило про быстрый смоук.
Отсюда мелочь, о которой таблица обязана позаботиться: строк с именем
make lintтеперь две, и различает их не имя, а дверь.Карта проверок обязана сказать вслух и то, что схема иначе скроет:
make lintесть за обеими дверями, с разным охватом. Человек в корне видитзелёное и может решить, что зелёный весь репозиторий.
Цену корневого
lintзамерить: охват новый.ADR работа не заводит — решение обратимо переименованием, а его законный дом
карта проверок. Нового термина в
CONTEXT.mdтоже нет: «дверь» здесь фигураречи, а не понятие мира.