fix(infra): wire GlitchTip DSNs (build-arg + env_file) + reject sentry.io promote #225

Merged
lekss361 merged 1 commit from fix/glitchtip-wire-prod-dsns into main 2026-05-16 18:51:42 +00:00
Owner

Problem

GlitchTip Issues = 0 за всё время, хотя backend + frontend SDK интегрированы (PR #207, #208). Параллельно старый Sentry.io продолжает получать события (user видит уведомления оттуда).

Root cause (chrome-devtools verified)

  • Frontend bundle: i.env.NEXT_PUBLIC_GLITCHTIP_DSN evaluates undefined на runtime. DSN-строка не запечена ни в один из 9 chunks (/_next/static/chunks/*.js). frontend/Dockerfile builder stage не имеет ARG NEXT_PUBLIC_GLITCHTIP_DSN, npm run build бьёт с пустым env → Sentry.init() skipped guard'ом if (dsn).
  • Backend: VPS backend/.env.runtime содержит legacy SENTRY_DSN=https://...@sentry.io/.... backend/app/core/config.py:_promote_legacy_sentry_dsn слепо промоутит его в glitchtip_dsn → SDK шлёт события в чужой Sentry.io. GLITCHTIP_DSN на VPS не задан.
  • Deploy.yml: ни build-args для frontend, ни SED-блока для backend .env.runtime — никогда не было автоматизации DSN.

Solution

1. frontend/Dockerfile — build-time injection

ARG NEXT_PUBLIC_GLITCHTIP_DSN=
ARG NEXT_PUBLIC_ENVIRONMENT=production
ENV NEXT_PUBLIC_GLITCHTIP_DSN=$NEXT_PUBLIC_GLITCHTIP_DSN
ENV NEXT_PUBLIC_ENVIRONMENT=$NEXT_PUBLIC_ENVIRONMENT

Пустые defaults — локальный docker build без build-args работает по-прежнему (no-op DSN).

2. .forgejo/workflows/deploy.yml

build-frontendbuild-args передаёт secrets.GLITCHTIP_FRONTEND_DSN + NEXT_PUBLIC_ENVIRONMENT=production. Инвалидирует cache → frontend image пересоберётся (expected).

deploy step — добавлено GLITCHTIP_BACKEND_DSN env var, в SSH-скрипте:

sed -i '/^SENTRY_DSN=/d' backend/.env.runtime  # снести legacy sentry.io
# upsert GLITCHTIP_DSN тем же паттерном что SENTRY_RELEASE
if grep -q '^GLITCHTIP_DSN=' backend/.env.runtime; then
    sed -i "s|^GLITCHTIP_DSN=.*|GLITCHTIP_DSN=$GLITCHTIP_BACKEND_DSN|" backend/.env.runtime
else
    printf 'GLITCHTIP_DSN=%s\n' "$GLITCHTIP_BACKEND_DSN" >> backend/.env.runtime
fi

После compose up -d добавлен --force-recreate --no-deps backend worker beat — обычный up -d не перечитывает env_file без image change, поэтому без force-recreate backend остаётся со старым DSN.

3. backend/app/core/config.py — anti-regression

_promote_legacy_sentry_dsn ужесточён: принимает SENTRY_DSN только если host == errors.gendsgn.ru. Для других URLs (sentry.io) — UserWarning, не промоутит. Защита на случай ручного вмешательства в .env.runtime.

Required Forgejo secrets (ДО merge)

Settings → Actions → Secrets:

Secret Value
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

  • Forgejo deploy зелёный после merge
  • chrome-devtools на gendsgn.ru: bundle содержит запечённый https://5d7bc85e...@errors.gendsgn.ru/2
  • Frontend force-error → POST к errors.gendsgn.ru/api/2/envelope/ 200
  • Backend curl на 500-endpoint → событие в GlitchTip backend project
  • https://errors.gendsgn.ru/gendesign/issues показывает Issues > 0
  • https://errors.gendsgn.ru/gendesign/releases показывает текущий GIT_SHA (опционально — release tracking уже есть в коде)

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
  • Forgejo Issue #204, PRs #205/#207/#208/#219/#223 (full GlitchTip stack)
## Problem GlitchTip Issues = 0 за всё время, хотя backend + frontend SDK интегрированы (PR #207, #208). Параллельно старый Sentry.io продолжает получать события (user видит уведомления оттуда). ## Root cause (chrome-devtools verified) - **Frontend bundle:** `i.env.NEXT_PUBLIC_GLITCHTIP_DSN` evaluates `undefined` на runtime. DSN-строка не запечена ни в один из 9 chunks (`/_next/static/chunks/*.js`). `frontend/Dockerfile` builder stage не имеет `ARG NEXT_PUBLIC_GLITCHTIP_DSN`, `npm run build` бьёт с пустым env → `Sentry.init()` skipped guard'ом `if (dsn)`. - **Backend:** VPS `backend/.env.runtime` содержит legacy `SENTRY_DSN=https://...@sentry.io/...`. `backend/app/core/config.py:_promote_legacy_sentry_dsn` слепо промоутит его в `glitchtip_dsn` → SDK шлёт события в чужой Sentry.io. `GLITCHTIP_DSN` на VPS не задан. - **Deploy.yml:** ни build-args для frontend, ни SED-блока для backend `.env.runtime` — никогда не было автоматизации DSN. ## Solution ### 1. `frontend/Dockerfile` — build-time injection ```dockerfile ARG NEXT_PUBLIC_GLITCHTIP_DSN= ARG NEXT_PUBLIC_ENVIRONMENT=production ENV NEXT_PUBLIC_GLITCHTIP_DSN=$NEXT_PUBLIC_GLITCHTIP_DSN ENV NEXT_PUBLIC_ENVIRONMENT=$NEXT_PUBLIC_ENVIRONMENT ``` Пустые defaults — локальный `docker 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` env var, в SSH-скрипте: ```bash sed -i '/^SENTRY_DSN=/d' backend/.env.runtime # снести legacy sentry.io # upsert GLITCHTIP_DSN тем же паттерном что SENTRY_RELEASE if grep -q '^GLITCHTIP_DSN=' backend/.env.runtime; then sed -i "s|^GLITCHTIP_DSN=.*|GLITCHTIP_DSN=$GLITCHTIP_BACKEND_DSN|" backend/.env.runtime else printf 'GLITCHTIP_DSN=%s\n' "$GLITCHTIP_BACKEND_DSN" >> backend/.env.runtime fi ``` После `compose up -d` добавлен `--force-recreate --no-deps backend worker beat` — обычный `up -d` не перечитывает env_file без image change, поэтому без force-recreate backend остаётся со старым DSN. ### 3. `backend/app/core/config.py` — anti-regression `_promote_legacy_sentry_dsn` ужесточён: принимает `SENTRY_DSN` только если host == `errors.gendsgn.ru`. Для других URLs (sentry.io) — `UserWarning`, не промоутит. Защита на случай ручного вмешательства в `.env.runtime`. ## Required Forgejo secrets (ДО merge) Settings → Actions → Secrets: | Secret | Value | |---|---| | `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 - [ ] Forgejo deploy зелёный после merge - [ ] chrome-devtools на gendsgn.ru: bundle содержит запечённый `https://5d7bc85e...@errors.gendsgn.ru/2` - [ ] Frontend force-error → POST к `errors.gendsgn.ru/api/2/envelope/` 200 - [ ] Backend curl на 500-endpoint → событие в GlitchTip backend project - [ ] `https://errors.gendsgn.ru/gendesign/issues` показывает Issues > 0 - [ ] `https://errors.gendsgn.ru/gendesign/releases` показывает текущий `GIT_SHA` (опционально — release tracking уже есть в коде) ## 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 - Forgejo Issue #204, PRs #205/#207/#208/#219/#223 (full GlitchTip stack)
lekss361 added 1 commit 2026-05-16 18:49:15 +00:00
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)
lekss361 merged commit b5379fb135 into main 2026-05-16 18:51:42 +00:00
Author
Owner

Deep review — APPROVE (merged)

Все три файла проверены, blast radius прозрачный, anti-regression покрыт.

Ключевое:

  • frontend/Dockerfile — ARG/ENV размещены в builder stage до npm run build (корректный Next.js inline timing), пустые defaults сохраняют локальный docker build без build-args.
  • _promote_legacy_sentry_dsn — substring-guard "errors.gendsgn.ru" in legacy отсекает sentry.io. Не строгий urlparse, но для DSN-формата ложных срабатываний на практике не будет (handles :443 и path-id корректно).
  • SED-блок: разделитель | безопасен (DSN не содержит |/&/\), якорь ^SENTRY_DSN= не зацепит GLITCHTIP_SENTRY_DSN-подобные имена, touch backend/.env.runtime выше гарантирует существование файла.
  • --force-recreate --no-deps backend worker beat корректно пересоздаёт только три сервиса, postgres/redis нетронуты.

Pre-merge requirement (напоминание перед deploy):

  • Forgejo Settings → Actions → Secrets должны содержать GLITCHTIP_BACKEND_DSN и GLITCHTIP_FRONTEND_DSN. Если не заданы — deploy зелёный, SDK no-op, без регрессии (safe fallback).

Минорные наблюдения (не блокирующие, можно отдельным PR):

  • legacy.split('@', 1)[-1][:40] в warning — для DSN без @ вернёт весь URL; [:40] ограничивает leak в логах, OK.
  • Anti-regression unit test для нового sentry.io rejection guard было бы плюсом (текущий test_sentry_init.py покрывает init/release/scrub, но не promote-guard).

Test plan после deploy (из PR body):

  1. Forgejo deploy зелёный
  2. chrome-devtools: bundle содержит errors.gendsgn.ru/2
  3. Force-error → POST errors.gendsgn.ru/api/2/envelope/ 200
  4. GlitchTip dashboard Issues > 0
## Deep review — ✅ APPROVE (merged) Все три файла проверены, blast radius прозрачный, anti-regression покрыт. **Ключевое:** - `frontend/Dockerfile` — ARG/ENV размещены в `builder` stage до `npm run build` (корректный Next.js inline timing), пустые defaults сохраняют локальный `docker build` без build-args. - `_promote_legacy_sentry_dsn` — substring-guard `"errors.gendsgn.ru" in legacy` отсекает sentry.io. Не строгий urlparse, но для DSN-формата ложных срабатываний на практике не будет (handles `:443` и path-id корректно). - SED-блок: разделитель `|` безопасен (DSN не содержит `|`/`&`/`\`), якорь `^SENTRY_DSN=` не зацепит `GLITCHTIP_SENTRY_DSN`-подобные имена, `touch backend/.env.runtime` выше гарантирует существование файла. - `--force-recreate --no-deps backend worker beat` корректно пересоздаёт только три сервиса, postgres/redis нетронуты. **Pre-merge requirement (напоминание перед deploy):** - Forgejo Settings → Actions → Secrets должны содержать `GLITCHTIP_BACKEND_DSN` и `GLITCHTIP_FRONTEND_DSN`. Если не заданы — deploy зелёный, SDK no-op, без регрессии (safe fallback). **Минорные наблюдения (не блокирующие, можно отдельным PR):** - `legacy.split('@', 1)[-1][:40]` в warning — для DSN без `@` вернёт весь URL; `[:40]` ограничивает leak в логах, OK. - Anti-regression unit test для нового sentry.io rejection guard было бы плюсом (текущий `test_sentry_init.py` покрывает init/release/scrub, но не promote-guard). **Test plan после deploy (из PR body):** 1. Forgejo deploy зелёный 2. chrome-devtools: bundle содержит `errors.gendsgn.ru/2` 3. Force-error → POST `errors.gendsgn.ru/api/2/envelope/` 200 4. GlitchTip dashboard Issues > 0
Sign in to join this conversation.
No reviewers
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: lekss361/gendesign#225
No description provided.