Add conventional-prefix branch patterns to on.push.branches so
CI triggers immediately on feat/**, fix/**, refactor/**, chore/**,
docs/**, perf/**, test/**, hotfix/** pushes без waiting for PR.
Side effects:
- Double-run при PR (push + pull_request events) — acceptable, capacity=2
- Не trigger'ит на experimental/wip/temp branches (no prefix) — intentional
PR #151 deploy упал с 'POSTGRES_USER: unbound variable'.
source backend/.env — backend/.env содержит app vars (SCRAPE_ADMIN_TOKEN,
OPENROUTESERVICE_API_KEY и пр.), НО НЕ POSTGRES_USER/PASSWORD/DB.
Postgres credentials живут в /opt/gendesign/.env (root) — оттуда их читает
compose через env_file для postgres service. Это canonical location для
DB creds на этом стеке.
Fix: source .env (root) — corresponds к compose env_file resolution.
После merge → next deploy auto-apply применит pending #89/#91 (90 уже
applied via MCP).
Refs: #150, incident 2026-05-15 deploy #25901378924
Co-authored-by: lekss361 <claudestars@proton.me>
* fix(frontend): apiFetchWithStatus body stream double-read crash
Per user report 2026-05-14: 'Failed to execute text on Response: body stream
already read' при analyze с non-JSON error response.
apiFetchWithStatus делал .json() (consumes stream), потом .text() в catch
(FAILS — stream already consumed). Fetch API: body — ReadableStream,
consumable один раз.
Fix: read text() once → JSON.parse on string. На fail (HTML / non-JSON)
оборачиваем в {detail: rawText}. Никаких double-reads.
Site-finder cad search больше не крашится на error pages.
* feat(infra): auto-apply data/sql/*.sql migrations in deploy pipeline (#150)
Закрывает root cause production 500 на /api/v1/admin/site-finder/weight-profiles
(а также 2 unapplied migrations: #89/#91).
## Why это нужно
После audit обнаружили что:
- backend/alembic/versions/ пустой (Alembic configured но не используется)
- deploy.yml не имеет migration step
- 47+ raw SQL files накопилось без auto-apply
- 3 файла unapplied: 87 (engineering), 89 (drop brin), 90 (weight profiles)
User report: GET weight-profiles → 500 потому что user_weight_profiles
table не существует на prod.
## Changes
### deploy.yml +39 lines
- Path trigger расширен на data/sql/*.sql
- New step 'Apply DB migrations' между compose pull и compose up -d:
- Idempotent CREATE TABLE _schema_migrations
- Loop по data/sql/*.sql sorted → skip applied → psql with ON_ERROR_STOP=on
- Record applied filename в tracking table
- Exit 1 на failure → containers НЕ обновляются (no partial state)
### NN collision fix
- 87_engineering_networks_191fz.sql → 91_engineering_networks_191fz.sql
(collision с 87_on_demand_indexes.sql, мой renumber 85→87 был неудачным)
### CLAUDE.md +32 lines
- Subsection 'Auto-apply' под Migration pattern
- Bootstrap procedure docs
- Idempotency requirement
- Failure recovery
### scripts/bootstrap_schema_migrations.sh +100 lines
- One-time seed _schema_migrations с 48 already-applied files
- 3 файла intentionally NOT seeded: 89/90/91 (auto-apply на first deploy)
- Usage docs + verification query
## Vault
domains/infra/Runbook_Auto_Apply_Migrations.md NEW
infra-MOC.md updated
## Required manual steps ДО merge
1. SSH gendesign (tunnel)
2. POSTGRES creds export
3. bash scripts/bootstrap_schema_migrations.sh — seed 48 already-applied files
4. Verify: SELECT COUNT(*) FROM _schema_migrations → 48
5. После merge → first deploy auto-apply #89/#90/#91 → 500 closes
Closes#150
---------
Co-authored-by: lekss361 <claudestars@proton.me>
Previously `docker image prune -af --filter until=72h` only removed
images older than 72h. With multiple deploys/day we accumulate many
fresh (<72h) tagged images — each gendesign-backend / worker / frontend
SHA-tag is 500MB-1GB, filling /var/lib/docker.
New logic: for each ghcr.io/lekss361/gendesign-* repo, list tags sorted
by Created DESC, skip :latest, keep top 2, remove the rest. Still runs
the standard prune after for dangling layers + build cache.
Add `changes` job (dorny/paths-filter@v3) to detect which parts of the
codebase changed. Build jobs now run only when their source tree is
touched; deploy runs as long as no build failed (skipped builds are OK).
Split IMAGE_TAG (latest) from SENTRY_RELEASE_VAL (git sha) so Sentry
release tracking still points to the exact commit while compose pulls
the :latest tag.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Split single build-and-deploy job into 4 jobs: build-backend,
build-worker, build-frontend run in parallel (all 3 independent),
deploy job has needs:[...] on all three. Removes ~2 min of sequential
build time on warm cache runs.
Also: scope GHA cache keys per image (backend-lean / worker-chromium /
frontend) so builds don't invalidate each other's layer cache. Replace
sleep 8 health-check with a 30-iteration polling loop (fails fast on
bad deploy, doesn't over-wait on fast ones). Add --mount=type=cache to
frontend npm ci for node_modules cache across builds.
Obsidian Self-hosted LiveSync (CouchDB) выведен в отдельный docker-compose
stack и GHA workflow. Любой стек теперь редеплоится независимо.
* docker-compose.obsidian.yml: только couchdb в shared external network
* docker-compose.prod.yml: убрана couchdb-секция; caddy подключён к
shared network для маршрута obsidian.gendsgn.ru → couchdb:5984
* .github/workflows/deploy-obsidian.yml: новый workflow с path-filters
(триггерится только при изменениях obsidian-related файлов)
* .github/workflows/deploy.yml: + path-filters (триггерится только при
main-related файлах) + создание shared network в bootstrap
* docs/obsidian-livesync.md: обновлены инструкции под split-архитектуру
После push:
1. ssh gendesign 'docker network create gendesign_shared' (один раз)
2. Прописать COUCHDB_USER/PASSWORD в backend/.env.runtime
3. Добавить DNS A-record obsidian.gendsgn.ru → IP VPS
4. git push → GHA задеплоит обе части независимо
Преимущества:
- Изменения main не разрывают LiveSync клиентов
- Изменения obsidian не рестартуют main
- Падение obsidian-стека не влияет на основное приложение