Lesson: F-09 GitHub Actions CI Pipeline
Date: 2026-05-17 Phase: F-09 Author: Claude Sonnet 4.6 (interactive LLM session) Tags: ci, github-actions, security, cross-tenant, testing, devops
Что сделано
Реализован production-grade GitHub Actions CI pipeline (9-stage) для проекта Razmakh.
Созданные файлы
| Файл | Назначение |
|---|---|
.github/workflows/ci.yml | Основной 9-stage CI pipeline |
.github/workflows/security-weekly.yml | Еженедельный security scan |
.github/dependabot.yml | Automated dependency updates |
.github/PULL_REQUEST_TEMPLATE.md | Шаблон PR с checklist (P15/P17/P19) |
.github/CODEOWNERS | Auto-assign reviewers (Николай всё в Phase 1) |
.github/ISSUE_TEMPLATE/bug.yml | Bug report с severity + cross-tenant флагом |
.github/ISSUE_TEMPLATE/feature.yml | Feature request с P10 alternatives |
.github/ISSUE_TEMPLATE/tech-debt.yml | Tech debt с impact/effort matrix |
.github/workflows/scripts/seed_test_orgs.py | Seed org_alpha + org_beta в CI DB |
.github/workflows/scripts/check_coverage.sh | Coverage threshold check |
.github/workflows/scripts/coverage_pr_comment.sh | Coverage PR comment |
.github/workflows/scripts/check_tests_presence.sh | No-tests gate |
.github/workflows/scripts/check_docs_presence.py | No-doc gate |
Stage breakdown
Stage 1: pre-commit (lefthook + gitleaks) — ~10m timeoutStage 2: lint/format/mypy (ruff + mypy) — needs Stage 1Stage 3: tests + coverage (pytest + pgvector:pg18 service) — needs Stage 2Stage 4: security scan (bandit + pip-audit + trivy + semgrep) — parallel со Stage 3Stage 4.5: cross-tenant probe (P15 CRITICAL — 100% required) — parallel со Stage 3Stage 5: manifest validator (razmakh module validate --all) — parallel со Stage 3Stage 6: Verifier agent — DEFERRED Phase 1.5Stage 7: Visual regression — DEFERRED Phase 1.5 (нужен F-11 frontend)Stage 8: E2E — DEFERRED Phase 1.5 (нужен F-11 frontend)Stage 9a: No-tests gate — needs Stage 3 + 4.5Stage 9b: No-doc gate — needs Stage 3 + 4.5 (Phase 1: warn only)Stage 9c: Deploy — только main branch push, нужны Stage 3+4+4.5+5Что Николаю нужно сделать вручную
Обязательные secrets (Settings → Secrets and variables → Actions):
# 1. SSH ключ для deploy на razmakh-vpsgh secret set RAZMAKH_VPS_SSH_KEY < ~/.ssh/id_ed25519_servers
# 2. GitHub Actions уже имеет GITHUB_TOKEN автоматически (не нужно вручную)Опциональные secrets (для будущих фич):
# Infisical machine identity для CI (Phase 1.5 — когда понадобится)gh secret set INFISICAL_CLIENT_ID --body "<machine-identity-client-id>"gh secret set INFISICAL_CLIENT_SECRET --body "<machine-identity-client-secret>"gh secret set INFISICAL_PROJECT_ID --body "razmakh-project-id"# INFISICAL_API_URL = https://secrets.razmakh.ru (задать как repository variable, не secret)
# Codecov token (если захочешь Codecov cloud — сейчас artifacts достаточно)# gh secret set CODECOV_TOKEN --body "<token-from-codecov.io>"GitHub repo settings:
-
Branch protection rule для
main:- Require status checks:
Stage 1,Stage 2,Stage 3,Stage 4.5(cross-tenant),Stage 5,Stage 9a - Require branches to be up to date before merging
- Dismiss stale reviews when new commits are pushed
- Require status checks:
-
Environments → создать
productionenvironment:- URL:
https://razmakh.ru/api/health - Required reviewers: пока без (Николай единственный → auto-approve)
- URL:
-
Actions permissions → “Allow all actions” или “Allow actions from verified creators”
Технические решения и их обоснование
PostgreSQL image: pgvector/pgvector:pg18
- Соответствует production custom
Dockerfile.postgres(base =pgvector/pgvector:pg18) - Включает pgvector extension из коробки
- Не используем production image напрямую (нет pgBackRest в CI — не нужен)
Seed orgs: фиксированные UUID v7-совместимые
ORG_ALPHA_ID = 00000000-0000-7000-8000-000000000001— reproducible cross-tenant testsORG_BETA_ID = 00000000-0000-7000-8000-000000000002- Одинаковы между Stage 3 и Stage 4.5 (независимые postgres containers)
bandit -ll flag = medium severity и выше
- Low severity имеет много false positives (subprocess, random, etc.)
- Текущий результат: 0 medium/high — чисто
- Если появятся false positives — добавить
# nosec B<number>с обоснованием
Stage 4.5 cross-tenant probe отдельный job
- Намеренно отдельный от Stage 3 для visibility в GitHub Actions UI
- 100% pass = hard block (не warn) — P15 critical, любой fail = потенциальная утечка
- Отдельный postgres service container (isolated, не shared с Stage 3)
Deploy strategy: standard runner + SSH (не self-hosted runner)
- Standard runner проще, не требует maintenance
- Self-hosted в Phase 1.5+ когда compute растёт (F-19’)
- SSH команда использует тот же ключ что и ручной
ssh -i ~/.ssh/id_ed25519_servers webfactory/ssh-agent@v0.9.0— популярный, надёжный action для SSH
No-doc gate: warn (не fail) в Phase 1
- Николай = единственный разработчик, жёсткий блок создаст friction
- Phase 1.5+: поменять
--mode warnна--mode failвci.ymlStage 9b - Exemption patterns:
docs/,research/,lessons/, CI files, test files
Известные ограничения и TODO
-
Stage 9c deploy — падает из-за отсутствия
RAZMAKH_VPS_SSH_KEYsecret. Николаю нужно установить:gh secret set RAZMAKH_VPS_SSH_KEY < ~/.ssh/id_ed25519_servers -
LEFTHOOK_SKIP=gitleaksв Stage 1 — gitleaks запускается отдельно до lefthook с fetch-depth=0. Если lefthook запустит gitleaks ещё раз внутри — будет дублирование (не критично, просто медленно). -
Rollback в Stage 9c deploy — базовый (переключение на предыдущий image). Для production-grade нужен blue-green или хранение тегов. Phase 1.5: улучшить.
-
Semgrep использует
returntocorp/semgrep-action@v1— может потребовать SEMGREP_APP_TOKEN для расширенных правил. Без токена работает с open-source rulesets. -
Coverage 73% — Phase 1 порог warning only. Phase 1.5+: uncomment exit 1 в
check_coverage.shдля жёсткого блока.
Уроки (дополнено после CI run)
L1: asyncpg в CI seed scripts
Для простых seed scripts удобнее использовать asyncpg напрямую (без SQLAlchemy overhead). Нужно конвертировать DSN: postgresql+asyncpg:// → postgresql://. Вызов скриптов через uv run --frozen (не shebang) — asyncpg есть только в venv.
L2: bandit -ll vs -l
-ll = medium+high; -l = low+medium+high. Для CI используем -ll чтобы избежать noise от low-severity false positives. Low-severity issues видны в weekly security scan.
L3: pgvector + роли + схемы в CI
Alembic нужны 3 вещи ДО upgrade head:
CREATE EXTENSION IF NOT EXISTS vector(pgvector)- Роли
razmakh_adminиrazmakh_app(используются вCREATE SCHEMA ... AUTHORIZATION) - Схемы
raw, core, mart, ops(alembic version table =core.alembic_version) Порядок важен: роли → схемы → alembic.
L4: RLS в CI требует non-superuser connection
PostgreSQL superuser bypass RLS даже при FORCE ROW LEVEL SECURITY. Для тестирования RLS нужен отдельный DATABASE_URL как non-superuser:
ADMIN_DATABASE_URL= razmakh_ci (superuser) → только migrations + seedDATABASE_URL= razmakh_app (non-superuser с LOGIN) → app sessions + RLS tests После migrations:GRANT SELECT/INSERT/UPDATE/DELETE ON ALL TABLESдля razmakh_app.
L5: .gitignore secrets/ блокирует Python пакет
.gitignore паттерн secrets/ исключает packages/core/src/razmakh_core/secrets/
из git → hatchling не включает его в wheel → No module named 'razmakh_core.secrets' в CI.
Решение: добавить negation !packages/core/src/razmakh_core/secrets/ И явно git add -f.
L6: pytest pythonpath для uv workspace editable install
Локально uv создаёт _editable_impl_razmakh_core.pth → все sub-packages доступны.
В CI uv может строить wheel (нет .pth) → --import-mode=importlib не находит sub-packages.
Решение: pythonpath = ["apps/api/src", "packages/core/src"] в [tool.pytest.ini_options].
L7: gitleaks GitHub CDN 502
GitHub releases CDN иногда возвращает 502 при download. Нужен retry loop (3 попытки, sleep 5s).
Верификация (первый успешный CI run)
Run: https://github.com/nikolai3690-code/razmakh/actions/runs/26000732521
- Stage 1 (gitleaks): PASS (7s)
- Stage 2 (lint+format+mypy): PASS (20s)
- Stage 3 (tests+coverage): PASS — 200 passed, coverage 73% (1m32s)
- Stage 4 (security): PASS (55s)
- Stage 4.5 (P15 cross-tenant probe): PASS — 18 passed (36s)
- Stage 5 (manifest validator): PASS (12s)
- Stage 9c (deploy): FAIL — RAZMAKH_VPS_SSH_KEY не установлен (ожидаемо)
- secrets RAZMAKH_VPS_SSH_KEY установлен → Stage 9c deploy green на main
Verification gap: Stage 9c deploy не может быть проверен без установки секрета RAZMAKH_VPS_SSH_KEY.
Связанные документы
research/AN-testing-verification.md— source of truth для CI архитектурыresearch/AH-self-maintenance.md— Verifier anti-collusion pattern (Stage 6 DEFERRED)research/W-pentest-scenarios.md— P03/P04 cross-tenant scenarios (Stage 4.5)research/AK-documentation-strategy.md— no-doc-no-merge gate (Stage 9b)netnik-state/decisions/razmakh-ci-docs-gate-2026-05-17.mdnetnik-state/decisions/razmakh-verifier-model-discipline-2026-05-17.md