Быстрый путь «правка только прокси» наконец включается: считаем изменённые файлы сами (#3448) #3465
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
ИРД
вторичка
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#3465
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/3448-caddy-only-detection"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #3448
Причина установлена — и она НЕ та, что в гипотезе
Гипотеза issue (пустой
github.event.beforeу мерж-коммита) опровергнута. Улика — лог джобыchangesтого самого прогона (run 10881, задача 29244, мерж84920e6c), снятый с диска Forgejo (/data/gitea/actions_log/lekss361/gendesign/3c/29244.log.zst; через API логи недоступны — всеactions/*эндпоинты на Forgejo 10.0.3 отдают 404):beforeвалиден, коммит дотянутgit fetch --depth=1, дифф верный — ровно один файл. Настоящая причина в следующих строках того же лога:Исключённый файл сам себя и «исключил»:
dorny/paths-filterсклеивает шаблоны ОДНОГО фильтра черезsome, то есть ИЛИ (src/filter.ts:patterns.some(aPredicate), параметрpredicate-quantifierпо умолчаниюsome— проверено в исходнике действия на раннере). Поэтомузначит «подходит под
**ИЛИ не Caddyfile ИЛИ не caddy/**», а**матчит всё.non_caddyбыл true всегда →caddy_only= false всегда →deploy-caddyпропускался с первого дня #2916. В логе самой джобы это видно дословно:Skipping job 'deploy-caddy' due to 'github.event_name == 'push' && needs.changes.outputs.caddy_only == 'true''— и Forgejo показал этот пропуск зелёным.Прод подтверждает (только чтение):
gendesign-caddy-1Created2026-09-11T20:40:14, в логе ровно одноserving initial configurationи ни одного reload.Что изменено
.forgejo/workflows/deploy.yml— jobchangesсчитает список файлов сам:git diff --no-renames --name-only <before> HEADпо явным границам, флагиbackend/frontend/infra/caddy_onlyвыводятся из этого списка теми же наборами путей, что были в фильтрах.dorny/paths-filterиз этого воркфлоу убран.Почему не заплатка к фильтрам:
predicate-quantifier: everyдействует на ВЕСЬ блок и сломал быbackend/frontend/infra— фикс снова висел бы на незаметном умолчании чужого действия.Два требования #3448 закрыты в этой же правке:
Флаги: backend=… frontend=… infra=… caddy_only=…. Без этого «сработало» и «просто не совпало» выглядят одинаково, а единственным свидетелем остаётся меткаCreatedконтейнера на проде.before, коммита нет в клоне) → изменённым считается весь репозиторий, деплой полный. Фолбэка наHEAD^..HEADнамеренно нет: у мерж-коммита он дал бы верный ответ, а у push'а из нескольких коммитов молча урезал бы список и включил быстрый путь там, где приехал бэкенд.Проверка, которая краснеет при регрессе
backend/tests/ops/test_3448_caddy_only_detection.py— не текстовый гейт: он ИЗВЛЕКАЕТ шаг изdeploy.ymlи ИСПОЛНЯЕТ его на временном git-репозитории с настоящим мерж-коммитом (случай #3448 дословно), сверяя значения флагов:caddy/sites/apps.caddy→caddy_only=true;caddy_only=false,backend=true;beforeпустой / нулевой / ручной запуск → полный деплой, а не пустой список;Плюс отдельная проверка по ВСЕМ воркфлоу репозитория: ни один
paths-filterне вычитает пути шаблонами!…безpredicate-quantifier: every— это класс бага, а не единственный его случай.Фальсификация (дефект возвращён руками,
git stashне использовался):На исправленном коде:
14 passed.Критерий приёмки (из #3448)
Следующий мерж с единственным файлом под
caddy/**не должен менятьdocker inspect gendesign-caddy-1 --format '{{.Created}}'(сейчас2026-09-11T20:40:14.789645422Z), а в логе Caddy должен появиться reload, а не новоеserving initial configuration. В логе джобыchangesпри этом будет видноФлаги: … caddy_only=true.Раунд 2 — по итогам deep-ревью (коммиты
9a8aaa2d,2e928c71)БЛОКЕР: оживший быстрый путь снимал гард свежести
:latestПока
caddy_onlyбыл мёртв, любой push шёл полным деплоем, и гард #2950 (jobdeploy) прикрывал прод по умолчанию. Приcaddy_only=trueджобаdeployпропускается целиком — вместе с гардом. Сценарий: push A правит бэкенд, билды ~6 мин,deployв очереди; через 2 мин push B правит толькоcaddy/; на Forgejo 10.0.3 ещё не стартовавшаяdeployпрогона A отменяется даже приcancel-in-progress: false(наблюдение 21.08.2026 10:35:13, шапкаscripts/check-latest-image-revision.sh). Дифф A..B — один caddy-файл → быстрый путь →compose pull+up -dне делает никто: прод на старом образе, голова main зелёная.Закрыто сторожем в ssh-скрипте
deploy-caddyдоgit reset --hard: если прод отстаёт отorigin/mainне только поCaddyfile/caddy/**— шаг падает и называет файлы. До reset намеренно: при отказе прод-HEAD остаётся честным для следующего прогона.Чего сторож не ловит (прямо):
deployпрогона A, упавшую послеgit reset --hard(например на миграции). Тогда прод-HEAD уже равен A, контейнеры старые, и следующий caddy-only push пройдёт быстрым путём. Это закрывается только настоящим маркером «что задеплоено» — у Trade-In он есть (/opt/gendesign/.tradein-deployed-sha), у ПТИЦЫ нет; заводить его в этом PR не стал.Остальное
git diff --name-only/git ls-filesприcore.quotePath=true(умолчание) отдают не-ASCII пути закавыченными —backend/<кириллица>.pyдавалbackend=false. Старый paths-filter брал--name-status -z, где квотирования нет: единственное место, где переход на свой diff менял поведение. Добавлен-c core.quotePath=falseв обе команды и в сторож.deploy-caddyделаетgit reset --hardв/opt/gendesign— теперь берёт тот жеflock /var/lock/gendesign-docker-deploy.lock, что и полный деплой (#2950).backendрасширен на три файла (правка списка идёт ТОЛЬКО этой веткой, чтобы параллельные PR не подрались за один список):.forgejo/workflows/ci-tradein.yml— там тоже живёт paths-filter, без этой строки правка с!-шаблоном не запустила быbackend-tests, то есть гейт этого PR не побежал бы на той правке, от которой стережёт;.forgejo/workflows/deploy-metrics.ymlиdocker-compose.metrics.yml— их читает гейтbackend/tests/ops/test_3467_prometheus_reload.pyиз параллельного PR #3475; без них правка, трогающая толькоdeploy-metrics.yml(скажем, дописывающая|| trueк шагу перезагрузки Prometheus), даётbackend=false,backend-testsпропускается и регрессия уезжает в main зелёной.git show, но не дляgit diff HEAD^ HEAD). Переписан; подмену базы стережёт отдельная фикстура.Мутационный прогон (8 мутаций, все красные)
Чистое состояние —
18 passed. Сторож не текстовая проверка: его кусок извлекается из ssh-скрипта и исполняется на временном репозитории, где прод-дерево отстаёт отorigin/main.Проследить первый caddy-only push после мержа
Это будет первое в истории срабатывание
deploy-caddy. По логу джобыchangesтеперь видно решение (база, список файлов, строкаФлаги: … caddy_only=true), то есть «сработало» отличимо от «просто не совпало» ещё до того, как смотреть на прод.🤖 Generated with Claude Code