Commit graph

36 commits

Author SHA1 Message Date
9a8aaa2d2d deploy-caddy: сторож отставания прода, общий лок и квотирование путей (#3448)
Оживший быстрый путь снимает гард свежести :latest. Пока caddy_only был мёртв,
любой push шёл полным деплоем, и гард #2950 прикрывал прод по умолчанию. Теперь
при caddy_only=true джоба `deploy` пропускается целиком — вместе с гардом.

Сценарий отказа. Push A правит бэкенд, билды ~6 мин, `deploy` в очереди. Через
2 мин push B правит только caddy/. На Forgejo 10.0.3 ещё не стартовавшая
`deploy` предыдущего прогона отменяется ДАЖЕ при cancel-in-progress: false
(наблюдение 21.08.2026 10:35:13, шапка scripts/check-latest-image-revision.sh;
workflow-level concurrency там не исполняется, см. deploy.yml). Дифф A..B — один
caddy-файл, быстрый путь включается, `compose pull` + `up -d` не делает никто:
прод крутит старый образ, голова main зелёная, сигнала нет.

Сторож в ssh-скрипте deploy-caddy: если прод отстаёт от origin/main не только
по Caddyfile/caddy/**, быстрый путь запрещён, шаг падает и называет файлы.
Стоит ДО `git reset --hard` намеренно — при отказе прод-HEAD остаётся честным
для следующего прогона. У Trade-In для того же заведён отдельный маркер
(/opt/gendesign/.tradein-deployed-sha, deploy-tradein.yml), у ПТИЦЫ маркера нет,
и `git reset` делает прод-HEAD его эквивалентом.

ЧЕГО СТОРОЖ НЕ ЛОВИТ: `deploy`, упавшую ПОСЛЕ `git reset --hard` (например на
миграции). Тогда прод-HEAD уже равен новому коммиту, а контейнеры старые —
это остаётся за настоящим маркером «что задеплоено».

Тот же лок, что и у полного деплоя. deploy-caddy делает `git reset --hard` в
/opt/gendesign, то есть правит прод-дерево — ровно то, что job `deploy`
сериализует через flock /var/lock/gendesign-docker-deploy.lock. Пока путь был
мёртв, сталкиваться было нечему; теперь это первая джоба, трогающая прод-дерево
в обход сериализации.

Квотирование путей. `git diff --name-only` и `git ls-files` при core.quotePath
(умолчание true) отдают не-ASCII пути закавыченными с \NNN-экранированием —
`^backend/` такую строку не матчит, и файл backend/<кириллица>.py дал бы
backend=false. Старый paths-filter брал `--name-status -z`, где квотирования
нет: это единственное место, где переход на свой diff менял поведение. В дереве
такие пути уже живут (docs/Бизнес-план…). Добавлен `-c core.quotePath=false` в
обе команды и в сторож выше.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 15:56:24 +05:00
091137a0c5 Быстрый путь caddy_only: считаем изменённые файлы сами, без исключающих шаблонов (#3448)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 13s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m34s
CI / backend-tests (pull_request) Successful in 17m41s
Быстрый путь «правка ТОЛЬКО прокси» (#2916) не отработал ни разу: мерж
84920e6c, где в диффе один файл caddy/sites/apps.caddy, пересоздал весь стек
ПТИЦЫ (gendesign-caddy-1 Created=2026-09-11T20:40:14, в логе Caddy
"serving initial configuration" — холодный старт, а не reload).

ПРИЧИНА НЕ ТА, ЧТО В ГИПОТЕЗЕ. Гипотеза #3448 — пустой github.event.before у
мерж-коммита — опровергнута логом задачи 29244 (run 10881):

    Changes will be detected between 204e2e09de and main
    git diff --no-renames --name-status -z 204e2e09..refs/remotes/origin/main
    M caddy/sites/apps.caddy
    Detected 1 changed files

before валиден, коммит дотянут, дифф верный. Дальше в том же логе:

    ##[group]Filter non_caddy = true
    Matching files:
    caddy/sites/apps.caddy [modified]

Исключённый файл сам себя и «исключил». dorny/paths-filter склеивает шаблоны
одного фильтра через some, то есть ИЛИ (src/filter.ts: patterns.some(aPredicate),
predicate-quantifier по умолчанию some), поэтому

    non_caddy: ['**', '!Caddyfile', '!caddy/**']

читается как «подходит под ** ИЛИ не Caddyfile ИЛИ не caddy/**» — а ** матчит
всё. non_caddy был true ВСЕГДА, caddy_only — false всегда, deploy-caddy
пропускался. Сигнала не было ни одного: пропущенную джобу Forgejo рисует
зелёной, и «зелёный deploy-caddy» неотличим от невыполненного.

ЧТО СДЕЛАНО. Job changes считает список файлов сам: git diff по явным границам
(before → HEAD), флаги backend/frontend/infra/caddy_only выводятся из этого
списка. Заплатки к фильтрам не годятся: predicate-quantifier: every действует
на ВЕСЬ блок и сломал бы backend/frontend/infra, то есть фикс снова висел бы на
незаметном умолчании.

Шаг ПЕЧАТАЕТ и список файлов, и итоговые флаги — у правки должен быть
наблюдаемый признак, иначе «сработало» и «просто не совпало» выглядят одинаково.
База не разрешилась (ручной запуск, пустой/нулевой before, коммита нет в клоне)
→ изменённым считается весь репозиторий: лишний полный деплой безопаснее
пропущенного. Фолбэка на HEAD^..HEAD намеренно нет — у push'а из нескольких
коммитов он молча урезал бы список и включил быстрый путь там, где приехал бэкенд.

Гейт backend/tests/ops/test_3448_caddy_only_detection.py ИСПОЛНЯЕТ этот шаг на
временном репозитории с настоящим мерж-коммитом и проверяет значения флагов:
только caddy → caddy_only=true; caddy+backend → false; база не разрешилась →
полный деплой; решение видно в логе. Отдельная проверка ловит класс бага во всех
воркфлоу — исключающие шаблоны '!' в любом paths-filter без predicate-quantifier: every.

Приёмка на проде: следующий мерж с единственным файлом под caddy/** не меняет
docker inspect gendesign-caddy-1 --format '{{.Created}}', а в логе Caddy —
reload, а не "serving initial configuration".

Closes #3448

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 14:26:52 +05:00
1b1977efa3 fix(deploy): правки ревью гейта ПТИЦЫ — таймаут сессии, crash-loop, множественный id (#3324)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m4s
CI / backend-tests (pull_request) Successful in 17m35s
command_timeout: 30m — дефолт appleboy/ssh-action 10m короче worst-case гейта
(~17 мин), сессию убило бы посреди диагноза и авария читалась бы обрывом связи.

Деградация «нет health-конфига» требовала лишь running дважды: crash-loop с
временем жизни больше паузы проходил как стабильный (обе проверки видят running,
просто это разные жизни контейнера). Теперь сверяется RestartCount до/после окна
15 с; окно учитывается в счётчике ожидания, иначе таймаут 240 с растянулся бы на
~24 мин и упёрся в command_timeout.

cid(): `ps -aq` возвращает несколько id при залежавшемся exited-контейнере →
docker inspect падает → пустой статус → ложный красный. tail -n1.

worker healthcheck: убран `2>&1` (глушил причину, которую деплой печатает из
.State.Health.Log), retries 3→5 — при interval 60s тройка промахов = 3 минуты,
столько длится обычный флап Redis, а из unhealthy контейнер сам не выходит.
2026-09-02 16:58:40 +05:00
13c4420d1f fix(deploy): гейт ПТИЦЫ читает health worker/beat/frontend, а не только curl backend (#3324)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 12s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m4s
CI / backend-tests (pull_request) Successful in 17m40s
Деплой ПТИЦЫ проверял ровно один признак — `curl backend /health`. Worker и beat
не имели healthcheck'а в compose вообще, у frontend была TCP-only проба, которую
деплой не читал. Crash-loop воркера, вставший beat и фронт с 500 уезжали зелёным
деплоем: признак «прод жив» отсутствовал в старом состоянии ровно так же, как в
новом.

compose: worker — `celery inspect ping -d celery@$(hostname)` (адресно в ЭТОТ узел,
без -d ответил бы любой воркер на брокере); beat — свежесть shelve-файла
расписания (на inspect ping beat не отвечает; поминутная beat-задача гарантирует
обновление mtime не реже ~3 мин при sync_every=180 с, порог 10 мин = 3× запас).
Фиктивной `true`-пробы нет: она повторяла бы State.Running.

deploy.yml: после подъёма — ожидание healthy для backend/worker/beat/frontend
через docker inspect, HTTP-статус фронта (TCP мало), сверка running-образа с
локально скачанным $IMAGE_TAG (приём #2679 из deploy-tradein). Каждая проверка
при провале печатает контейнер, статус, healthcheck-лог и хвост логов; итог —
явный rc в логе и exit им же. Порядок «миграции до подъёма кода» не тронут,
`up -d --wait` не используется намеренно (подъём разбит на несколько up).
2026-09-02 16:48:50 +05:00
bot-backend
efaab2efda chore(ops): адрес Poincare — 188.124.37.140 вместо заменённого 188.246.224.93 (#3110)
All checks were successful
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 9s
CI / openapi-codegen-check (pull_request) Successful in 1m58s
CI / backend-tests (pull_request) Successful in 17m16s
Selectel заменил адрес сервера в ночь на 27.08 по нашей заявке: старый
188.246.224.93 фильтровался российскими операторами и не открывался ни с
домашнего интернета, ни с мобильного (#3110). Новый адрес фильтрации не имеет —
проверено с машины владельца после переключения DNS.

Замена уронила сервер на 10 часов: адрес на порте сменился, а в ОС остался
прежний (разбор и восстановление — #3119). Здесь только то, что осталось в
репозитории.

Единственное функциональное вхождение — дефолт HOST_IP в ops/selectel-ci-access.sh:
скрипт открывает раннеру доступ на прод-хост, и с прежним значением он молча
настроил бы доступ на адрес, которого у нас больше нет. Остальные семь — тексты
комментариев в Caddyfile-секциях, compose, bootstrap-скриптах и раннбуке крона;
они не исполняются, но именно по ним сверяются при переезде, и разошедшийся
адрес в них дороже, чем кажется.
2026-08-27 11:23:37 +03:00
17d23cfaae chore(ci): деплой не убивает worker посреди скрап-прогона (#3084)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-worker (push) Successful in 43s
Deploy / build-backend (push) Successful in 44s
Deploy / build-frontend (push) Successful in 42s
Deploy / deploy (push) Successful in 1m41s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
2026-08-24 17:25:23 +00:00
536137d460 feat(ci): деплой проверяет, к тому ли хосту подключился (#3079)
All checks were successful
Deploy Infra Host / sync-infra-host (push) Successful in 2s
Deploy / changes (push) Successful in 9s
Deploy Trade-In / changes (push) Successful in 12s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 52s
Deploy / build-worker (push) Successful in 52s
Deploy / build-frontend (push) Successful in 52s
Deploy Trade-In / build-browser (push) Successful in 41s
Deploy Obsidian / deploy-obsidian (push) Successful in 2m15s
Deploy / deploy (push) Successful in 1m53s
Deploy Trade-In / build-frontend (push) Successful in 2m44s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 12s
Deploy Trade-In / test (push) Successful in 4m8s
Deploy Trade-In / build-backend (push) Successful in 47s
Deploy Trade-In / deploy (push) Successful in 1m42s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 9s
2026-08-24 16:36:50 +00:00
0ba52e55db chore(ops): развести crontab по хостам под переезд (#3072)
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-frontend (push) Successful in 41s
Deploy / build-backend (push) Successful in 43s
Deploy / build-worker (push) Successful in 45s
Deploy / deploy (push) Successful in 1m32s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 9s
2026-08-24 02:04:40 +00:00
f2945b7157 fix(ci): деплой падает громко, если :latest отстаёт от головы по компоненту (#2950) (#3023)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy Trade-In / changes (push) Successful in 15s
Deploy / deploy-caddy (push) Has been skipped
Deploy Trade-In / build-browser (push) Successful in 57s
Deploy / build-backend (push) Successful in 1m2s
Deploy / build-frontend (push) Successful in 1m8s
Deploy / build-worker (push) Successful in 1m9s
Deploy / deploy (push) Successful in 1m58s
Deploy / deploy-status (push) Successful in 3s
Deploy Trade-In / build-frontend (push) Successful in 3m14s
Deploy / perimeter-smoke (push) Successful in 14s
Deploy Trade-In / test (push) Successful in 4m12s
Deploy Trade-In / build-backend (push) Successful in 33s
Deploy Trade-In / deploy (push) Successful in 2m16s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 9s
2026-08-21 12:19:07 +00:00
bot-backend
2d2336cd51 fix(ops): деплой триггерится на любой ops/*.sh, а не только docker-prune.sh (#2203)
All checks were successful
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 9s
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m6s
CI / backend-tests (pull_request) Successful in 17m11s
paths-filter в deploy.yml знал только про ops/docker-prune.sh (#2887) — правка
ops/backup.sh или новый ops/restore-drill.sh из этого же PR не долетели бы до
/opt/gendesign: деплой не триггерится -> git reset --hard origin/main не
исполняется -> cron на VM месяцами крутит старую версию, молча.

Точечный список сам по себе и есть баг: #2887 добавил только тот файл, о
котором тогда шла речь, и следующий новый ops-скрипт (backup.sh) остался
за бортом. Глоб ops/*.sh закрывает класс целиком — не матчит подпути
(ops/db-bootstrap/**, ops/glitchtip-auth-forwarder/**), у них свои explicit
триггеры уже есть, дублирования нет.
2026-08-20 22:24:03 +03:00
68d041022d fix(ci): ожидание докер-лока оставляет след в логе (#2950) (#2958)
Some checks are pending
Deploy / changes (push) Waiting to run
Deploy / build-backend (push) Blocked by required conditions
Deploy / build-worker (push) Blocked by required conditions
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
Deploy / deploy-caddy (push) Blocked by required conditions
Deploy / perimeter-smoke (push) Blocked by required conditions
Deploy / deploy-status (push) Blocked by required conditions
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-browser (push) Successful in 50s
Deploy Trade-In / build-frontend (push) Successful in 2m58s
Deploy Trade-In / test (push) Successful in 3m53s
Deploy Trade-In / build-backend (push) Successful in 32s
Deploy Trade-In / deploy (push) Successful in 1m53s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 8s
2026-08-20 08:44:10 +00:00
feff8214f7 fix(ci): докер-секции прод-деплоев исключают друг друга через host-lock (#2950) (#2955)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-browser (push) Successful in 48s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 47s
Deploy / build-frontend (push) Successful in 49s
Deploy / build-worker (push) Successful in 49s
Deploy / deploy (push) Successful in 1m26s
Deploy / deploy-status (push) Successful in 3s
Deploy / perimeter-smoke (push) Successful in 13s
Deploy Trade-In / build-frontend (push) Successful in 2m59s
Deploy Trade-In / test (push) Successful in 3m52s
Deploy Trade-In / build-backend (push) Successful in 37s
Deploy Trade-In / deploy (push) Successful in 2m22s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 10s
2026-08-20 08:13:56 +00:00
2a01dea103 fix(ci): прод-деплои в одну группу concurrency — прун одного убивал pull другого (#2950) (#2952)
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy Trade-In / changes (push) Successful in 10s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-worker (push) Successful in 51s
Deploy / build-backend (push) Successful in 52s
Deploy / build-frontend (push) Successful in 51s
Deploy Trade-In / build-browser (push) Successful in 32s
Deploy / deploy (push) Successful in 1m21s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Successful in 3m30s
Deploy Trade-In / test (push) Successful in 4m15s
Deploy Trade-In / build-backend (push) Successful in 31s
Deploy Trade-In / deploy (push) Successful in 2m6s
Deploy Trade-In / deploy-status (push) Successful in 2s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
2026-08-20 07:47:48 +00:00
d25ff668f7 ci: правка конфига прокси больше не тянет полный деплой Site Finder (#2916) (#2925)
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / perimeter-smoke (push) Successful in 9s
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-worker (push) Successful in 45s
Deploy / build-frontend (push) Successful in 44s
Deploy / build-backend (push) Successful in 45s
Deploy / deploy (push) Successful in 1m8s
Deploy / deploy-status (push) Successful in 1s
2026-08-19 08:39:01 +00:00
e86f0782da ci: смоук периметра МЕРЫ запускается сразу после деплоя (#2917) (#2922)
All checks were successful
Deploy / changes (push) Successful in 10s
perimeter-smoke-mera / smoke (push) Successful in 12s
Deploy Trade-In / changes (push) Successful in 14s
Deploy / build-frontend (push) Successful in 51s
Deploy / build-backend (push) Successful in 53s
Deploy / build-worker (push) Successful in 53s
Deploy Trade-In / build-browser (push) Successful in 40s
Deploy / deploy (push) Successful in 1m33s
Deploy / deploy-status (push) Successful in 2s
Deploy / perimeter-smoke (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Successful in 2m50s
Deploy Trade-In / test (push) Successful in 4m8s
Deploy Trade-In / build-backend (push) Successful in 35s
Deploy Trade-In / deploy (push) Successful in 1m39s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 10s
2026-08-19 08:13:49 +00:00
bot-backend
87258075a2 Merge main в chore/ci-config-cleanup-v2
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 9s
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Successful in 52s
CI Trade-In / frontend-checks (pull_request) Successful in 1m19s
CI Trade-In / backend-tests (pull_request) Successful in 5m7s
Слить актуальный main (билд-раннер #2841/#2869, невалидные индексы #2752,
честный health-check и deploy-status #2841) в ветку очистки CI. Один конфликт
в .forgejo/workflows/deploy.yml: список triggers.paths — main добавил
ops/docker-prune.sh (#2887), ветка добавила auth/** (RBAC roles config).
Разрешено сохранением обоих путей, без потери ни одного триггера.
2026-08-15 22:30:47 +03:00
bot-backend
8bce8cf5ae fix(ci): fail-safe registry verification + real cache self-heal + honest health-check (#2841 R2)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Ревью R2 нашёл, что вся безопасность предыдущего фикса держалась на
недоказанной поддержке act_runner'ом steps.<id>.outcome: если раннер его
не заполняет, retry-шаг молча не бежит, continue-on-error проглатывает
падение сборки, job зелёный — а деплой тянет старый :latest на прод.

- Добавлен engine-agnostic verify-шаг после каждого retry (6 мест,
  deploy.yml + deploy-tradein.yml): `docker buildx imagetools inspect
  <image>:<sha>` без continue-on-error. Не зависит от того, поддерживает
  ли раннер outcome — проверяет реальное состояние registry напрямую.
  Если ни build, ни retry реально не запушили образ — шаг падает и job
  честно FAILURE независимо от семантики outcome.
- Вернул `cache-to` в retry-шаги (6 мест): без него битый buildcache-тег
  никогда не перезаписывался — retry всегда собирал без cache-to, значит
  cache-to не выполнялся НИКОГДА, и каждый следующий прогон снова падал
  на том же cache-from. Заявленное самолечение не работало ни разу.
- Health-check в deploy.yml (main-стек) под `set -e` не мог упасть:
  `curl ... && break` — curl не последняя команда &&-списка, POSIX
  освобождает такие команды от errexit, цикл дохаживал до sleep (exit 0)
  даже если curl ни разу не отдал 200. Приведено к паттерну
  deploy-tradein.yml: явный флаг healthy + `exit 1` после цикла.
  Подтверждено локальным bash-репро (mock curl, всегда failure): старая
  версия — exit 0, новая — exit 1; позитивный сценарий не сломан.

docker rm -f без -v в SSH-скриптах деплоя не тронут.
2026-08-15 18:44:48 +03:00
bot-backend
9b3889bb36 ci(deploy): честный статус деплоя + нефатальный buildcache (#2841)
Зелёная галка прогона не отличима от пропущенного деплоя: если build падает
из-за битого blob в удалённом buildcache, шаг deploy молча пропускается
(if-условие даёт result=skipped), а прогон в целом не подсвечен как FAILED.

- deploy-status: новая job в конце deploy.yml и deploy-tradein.yml, всегда
  бежит (if: always() && !cancelled()) и падает явно, если deploy.result !=
  success — неважно, пропущен он (upstream build/test упал) или упал сам.
- cache-from нефатален: каждый build-push-action-шаг получил id + continue-
  on-error, и ретрай без cache-from/cache-to при steps.build.outcome ==
  'failure'. Битый remote-кеш больше не роняет саму сборку; следующий
  успешный прогон с кешем перезаписывает buildcache-тег целиком (mode=max)
  и самолечит порчу. Реальные ошибки сборки (не кеш) по-прежнему валят job
  на ретрае — deploy-status их тоже поймает.

Гейт против публикации services-портов на VPS (та же задача, проблема 1)
уже покрыт scripts/check-workflow-ports.py + шагом в ci.yml (#2757/#2759,
слит ранее) — сканирует все .forgejo/workflows/*.yml, включая эти два файла;
новых правок не потребовалось.

docker rm -f БЕЗ -v в SSH-скриптах деплоя не тронут — эти вызовы намеренно
без -v (боевые тома), правка их не касается.
2026-08-15 17:55:17 +03:00
bot-backend
c0782a8c4c chore(deploy): триггерить деплой на правку ops/docker-prune.sh
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Скрипт уборки docker-мусора (#2887) исполняется на прод-VM по cron из
/opt/gendesign/ops/. Файлы туда попадают единственным путём — шагом
`git reset --hard origin/main` внутри deploy.yml.

Но paths-фильтр deploy.yml перечисляет подпути ops/ поимённо, а не ops/**.
Поэтому мерж #2887 деплой НЕ запустил: скрипт остался в main, на VM его не
было, а установленный cron указывал в пустоту. Правки скрипта и дальше
доезжали бы только случайно — со следующим чужим коммитом в backend/.

Ровно этот же баг уже ловили на ops/db-bootstrap/** — там рядом стоит
комментарий с той же формулировкой. Добавляю ops/docker-prune.sh по образцу
и фиксирую грабли в rules/deploy.md, чтобы следующий исполняемый файл в ops/
не наступил на них третий раз.
2026-08-15 17:14:00 +03:00
92593404fa ci: убирать buildx-билдер в конце сборочных job'ов (#2869) (#2870)
Some checks failed
Deploy / changes (push) Successful in 9s
Deploy Trade-In / changes (push) Successful in 13s
Deploy / build-backend (push) Successful in 49s
Deploy / build-frontend (push) Failing after 50s
Deploy / build-worker (push) Successful in 50s
Deploy / deploy (push) Has been skipped
Deploy Trade-In / build-browser (push) Successful in 34s
Deploy Trade-In / build-frontend (push) Successful in 2m29s
Deploy Trade-In / test (push) Successful in 3m46s
Deploy Trade-In / build-backend (push) Successful in 34s
Deploy Trade-In / deploy (push) Successful in 2m11s
2026-08-13 12:48:28 +00:00
482deb4864 fix(migrations): закрепить lock_timeout для блокирующего DDL и ловить невалидные индексы (#2791)
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-frontend (push) Successful in 43s
Deploy Trade-In / build-browser (push) Successful in 43s
Deploy / build-frontend (push) Successful in 47s
Deploy / build-worker (push) Successful in 49s
Deploy / build-backend (push) Successful in 50s
Deploy / deploy (push) Successful in 1m12s
Deploy Trade-In / test (push) Successful in 3m25s
Deploy Trade-In / build-backend (push) Successful in 30s
Deploy Trade-In / deploy (push) Successful in 1m50s
2026-08-07 11:21:28 +00:00
bot-backend
26fe9e4d77 chore(ci): деплой на изменение ролей, ruff-гейт для МЕРЫ, удаление мёртвого workflow
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Successful in 46s
CI Trade-In / frontend-checks (pull_request) Successful in 1m14s
CI Trade-In / backend-tests (pull_request) Successful in 3m57s
- deploy.yml: добавлен auth/** в path-триггеры. app.core.auth кэширует
  roles.yaml на весь lifetime процесса (@lru_cache), а файл монтируется
  ТОЛЬКО в backend (не в worker, у него лишь ./data и ./reports) — без
  триггера правка ролей вступала бы в силу в случайный момент.
- ci-tradein.yml: добавлен блокирующий шаг `ruff check` — гейт для tradein
  backend его не гонял вообще. Заодно почищены 3 срабатывания RUF059
  (unused unpacked vars) в test_estimator_pure_units.py:381 по established
  convention (`_` уже используется в соседнем тесте того же файла).
- Удалён мёртвый .github/workflows/ci.yml (Forgejo его не исполняет; GitHub
  mirror отстал на 500+ коммитов). Закомментированный postgres-services блок
  НЕ перенесён — обоснование, ради которого его хотели оставить как заготовку
  ("тестов против живой БД нет"), уже неверно: tests/sql/ реально гоняются
  против Postgres в ci.yml (#2745), и там намеренно НЕ используется `services:`
  — раннер поднимает job и service-контейнеры в сети хоста, где порт 5432
  занят прод-базой (см. ci.yml:91-100).
- README.md: убрана ложная привязка mypy к живому CI-гейту (только ruff +
  pytest + coverage gate ≥65%); убран stale-блок про .github/workflows/ —
  каталог после удаления ci.yml пуст.
2026-08-06 21:34:36 +03:00
bot-backend
bea61f6cd9 feat(auth): отдельная БД auth — фундамент единого входа «Меры» и «Птицы»
All checks were successful
CI Trade-In / changes (pull_request) Successful in 14s
CI / changes (pull_request) Successful in 14s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 3m0s
CI / backend-tests (pull_request) Successful in 15m35s
PR-1 эпика: вся авторизация переезжает на одну нейтральную форму входа, браузерный
popup (Caddy basic_auth) убирается. Этот PR — ТОЛЬКО фундамент, прод работает как
сейчас: в БД gendesign ничего не меняется, новая БД создаётся и наполняется
логинами без паролей, читать её пока некому.

Почему отдельная БД, а не таблица в существующей: хранилище доступов не должно
принадлежать продукту, из которого аккаунты выносятся. Сервер — существующий
gendesign-postgres (новый контейнер не заводим); проверено, что оба бэкенда
сидят в сети gendesign_shared и TCP-достают до него.

Состав:
- data/sql/auth/001-003 — схема (users, sessions), роль приложения, сид 13 логинов.
  password_hash = NULL у ВСЕХ: plaintext и bcrypt-хеши в git запрещены, пароли
  проставляются отдельно на проде (конвенция репы, прецедент tradein м.193).
- ops/db-bootstrap/create_auth_db.sql — CREATE DATABASE через \gexec. Не миграцией:
  CREATE DATABASE запрещён в транзакции, а миграции обязаны быть транзакционными.
- ops/db-bootstrap/set_auth_app_password.sql — пароль роли из env, зеркало
  set_tradein_fdw_password.sql (GUC + \o /dev/null + %L, строго через stdin —
  :'pw' не интерполируется внутри $$...$$, на этом падал деплой 2026-05-24).

Схема лежит в ПОДКАТАЛОГЕ data/sql/auth/ намеренно: основной цикл деплоя использует
`ls -1 data/sql/*.sql`, который в подкаталоги не рекурсирует → эти файлы физически
не могут примениться в БД gendesign. Защита не на дисциплине, а на глобе. Триггер
`data/sql/**` подкаталог при этом покрывает.

Права: владелец БД — суперюзер, а не auth_app (иначе гранты были бы декорацией).
users — только SELECT+UPDATE (INSERT не выдан: создания аккаунтов в этом PR нет, а
снять грант, на который уже опирается прод-код, сложнее чем выдать). REVOKE ALL ON
DATABASE FROM PUBLIC продублирован в bootstrap и в миграции намеренно: bootstrap
гоняется каждый деплой (переприменяемость), миграция — однократно (самодостаточность).

Проверено ИСПОЛНЕНИЕМ на postgis/postgis:16-3.4 (тот же образ, что на проде):
двойной прогон всех файлов идемпотентен; COALESCE-защита сида не затирает вручную
проставленные пароль/имя (проверено живьём); ASCII-CHECK отклоняет кириллицу;
auth_app коннектится, посторонняя роль → permission denied; битая миграция даёт
exit 1 и НЕ пишется в _schema_migrations, т.е. деплой прервётся до подъёма кода;
пароль с кавычками и бэкслешем не ломает %L и не печатается в stdout.

Тест backend/tests/sql/test_auth_sql_migrations.py: имена, транзакционность, наличие
wiring в deploy.yml и детектор паролей/хешей, покрывающий И data/sql/auth, И
ops/db-bootstrap — единственное место в репе с ALTER ROLE ... PASSWORD.
Детектор проверен на живучесть: подложенный bcrypt-хеш роняет тест.

Открытые развилки зафиксированы комментариями в коде, решаются в PR-2/3:
судьба tradein_users/tradein_sessions (два одинаковых по схеме хранилища) и
expired != disabled (trial-экран не выражается булевым is_active).

NB: переменную AUTH_DB_PASSWORD нужно завести вручную в runtime-env бэкенда на VPS.
Пока пусто — шаг ALTER ROLE пропускается с warning'ом, деплой не падает.
2026-07-31 21:47:33 +03:00
034d9f9bca feat(reports): Celery-джоба полного PDF-отчёта + кэш + 3 endpoint'а (#2259 PR-D) (#2287)
Some checks failed
Deploy / changes (push) Successful in 8s
Deploy / build-backend (push) Successful in 3m12s
Deploy / build-worker (push) Successful in 4m8s
Deploy / build-frontend (push) Successful in 4m46s
Deploy / deploy (push) Has been cancelled
2026-07-03 11:13:06 +00:00
6883d14177 fix(ops): repair broken main-DB backup + harden auth scripts/docs (#71 #427 #429 #428)
Some checks failed
CI / changes (push) Successful in 7s
CI / backend-tests (push) Has been skipped
CI / frontend-tests (push) Has been skipped
CI / changes (pull_request) Successful in 6s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
Deploy / build-backend (push) Blocked by required conditions
Deploy / build-worker (push) Blocked by required conditions
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
Deploy / changes (push) Has been cancelled
#71 (CRITICAL): backup.sh committed 100644 → git reset --hard on deploy
re-asserts non-exec mode → raw-path cron fails Permission denied (last good
dump 2026-05-27, no S3). Commit 100755 + chmod ops/*.sh in deploy.yml +
size sanity-check (never prune good dumps for a truncated one) + keep-N
retention + optional S3 (redacted /etc/default template). Modeled on the
working backup-tradein-db.sh.

#427: widen basic_auth username regex ^[a-z][a-z0-9_.-]{1,62}$ + literal-escape
  dotted names in grep probes.
#429: replace list_users.sh false-positive grep with anchored awk over basic_auth block.
#428: PILOT_ACCESS.md support email → pilot@gendsgn.ru.

Closes #71
Closes #427
Closes #429
Closes #428
2026-06-13 20:13:04 +05:00
d7960276b1 chore(deploy): wire OWN_DEVELOPER_IDS into prod env for §25.3 own-portfolio (#1169)
All checks were successful
CI / changes (push) Successful in 6s
CI / backend-tests (push) Has been skipped
CI / frontend-tests (push) Has been skipped
Deploy / changes (push) Successful in 6s
Deploy / build-worker (push) Successful in 32s
Deploy / build-frontend (push) Successful in 30s
CI / changes (pull_request) Successful in 5s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
Deploy / build-backend (push) Successful in 35s
Deploy / deploy (push) Successful in 1m3s
Mirror the LLM_ENABLED wiring for OWN_DEVELOPER_IDS (own-portfolio
cannibalization, #1169): forward vars.OWN_DEVELOPER_IDS into the SSH deploy
(env block + envs list) and write it to backend/.env.runtime ONLY when
non-empty (conditional, like OPENAI_API_KEY/LLM_ENABLED). Non-sensitive
(public companyGroup ids) → actions variable. Unset → cannibalization keeps
the proxy (dormant). No literal id in the workflow (refs only).

Captures in the pipeline the value already set live on prod via .env.runtime
(OWN_DEVELOPER_IDS=6208 = PRINZIP, verified against domrf_kn → 28 projects).
Owner must set Forgejo Variable OWN_DEVELOPER_IDS=6208 for full pipeline
management; meanwhile the .env.runtime line holds (survives git reset).

Refs #1169
2026-06-09 18:14:12 +05:00
98ecdecb5b chore(deploy): wire OPENAI_API_KEY + LLM_ENABLED into prod env for LLM chat
All checks were successful
CI / changes (push) Successful in 6s
CI / backend-tests (push) Has been skipped
CI / frontend-tests (push) Has been skipped
CI / changes (pull_request) Successful in 5s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
Deploy / changes (push) Successful in 5s
Deploy / build-worker (push) Successful in 28s
Deploy / build-backend (push) Successful in 30s
Deploy / build-frontend (push) Successful in 28s
Deploy / deploy (push) Successful in 1m1s
Forward OPENAI_API_KEY (Forgejo secret) and LLM_ENABLED (Forgejo actions
variable) into the SSH deploy and write them to backend/.env.runtime ONLY
when non-empty, so the LLM chat (#960/#957) stays dormant
(llm_enabled=False, openai_api_key=None) until BOTH are set in Forgejo.
Mirrors the OBJECTIVE_API_KEY wiring (env block + envs list + idempotent
.env.runtime grep-sed-or-printf), but guarded by [ -n ] so an unset
secret/var writes nothing. No literal key (refs only). Compose header
documents both as optional. .env.runtime is shared by backend+worker+beat.

Refs #960
2026-06-09 13:51:41 +05:00
ed49f6b953 fix(deploy): resolve FDW bootstrap race + extract bootstrap DO block to .sql file (#499)
Some checks failed
Deploy Trade-In / changes (push) Successful in 6s
Deploy / changes (push) Successful in 5s
Deploy Trade-In / build-backend (push) Successful in 24s
Deploy Trade-In / build-frontend (push) Successful in 23s
Deploy / build-backend (push) Successful in 26s
Deploy / build-worker (push) Successful in 25s
Deploy / build-frontend (push) Successful in 25s
Deploy Trade-In / deploy (push) Successful in 33s
Deploy / deploy (push) Failing after 22s
2026-05-24 09:46:00 +00:00
94cf199276 feat(tradein): postgres_fdw live read of gendesign.cad_buildings (replaces snapshot) (#493)
Some checks failed
Deploy Trade-In / changes (push) Successful in 5s
Deploy / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Successful in 26s
Deploy Trade-In / build-backend (push) Successful in 47s
Deploy / build-frontend (push) Successful in 29s
Deploy / build-backend (push) Successful in 1m24s
Deploy Trade-In / deploy (push) Successful in 40s
Deploy / build-worker (push) Successful in 2m57s
Deploy / deploy (push) Failing after 37s
Replaces tradein.cad_buildings snapshot with live postgres_fdw foreign table reading gendesign.v_tradein_cad_buildings. Fixes /trade-in/api/v1/geocode/reverse 500 (Nominatim ban) and address_not_geocoded for cadastre addresses (e.g. Хохрякова 81).

Security (deep-review fixes):
- 100_tradein_fdw_role.sql: passwordless CREATE ROLE; password set by deploy.yml ALTER ROLE bootstrap reading GENDESIGN_FDW_PASSWORD from backend/.env.runtime (via psql :'pw' var → format %L — injection-safe).
- core/fdw.py: regex whitelist [A-Za-z0-9_-]{32,256} on password, ValueError without echoing value, try/rollback on commit.
- 060_postgres_fdw_extension.sql: connect_timeout='3' on FOREIGN SERVER + ALTER ADD/SET fallback.
- geocoder.py: _cadastral_forward_sync / _cadastral_reverse_sync wrapped in asyncio.to_thread.
- 100_*.sql: REVOKE ALL ON ALL TABLES/SEQUENCES/FUNCTIONS IN SCHEMA public; only GRANT SELECT on v_tradein_cad_buildings.
- pg_user_mappings query handles PUBLIC mapping (usename IS NULL).

Tests: 3 SQL-injection guards on ensure_fdw_user_mapping + rewritten cadastral suite.
2026-05-24 08:57:30 +00:00
3295808b41 fix(security): caddy/** в deploy paths + username из 401 попытки (#434)
All checks were successful
Deploy / changes (push) Successful in 5s
Deploy / build-frontend (push) Successful in 24s
Deploy / build-worker (push) Successful in 25s
Deploy / build-backend (push) Successful in 26s
Deploy / deploy (push) Successful in 54s
2026-05-23 09:25:41 +00:00
f4d43d1cfc fix(security): forwarder PermissionError — убрать USER nobody + deploy automation (#432)
All checks were successful
Deploy / changes (push) Successful in 5s
Deploy / build-backend (push) Successful in 27s
Deploy / build-worker (push) Successful in 27s
Deploy / build-frontend (push) Successful in 25s
Deploy / deploy (push) Successful in 59s
Hotfix для GlitchTip Issue #71 (PermissionError при чтении /var/log/caddy/gendsgn.ru.log).

Changes:
- ops/glitchtip-auth-forwarder/Dockerfile — убран USER nobody, форвардер под root
- ops/glitchtip-auth-forwarder/forwarder.py — throttle capture_exception (300s)
- .forgejo/workflows/deploy.yml — paths-filter + build + force-recreate sidecar
- docs/runbooks/basic_auth_management.md — Deploy section

Security: caddy_logs mount read-only, нет network listener, нет shell,
auth_forwarder_state dedicated volume. Attack surface минимален.

После merge deploy.yml автоматически rebuild+recreate sidecar на VPS.
2026-05-23 09:04:00 +00:00
d1b721a3be fix(deploy): trigger workflow on all data/sql/** changes, not only .sql (#320)
All checks were successful
Deploy / changes (push) Successful in 4s
Deploy / build-backend (push) Successful in 25s
Deploy / build-worker (push) Successful in 26s
Deploy / build-frontend (push) Successful in 24s
Deploy / deploy (push) Successful in 37s
2026-05-17 18:51:28 +00:00
a93bef4147 feat(deploy): inject OBJECTIVE_API_KEY into backend/.env.runtime (#311)
All checks were successful
Deploy / changes (push) Successful in 5s
Deploy / build-backend (push) Successful in 33s
Deploy / build-worker (push) Successful in 31s
Deploy / build-frontend (push) Successful in 25s
Deploy / deploy (push) Successful in 54s
2026-05-17 16:05:13 +00:00
lekss361
11e78d738e fix(deploy): force-recreate caddy чтобы новые compose mounts применились
Раньше deploy.yml делал `docker compose exec caddy caddy reload` — это
только reload Caddyfile, но НЕ пересоздаёт container. Когда в
docker-compose.prod.yml добавлялся новый volume mount (например
./preview:/srv/preview:ro в PR #268), он не появлялся в running container
до manual `docker compose up -d --force-recreate caddy` через SSH.

Теперь deploy всегда вызывает force-recreate caddy после image pull —
идемпотентно, +~5 сек к deploy time, гарантирует что новые mounts +
Caddyfile блоки активны.
2026-05-17 11:30:45 +03:00
lekss361
e64fbd670b fix(infra): wire GlitchTip DSNs (build-arg + env_file) + reject sentry.io promote
Problem
- GlitchTip Issues = 0 за всё время, хотя backend + frontend SDK интегрированы (PR #207, #208).
- Старый Sentry.io продолжает получать события — user видит уведомления оттуда.

Root cause
- frontend/Dockerfile не имеет ARG NEXT_PUBLIC_GLITCHTIP_DSN → `npm run build` бьёт
  с пустым env var → Next.js инлайнит undefined → SDK init guard `if (dsn)` skips.
  Chrome-devtools check на prod bundle подтвердил: ни в одном из 9 chunks DSN-строка
  не запечена; `i.env.NEXT_PUBLIC_GLITCHTIP_DSN` evaluates to undefined.
- .forgejo/workflows/deploy.yml build-frontend не передавал build-args.
- На VPS backend/.env.runtime содержит legacy SENTRY_DSN=...@sentry.io/...
  config.py:_promote_legacy_sentry_dsn слепо промоутит его в glitchtip_dsn → SDK
  шлёт в чужой Sentry. GLITCHTIP_DSN там не задан.
- deploy.yml SSH-скрипт никогда не редактировал SENTRY_DSN/GLITCHTIP_DSN в .env.runtime.

Solution
1. frontend/Dockerfile: ARG NEXT_PUBLIC_GLITCHTIP_DSN + NEXT_PUBLIC_ENVIRONMENT
   с пустыми defaults, ENV-mirror перед `npm run build`. Локальный build без
   build-args работает по-прежнему (no-op DSN).

2. .forgejo/workflows/deploy.yml:
   - build-frontend: `build-args` передаёт `secrets.GLITCHTIP_FRONTEND_DSN`
     + NEXT_PUBLIC_ENVIRONMENT=production. Инвалидирует cache → frontend
     image пересоберётся (expected).
   - deploy step: GLITCHTIP_BACKEND_DSN через secret, в SSH-скрипте:
     a) `sed -i '/^SENTRY_DSN=/d' backend/.env.runtime` — снести legacy
     b) upsert GLITCHTIP_DSN (sed/printf) тем же паттерном что SENTRY_RELEASE
     c) `compose up -d --force-recreate --no-deps backend worker beat` —
        обычный `up -d` не перечитывает env_file без image change.

3. backend/app/core/config.py: _promote_legacy_sentry_dsn ужесточён —
   принимает SENTRY_DSN только если host == errors.gendsgn.ru. Для других
   URLs (sentry.io) выдаёт UserWarning и НЕ промоутит. Anti-regression на
   случай если SENTRY_DSN снова окажется в .env.runtime после ручного
   вмешательства.

Required Forgejo secrets (Settings → Actions → Secrets)
- GLITCHTIP_BACKEND_DSN = https://3d6e291003e142458957490c83559867@errors.gendsgn.ru/1
- GLITCHTIP_FRONTEND_DSN = https://5d7bc85e300c4e80a8554ccc818ff56d@errors.gendsgn.ru/2
DSNs публичны (видны в browser bundle) — secrets ради build-time injection,
не для конфиденциальности. Если secrets не заданы → deploy succeeds, SDK
no-op, без регрессии.

Test plan
- Verify Forgejo deploy.yml зелёный после merge
- chrome-devtools: открыть gendsgn.ru → search bundle на DSN string → должна
  быть запечена строка errors.gendsgn.ru/2
- Trigger frontend error → POST к errors.gendsgn.ru/api/2/envelope/
- Backend: curl на endpoint вызывающий 500 → событие в GlitchTip backend project
- GlitchTip dashboard https://errors.gendsgn.ru/gendesign/issues — Issues > 0

References
- vault: meta/00_credentials.md (DSNs + incident notes 2026-05-16)
- vault: decisions/Dec_GlitchTip_Frontend_Sentry_SDK.md (env contract)
- vault: fixes/fixes-MOC.md (#204 backend SDK init)
2026-05-16 21:48:28 +03:00
e97f96afa7 fix(ci): switch deploy to .forgejo/workflows + shell GHCR login (#192)
All checks were successful
Deploy / changes (push) Successful in 6s
Deploy / build-backend (push) Successful in 3m44s
Deploy / build-frontend (push) Successful in 26s
Deploy / build-worker (push) Successful in 4m43s
Deploy / deploy (push) Successful in 35s
2026-05-16 05:25:00 +00:00
Renamed from .github/workflows/deploy.yml (Browse further)