Перейти к содержимому

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.ymlAutomated dependency updates
.github/PULL_REQUEST_TEMPLATE.mdШаблон PR с checklist (P15/P17/P19)
.github/CODEOWNERSAuto-assign reviewers (Николай всё в Phase 1)
.github/ISSUE_TEMPLATE/bug.ymlBug report с severity + cross-tenant флагом
.github/ISSUE_TEMPLATE/feature.ymlFeature request с P10 alternatives
.github/ISSUE_TEMPLATE/tech-debt.ymlTech debt с impact/effort matrix
.github/workflows/scripts/seed_test_orgs.pySeed org_alpha + org_beta в CI DB
.github/workflows/scripts/check_coverage.shCoverage threshold check
.github/workflows/scripts/coverage_pr_comment.shCoverage PR comment
.github/workflows/scripts/check_tests_presence.shNo-tests gate
.github/workflows/scripts/check_docs_presence.pyNo-doc gate

Stage breakdown

Stage 1: pre-commit (lefthook + gitleaks) — ~10m timeout
Stage 2: lint/format/mypy (ruff + mypy) — needs Stage 1
Stage 3: tests + coverage (pytest + pgvector:pg18 service) — needs Stage 2
Stage 4: security scan (bandit + pip-audit + trivy + semgrep) — parallel со Stage 3
Stage 4.5: cross-tenant probe (P15 CRITICAL — 100% required) — parallel со Stage 3
Stage 5: manifest validator (razmakh module validate --all) — parallel со Stage 3
Stage 6: Verifier agent — DEFERRED Phase 1.5
Stage 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.5
Stage 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-vps
gh 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:

  1. 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
  2. Environments → создать production environment:

    • URL: https://razmakh.ru/api/health
    • Required reviewers: пока без (Николай единственный → auto-approve)
  3. 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 tests
  • ORG_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.yml Stage 9b
  • Exemption patterns: docs/, research/, lessons/, CI files, test files

Известные ограничения и TODO

  1. Stage 9c deploy — падает из-за отсутствия RAZMAKH_VPS_SSH_KEY secret. Николаю нужно установить: gh secret set RAZMAKH_VPS_SSH_KEY < ~/.ssh/id_ed25519_servers

  2. LEFTHOOK_SKIP=gitleaks в Stage 1 — gitleaks запускается отдельно до lefthook с fetch-depth=0. Если lefthook запустит gitleaks ещё раз внутри — будет дублирование (не критично, просто медленно).

  3. Rollback в Stage 9c deploy — базовый (переключение на предыдущий image). Для production-grade нужен blue-green или хранение тегов. Phase 1.5: улучшить.

  4. Semgrep использует returntocorp/semgrep-action@v1 — может потребовать SEMGREP_APP_TOKEN для расширенных правил. Без токена работает с open-source rulesets.

  5. 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 + seed
  • DATABASE_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.md
  • netnik-state/decisions/razmakh-verifier-model-discipline-2026-05-17.md