diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index e9189f88..d140c43d 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -134,6 +134,9 @@ jobs: with: context: ./frontend push: true + build-args: | + NEXT_PUBLIC_GLITCHTIP_DSN=${{ secrets.GLITCHTIP_FRONTEND_DSN }} + NEXT_PUBLIC_ENVIRONMENT=production cache-from: type=registry,ref=${{ env.IMAGE_FRONTEND }}:buildcache cache-to: type=registry,ref=${{ env.IMAGE_FRONTEND }}:buildcache,mode=max tags: | @@ -156,12 +159,13 @@ jobs: IMAGE_TAG: latest SENTRY_RELEASE_VAL: ${{ github.sha }} GHCR_PAT: ${{ secrets.GHCR_PAT }} + GLITCHTIP_BACKEND_DSN: ${{ secrets.GLITCHTIP_BACKEND_DSN }} with: host: ${{ secrets.DEPLOY_HOST }} username: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_SSH_KEY }} port: ${{ secrets.DEPLOY_PORT }} - envs: IMAGE_TAG,SENTRY_RELEASE_VAL,GHCR_PAT + envs: IMAGE_TAG,SENTRY_RELEASE_VAL,GHCR_PAT,GLITCHTIP_BACKEND_DSN script: | set -euo pipefail cd /opt/gendesign @@ -182,6 +186,17 @@ jobs: else printf 'SENTRY_RELEASE=%s\n' "$SENTRY_RELEASE_VAL" >> backend/.env.runtime fi + + # GlitchTip wiring: убираем legacy SENTRY_DSN (auto-promote-логика в + # backend/app/core/config.py:_promote_legacy_sentry_dsn раньше брала + # его и слала события в чужой sentry.io). Устанавливаем GLITCHTIP_DSN + # из Forgejo secret. Пустой secret = no-op (SDK не инициализируется). + sed -i '/^SENTRY_DSN=/d' backend/.env.runtime + 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 chmod 600 backend/.env.runtime # External network для Caddy + obsidian-stack share @@ -227,6 +242,13 @@ jobs: docker compose -p gendesign -f docker-compose.prod.yml up -d + # backend/.env.runtime изменения (SENTRY_RELEASE, GLITCHTIP_DSN) + # требуют --force-recreate — обычный `up -d` не перечитывает env_file + # если только image не сменился. На deploy где меняется только runtime + # без backend image change — без этого backend остаётся со старым DSN. + docker compose -p gendesign -f docker-compose.prod.yml up -d \ + --force-recreate --no-deps backend worker beat + docker compose -p gendesign -f docker-compose.prod.yml exec -T caddy \ caddy reload --config /etc/caddy/Caddyfile || true diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 16c29b34..4fea5f2c 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -20,22 +20,31 @@ class Settings(BaseSettings): @model_validator(mode="after") def _promote_legacy_sentry_dsn(self) -> "Settings": - """Backward-compat: если SENTRY_DSN задан, а GLITCHTIP_DSN пуст — автопродвигаем. + """Backward-compat: legacy SENTRY_DSN → glitchtip_dsn ТОЛЬКО для self-hosted. - На существующем VPS .env.runtime содержит SENTRY_DSN=https://... - (старое имя переменной). Pydantic extra="ignore" молча его отбрасывает, - SDK остаётся no-op. Читаем напрямую из os.environ и выдаём DeprecationWarning. + Старый VPS .env.runtime мог содержать SENTRY_DSN=https://...@sentry.io/... + (legacy SaaS Sentry). Промоутить его НЕЛЬЗЯ — события пойдут в чужой проект. + Принимаем только URLs указывающие на наш errors.gendsgn.ru host. """ if not self.glitchtip_dsn: legacy = os.getenv("SENTRY_DSN") if legacy: - warnings.warn( - "SENTRY_DSN is set but ignored by pydantic; rename to GLITCHTIP_DSN. " - "Auto-promoting for backward compat.", - DeprecationWarning, - stacklevel=2, - ) - self.glitchtip_dsn = legacy + if "errors.gendsgn.ru" in legacy: + warnings.warn( + "SENTRY_DSN is set but ignored by pydantic; rename to GLITCHTIP_DSN. " + "Auto-promoting for backward compat.", + DeprecationWarning, + stacklevel=2, + ) + self.glitchtip_dsn = legacy + else: + warnings.warn( + f"SENTRY_DSN points to non-GlitchTip host " + f"({legacy.split('@', 1)[-1][:40]}...) — ignoring. " + "Set GLITCHTIP_DSN explicitly.", + UserWarning, + stacklevel=2, + ) return self # External APIs (Stage 2) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 6e34fff2..6c769b35 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -13,6 +13,12 @@ RUN --mount=type=cache,target=/root/.npm \ FROM node:20-alpine AS builder WORKDIR /app ENV NEXT_TELEMETRY_DISABLED=1 +# NEXT_PUBLIC_* must be present at `npm run build` — Next.js inlines them into +# the client bundle. Defaults to empty so local `docker build` без build-args не ломается. +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 COPY --from=deps /app/node_modules ./node_modules COPY . . RUN npm run build