Skip to content
proDreamsPublic

About

🤖 Self-hosted AI code review for your Pull Requests.

Topics

Resources

Stars

23 stars

Watchers

0 watching

Forks

Repository files navigation

Napkin Tools: ReVu

English · Русский

🤖 Self-hosted AI code review для ваших Pull Requests.

GitHub License GitHub Actions Workflow Status Сайт Код на салфетке Заметки на салфетке Кот на салфетке

ReVu


Оглавление

  1. О проекте
  2. Ключевые возможности
  3. Технологии
  4. ToDo
  5. Требования
  6. Быстрый старт
  7. Ревью в пайплайне
  8. Разработка
  9. Автор
  10. Поддержка
  11. Лицензия

О проекте

ReVu — это AI-ревью для Pull Request: self-hosted вебхук-сервис либо команда в пайплайне. В серверном режиме он принимает вебхуки от Git-провайдеров (сейчас поддерживаются GitHub, Gitea, GitLab и Bitbucket) и публикует результат прямо в PR; в режиме пайплайна делает то же самое там, где репозиторий уже склонирован, — без входящего адреса, постоянного хоста и отдельной учётной записи для бота.

Один инстанс обслуживает любое количество подключений: можно одновременно ревьюить репозитории на GitHub, в корпоративном GitLab и в Gitea, используя общий ИИ-провайдер или отдельный профиль для каждого.

Основная идея — разгрузить команду и повысить качество кода. Прежде чем ваш коллега возьмётся за ревью, PR уже проверит ИИ и укажет на потенциальные проблемы. ReVu — не сторонний сервис, а self-hosted инструмент, который вы разворачиваете у себя.


Ключевые возможности

  • Два способа запуска
    • Сервер — вебхук от Git-провайдера, постоянный инстанс, отдельная учётка для бота.
    • Пайплайн — revu review прямо в GitHub Actions, GitLab CI или Gitea Actions: тот же образ, тот же промпт, тот же результат, но без входящего адреса и без хоста. См. Ревью в пайплайне.
  • Два режима работы
    • Комментарий (comment) — единый подробный отзыв по всему Pull Request.
    • Инлайн (inline) — точечные комментарии к конкретным изменениям в коде.
  • Повторное ревью помнит предыдущее
    • Правки в PR проверяются с учётом того, что ReVu уже написал: исправленные замечания не повторяются, оставшиеся не дублируются комментариями, а попадают в итог. Подробности — в разделе Повторное ревью.
    • Для первого и повторного ревью используются разные промпты.
  • Управляемая глубина контекста
    • flat (по умолчанию) — ревью по диффу и комментариям PR.
    • deep — модель дополнительно видит структуру репозитория, тела изменённых функций целиком, сигнатуры того, что этот код вызывает, и связанные тесты. Меньше выдуманных замечаний и «а вы не обработали случай X», который обработан двадцатью строками ниже.
    • Бюджеты контекста настраиваются, а каждая ревью пишет в лог, сколько это стоило: сколько файлов и символов было в диффе, сколько добавил контекст и во что это примерно превращается в токенах.
  • Ревью, которое не теряется
    • Перед публикацией комментарии сверяются с диффом: строка, которой в изменениях нет, не отменяет остальные замечания. Раньше одно выдуманное моделью число могло стоить всего ревью.
    • Если ревью всё же не удалось, ReVu сообщает об этом в PR (NOTIFY_ON_FAILURE), а не молчит: сервис, который ничего не написал, выглядит как сервис, который всё проверил. Подробности остаются в логах.
  • Несколько провайдеров на одном инстансе
    • Один контейнер обслуживает сколько угодно Git-подключений — GitHub, Gitea, GitLab и Bitbucket в любом сочетании.
    • Для каждого подключения свой вебхук, свой токен и свой секрет.
    • Общий ИИ-провайдер по умолчанию, с возможностью переопределить для конкретного подключения.
  • Гибкая интеграция
    • Поддержка GitHub, Gitea, GitLab (включая self-managed) и Bitbucket.
    • Совместимость с OpenAI API и его альтернативами (OpenRouter, LocalAI, LM Studio и др.), а также с российскими ИИ-провайдерами: GigaChat и YandexGPT.
    • Именованные профили ИИ: разные модели для разных репозиториев без второго контейнера.
  • Полностью self-hosted
    • Разворачивается на ваших серверах без передачи данных третьим лицам.
    • Можно использовать локальные модели (например, DeepSeek, Qwen), запуская их отдельно в вашей инфраструктуре.
  • Простая конфигурация
    • Настройка через один YAML-файл; полный справочник — в config/settings.yaml.example_full.
    • Ошибка в конфигурации останавливает запуск с указанием проблемного ключа, а не превращает каждый вебхук в молчаливый сбой.
    • Таймауты, количество повторов и экспоненциальный backoff настраиваются; повторяются только запросы, которые нельзя задублировать.
    • Ограничение одновременных ревью считается отдельно по каждому подключению, чтобы шумный репозиторий не блокировал остальные.
  • Наблюдаемость
    • Логи пишутся в консоль и в файл с ротацией; в каждой записи указано подключение, к которому она относится.

Технологии

  • Python 3.13+ и FastAPI — API и вебхук-обработчик
  • HTTPX — единый HTTP-клиент для Git- и AI-провайдеров: таймауты, повторы и backoff в одном месте
  • OpenAI SDK, GigaChat SDK, Yandex AI Studio SDK — интеграция с LLM
  • Dishka — внедрение зависимостей; конфигурация и клиенты собираются в одном месте
  • Dynaconf + pydantic — конфигурация и валидация вебхуков
  • unidiff — разбор диффов и подготовка их к инлайн-комментариям
  • Docker — для удобного развертывания

ToDo

  • Поддержка Git-провайдеров:
    • GitHub
    • Gitea
    • GitLab (включая self-managed)
    • Bitbucket (спасибо lanity_roshoose)
  • Поддержка дополнительных AI-провайдеров:
    • Gemini
    • DeepSeek
    • Anthropic
    • Qween
    • GigaChat
    • YandexGPT
  • Гибкая настройка кастомных промптов для ревью (через SYSTEM_PROMPT)
  • Подробная документация по конфигурации и запуску
  • Англоязычная версия README
  • Покрытие тестами - Coverage 100%
  • Больше документации и Docstrings во всём коде
  • Replay-защита вебхуков GitLab по webhook-timestamp
  • Проверка инлайн-комментариев на реальных PR всех поддерживаемых провайдеров

Требования

Для корректной работы ReVu потребуется:

  • Docker и Docker Compose — ReVu поставляется готовым образом, отдельная установка Python не нужна.

  • Доменное имя с настроенным SSL-сертификатом

    • Обязательно для публичных сервисов (GitHub, GitLab.com): они доставляют вебхуки только на доступный извне HTTPS-адрес с валидным сертификатом.
    • Если Git-сервер и ReVu работают в одной Docker-сети, публичный адрес и SSL не нужны.
  • Персональный токен доступа (PAT) от вашего Git-провайдера

    • GitHub — PAT Classic с правами на repo.
    • Gitea — токен с доступом к репозиторию и Pull Requests.
    • GitLab — токен со scope api: он нужен и на чтение диффа, и на публикацию комментариев.
    • Bitbucket — токен с доступом на чтение Pull Requests и публикацию комментариев.
  • API-ключ от ИИ-провайдера

    • OpenAI и совместимые сервисы (OpenRouter, LocalAI, LM Studio), GigaChat или YandexGPT.

Быстрый старт

Для того чтобы запустить проект, достаточно выполнить несколько шагов:

  1. Подключитесь по SSH к серверу и в удобном месте создайте директорию для проекта:

    mkdir revu && cd revu
  2. Скачайте конфигурационный файл и пропишите настройки:

    1. Скачайте файл — упрощённый или полный, с комментариями по каждому параметру:
      • Упрощённый:
        wget -O settings.yaml https://raw.githubusercontent.com/proDreams/revu/refs/heads/main/config/settings.yaml.example
      • Полный:
        wget -O settings.yaml https://raw.githubusercontent.com/proDreams/revu/refs/heads/main/config/settings.yaml.example_full
    2. Откройте файл для редактирования:
      nano settings.yaml
    3. Заполните следующие параметры:
      • REVIEW_MODE - формат ревью по умолчанию. Доступные варианты:
        • "comment" - один большой комментарий к Pull Request
        • "inline" - точечные комментарии к конкретным участкам кода
      • DEFAULT_AI_PROVIDER - имя профиля из AI_PROVIDERS, который используется всеми Git-подключениями, если в подключении не указан свой.
      • AI_PROVIDERS - именованные настройки ИИ. Один профиль может использоваться несколькими Git-подключениями. Для каждого профиля:
        • AI_PROVIDER - используемый ИИ-провайдер. Доступные варианты:
          • "openai" - официальный API от OpenAI
          • "openai_compatible" - совместимый с OpenAI провайдер
          • "gigachat" - официальный GigaChat
          • "yandexgpt" - официальный YandexGPT
        • AI_PROVIDER_API_KEY - API Ключ от ИИ-провайдера
        • AI_PROVIDER_MODEL - используемая модель (например, gpt-4o-mini)
        • AI_PROVIDER_TIMEOUT - сколько секунд отпущено на ВЕСЬ запрос к модели, включая генерацию ответа (по умолчанию 300; таймаут Git-хостов к ИИ не применяется, а таймаут httpx не подходит — он считает паузы между чанками, а не общее время)
        • AI_PROVIDER_RETRIES - сколько раз повторить запрос, если модель не ответила (по умолчанию 4, то есть пять попыток; повторяются только сбои без ответа модели); превышение AI_PROVIDER_TIMEOUT повторяется только один раз
        • AI_PROVIDER_BASE_URL - обязателен для openai_compatible, URL от OpenAI-совместимого провайдера
        • AI_PROVIDER_SCOPE - только для GigaChat. Определяет используемую версию GigaChat, указан в личном кабинете.
        • AI_PROVIDER_FOLDER_ID - обязателен для YandexGPT. Идентификатор рабочей директории.
      • GIT_PROVIDERS - Git-подключения. Ключ записи становится частью URL вебхука: https://<ваш-хост>/webhooks/<ключ>. Ключ должен соответствовать ^[a-z0-9][a-z0-9_-]{2,31}$ и быть уникальным. Для каждого подключения:
        • GIT_PROVIDER - используемый Git-провайдер. Доступные варианты:
          • "github"
          • "gitea"
          • "gitlab"
          • "bitbucket"
        • GIT_PROVIDER_USER_TOKEN - персональный токен доступа (PAT) от вашего Git-провайдера
        • GIT_PROVIDER_URL - URL Git-провайдера. Обязателен для Gitea и BitBucket. Для GitHub и GitLab необязателен: без него используются api.github.com и https://gitlab.com. Для GitHub Enterprise Server укажите корень REST API вместе с суффиксом /api/v3 (например, https://ghe.example.com/api/v3), для GitLab Self-Managed — корень инстанса: /api/v4 ReVu добавляет сам.
        • GIT_PROVIDER_SECRET_TOKEN - случайный набор символов. Используется для валидации входящего вебхука от Git-провайдера: в вебхуке провайдера должен быть указан тот же самый секрет. У каждого подключения свой секрет — подпись одного подключения не подходит к другому.
          • GitHub, Gitea, BitBucket — подпись тела запроса в заголовках X-Hub-Signature-256, X-Gitea-Signature или X-Hub-Signature (формат sha256=<hex>). Для BitBucket подпись обязательна: нужен Bitbucket Data Center 8.9+ и заполненное поле Secret в настройках вебхука, иначе запросы будут отклоняться с 403.
          • GitLab — либо Signing token (GitLab 19.0+, рекомендуется: подписывается и тело запроса), либо legacy Secret token, который уходит открытым текстом в заголовке X-Gitlab-Token и тело запроса не защищает.
        • (необязательно) AI_PROVIDER - имя профиля из AI_PROVIDERS, чтобы это подключение ревьюилось другой моделью (например, "cheap"). Если не указано — берётся DEFAULT_AI_PROVIDER.
        • (необязательно) GIT_PROVIDER_REVIEWER - только для BitBucket. Username от имени которого будет производиться ревью. Ревью запускается на события pr:opened и pr:from_ref_updated (новые коммиты в PR); изменение только метаданных PR (pr:modified) ревью не запускает.
        • (необязательно) SYSTEM_PROMPT, REVIEW_MODE, MAX_CONCURRENT_REVIEWS, NOTIFY_ON_FAILURE, INCLUDE_PR_DISCUSSION - значения для этого подключения; если не указаны, берутся глобальные.
      • (необязательно) SYSTEM_PROMPT - дополнительные инструкции, которые добавляются к стандартному промпту (не заменяют его: формат ответа и критерии ревью сохраняются).
      • Логирование (все параметры необязательны):
        • LOG_LEVEL - уровень логирования (DEBUG, INFO, WARNING, ERROR, CRITICAL, по умолчанию INFO)
        • LOG_DIR / LOG_FILE - каталог и имя файла логов (по умолчанию logs и logs.txt). Логи пишутся и в консоль, и в файл; в Docker каталог удобно вынести на хост (в docker-compose.yaml это уже сделано).
        • MAX_SIZE_MB / BACKUP_COUNT - размер файла до ротации в мегабайтах и сколько ротаций хранить (по умолчанию 10 и 5).
        • В каждой записи указано подключение, к которому она относится, — при нескольких подключениях это заметно экономит время при разборе инцидента.
      • (необязательно) MAX_CONCURRENT_REVIEWS - сколько ревью может выполняться одновременно на одно подключение (по умолчанию 3). Остальные вебхуки ждут свободный слот, а не запускают неограниченное число запросов к ИИ. Лимит считается по каждому подключению отдельно, чтобы шумный репозиторий не блокировал остальные.
      • (необязательно) HTTP_CLIENT_TIMEOUT - таймаут HTTP-запросов в секундах (по умолчанию 30.0)
      • (необязательно) HTTP_CLIENT_REQUEST_ATTEMPTS - сколько всего раз может быть отправлен повторяемый запрос (по умолчанию 3). Повторяются только безопасные запросы: клиентские ошибки 4xx не повторяются, а комментарий ревью (POST) не отправляется повторно после 5xx или таймаута чтения — иначе в PR появился бы дубликат.
      • (необязательно) HTTP_CLIENT_RETRY_BACKOFF - базовая задержка экспоненциального backoff между повторами в секундах (по умолчанию 0.5, потолок — 8 секунд). Заголовок Retry-After от сервера имеет приоритет.
      • (необязательно) APP_HOST / APP_PORT - адрес, который слушает встроенный сервер (по умолчанию 0.0.0.0:8000)
      • (необязательно) NOTIFY_ON_FAILURE - публиковать ли в PR короткое сообщение, если ревью не удалось (по умолчанию true). В сообщение попадает только тип ошибки; подробности остаются в логах. Отключите, если не хотите шума при систематическом сбое.
      • (необязательно) CONTEXT_DEPTH - сколько кода видит модель: "flat" (по умолчанию, только дифф) или "deep" (ещё структура репозитория, тела изменённых функций, сигнатуры зависимостей и связанные тесты). Режим deep читает файлы репозитория и передаёт их ИИ-провайдеру целиком, поэтому он выключен по умолчанию.
      • (необязательно) CONTEXT_MAX_FILES (по умолчанию 12) - сколько дополнительных файлов разрешено подтянуть: модули зависимостей и тесты. Изменённые файлы и сам дифф в это число не входят и не урезаются никогда.
      • (необязательно) CONTEXT_MAX_CHARS (по умолчанию 40000, примерно 10k токенов) - общий бюджет всего добавленного контекста. Когда он исчерпан, контекст обрезается, а в лог пишется строка об этом; дифф остаётся полным.
      • (необязательно) INCLUDE_PR_DISCUSSION - передавать ли ИИ-провайдеру комментарии других участников PR как контекст повторного ревью (по умолчанию true). Собственные замечания ReVu передаются всегда: это её же прошлый ответ, а не новые данные, покидающие вашу сеть. Выключите флаг, если обсуждение в PR не должно уходить ИИ-провайдеру.

    Конфигурация проверяется при старте: опечатка в REVIEW_MODE, неизвестное имя профиля в AI_PROVIDER или отсутствующий токен провайдера останавливают запуск с указанием проблемного ключа, а не превращают каждый вебхук в молчаливый сбой. 4. Сохраните и выйдите, нажав CTRL+S, затем CTRL+X.

  3. Скачайте docker-compose-файл:

    wget -O docker-compose.yaml https://raw.githubusercontent.com/proDreams/revu/refs/heads/main/docker-compose.yaml
  4. Запустите проект:

    sudo docker compose up -d
  5. Откройте ReVu наружу. В docker-compose.yaml порт намеренно не проброшен: по умолчанию сервис доступен только из своей compose-сети. Выберите один из двух вариантов — оба уже лежат в файле закомментированными, достаточно убрать #:

    • Есть Traefik — раскомментируйте блок labels у сервиса и блок networks в конце файла, заменив revu.example.com на свой домен. Сертификат Traefik получит сам (resolver letsencrypt), контейнер при этом слушает обычный HTTP на порту 8000.
    • Прокси на хосте (Caddy, NGINX) или Git-сервер в одной сети с ReVu — раскомментируйте ports: - "8000:8000" и направьте прокси на 127.0.0.1:8000. Если Git-провайдер и ReVu в одной Docker-сети, прокси можно не использовать вовсе. HTTPS с валидным сертификатом обязателен, если Git-провайдер доставляет вебхуки извне.
  6. Пропишите вебхук в каждом Git-провайдере на URL своего подключения:

    https://<ваш-хост>/webhooks/<ключ из GIT_PROVIDERS>
    

    Например, для подключения main из примера конфигурации — https://revu.example.com/webhooks/main. Секрет вебхука должен совпадать с GIT_PROVIDER_SECRET_TOKEN этого подключения: подпись одного подключения не подходит к другому.

    • GitHub — включите событие Pull requests и укажите секрет в поле Secret.
    • Gitea — включите Pull request events и укажите секрет в поле Secret.
    • GitLab — включите Merge request events. В поле Signing token (GitLab 19.0+) вставьте GIT_PROVIDER_SECRET_TOKEN этого подключения, либо для более старых версий заполните Secret token. Ревью запускается на open, reopen и update; правка заголовка или меток (update без новых коммитов) ревью не перезапускает — GitLab не различает эти события, поэтому ReVu сравнивает head-коммит.
    • Bitbucket — включите события Opened и Source branch updated и заполните Secret.
  7. (необязательно) Проверьте, что сервис поднялся: curl http://localhost:8000/health должен ответить {"status":"ok"}. Команда сработает, если порт проброшен (шаг 5); без проброса то же самое изнутри контейнера — docker compose exec revu curl -fsS http://127.0.0.1:8000/health. Этот же эндпоинт используется в HEALTHCHECK Docker-образа. Список подключений, которые увидел сервис, печатается в лог при старте: configured git connections: gh-main (github), gitea-internal (gitea); default AI profile: strong

Несколько Git-провайдеров на одном инстансе

Один инстанс ReVu обслуживает сколько угодно Git-подключений: GitHub, Gitea, GitLab и Bitbucket в любом сочетании. Каждое подключение — своя запись в GIT_PROVIDERS со своими токеном и секретом, и свой URL вебхука.

ИИ-провайдер при этом общий: все подключения по умолчанию используют профиль из DEFAULT_AI_PROVIDER. Если для конкретного подключения нужна другая модель, добавьте ему AI_PROVIDER с именем другого профиля:

DEFAULT_AI_PROVIDER: "strong"

AI_PROVIDERS:
  strong:
    AI_PROVIDER: "openai"
    AI_PROVIDER_API_KEY: "sk-..."
    AI_PROVIDER_MODEL: "gpt-4.1"
  cheap:
    AI_PROVIDER: "openai_compatible"
    AI_PROVIDER_BASE_URL: "https://openrouter.ai/api/v1"
    AI_PROVIDER_API_KEY: "sk-or-..."
    AI_PROVIDER_MODEL: "qwen-2.5-coder"

GIT_PROVIDERS:
  main:                 # идёт на "strong" — как все
    GIT_PROVIDER: "github"
    GIT_PROVIDER_USER_TOKEN: "ghp_..."
    GIT_PROVIDER_SECRET_TOKEN: "..."
  side-project:         # переопределение: идёт на "cheap"
    GIT_PROVIDER: "gitea"
    GIT_PROVIDER_URL: "https://gitea.example.com"
    GIT_PROVIDER_USER_TOKEN: "..."
    GIT_PROVIDER_SECRET_TOKEN: "..."
    AI_PROVIDER: "cheap"
    REVIEW_MODE: "comment"          # необязательно
    MAX_CONCURRENT_REVIEWS: 1       # необязательно
  gitlab-internal:      # Self-Managed: без URL ReVu пошёл бы на gitlab.com
    GIT_PROVIDER: "gitlab"
    GIT_PROVIDER_URL: "https://gitlab.example.com"
    GIT_PROVIDER_USER_TOKEN: "glpat-..."
    GIT_PROVIDER_SECRET_TOKEN: "whsec_..."

Накладные расходы: один контейнер, один конфиг, один набор логов — вместо отдельного инстанса на каждого провайдера.

Повторное ревью

Когда автор отправляет правки, ReVu не начинает с чистого листа. Перед ревью она читает то, что уже есть в PR: собственные прошлые замечания и обсуждение вокруг них. Для такого прогона используется отдельный промпт, и результат отличается:

  • исправленное не повторяется — замечание, которое автор закрыл, больше не поднимается;
  • оставшееся не дублируется — вместо повторного комментария на ту же строку оно попадает в итог;
  • инлайн-комментарии ставятся только на новое — на проблемы в свежих изменениях;
  • итог начинается с прогресса — 📊 Из прошлых замечаний исправлено: 3 из 5. Осталось: 2. Общее число берётся из прошлых замечаний ReVu, поэтому занизить его нельзя.

Обсуждение учитывается: если участники объяснили решение или попросили не менять код, замечание не поднимется повторно.

Как ReVu узнаёт свои комментарии. Не по автору: ReVu обычно работает на вашем же токене, поэтому «бот» и «вы» — один аккаунт, и по автору любой ваш комментарий выглядел бы как прошлое ревью. Вместо этого ReVu подписывает то, что публикует: в конце текста остаётся невидимый маркер, а сверху — видимый заголовок 🤖 ReVu (у повторного — 🤖 ReVu · повторное ревью). Инлайн-комментарии помечаются 🤖 ReVu:. Так под одной учёткой видно, кто есть кто, а определение своих комментариев не зависит от аккаунта.

Рекомендуется всё же завести отдельную учётку для ReVu — не ради корректности (её обеспечивает подпись), а ради читаемости: в истории PR сразу видно, где бот, а где человек, и ваша переписка не смешивается с выводом модели.

Откуда берутся прошлые комментарии:

Провайдер Что читается
GitHub, Gitea таймлайн PR, инлайн-комментарии и тела ревью
GitLab заметки MR (системные пропускаются, состояние «resolved» сохраняется)
Bitbucket Data Center комментарии с якорями и вложенными ответами

Повторное ревью запускается по тому же вебхуку, что и первое: признак — наличие прошлых замечаний ReVu в PR. Если PR ещё не ревьюился, используется обычный промпт.

Приватность. Обсуждение в PR (комментарии коллег) уходит ИИ-провайдеру как контекст. Если это недопустимо, выключите INCLUDE_PR_DISCUSSION — тогда передаются только замечания самой ReVu.

Глубина контекста

По умолчанию ReVu ревьюит по диффу: предсказуемо по цене и ничего лишнего не отправляет наружу. Режим deep добавляет ровно четыре вещи:

  1. структуру репозитория — по строке на каталог, по несколько имён в каждой, манифесты и lock-файлы первыми. По uv.lock в этом списке видно, что проект на uv, по packages/ — где живут компоненты, по .gitea/workflows/ — чем он собирается. Дерево всё равно читается для поиска тестов и зависимостей, так что лишних запросов это не стоит;

  2. тела изменённых функций целиком — дифф показывает три строки вокруг правки, а модель судит обо всём методе. Это самая частая причина и ложных замечаний, и пропусков;

  3. сигнатуры того, что вызывает изменённый код — по импортам подтягиваются только объявления (и docstring), а не тела. Модель видит контракт и перестаёт его выдумывать;

  4. связанные тесты — по путям и именам файлов, только имена тестовых функций.

Что важно знать:

  • Дифф никогда не урезается. Бюджеты ограничивают только дополнительные файлы и общий объём добавленного. PR из 15 файлов модель увидит целиком независимо от CONTEXT_MAX_FILES.
  • Языки. Разбор идёт через tree-sitter; в поставку входят грамматики для Python, JavaScript, TypeScript, Go, Java, Kotlin, Ruby, Rust, C#, PHP, C, C++, Swift, Scala, Elixir, Lua, Perl, Dart, Bash, SQL, HTML, CSS, HCL, YAML, JSON, Make, Zig, Haskell, OCaml и Dockerfile. Для языка без грамматики режим не выключается: изменённый файл попадает в контекст целиком.
  • Приватность. В deep ИИ-провайдеру уходят целые файлы, а не только изменения. Содержимое .env, ключей, credentials, node_modules/, vendor/, dist/, минифицированных файлов и lock-файлов не отправляется никогда. Имена — отдельный случай: структура проекта называет uv.lock и pyproject.toml намеренно (иначе модель не поймёт, чем собран проект), а имена секретов и сгенерированных каталогов из неё исключены полностью. Если это неприемлемо — оставьте flat.
  • Деградация. Любой сбой сбора контекста (нет прав, хост недоступен, лимит) не ломает ревью: оно проходит по диффу, а в лог пишется причина.
  • Комментарии — только по диффу. Модели прямо сказано не оставлять инлайн-комментарии к окружающему коду; если проблема видна только там, она уходит в общий комментарий.

Как подобрать бюджеты. Каждое ревью пишет в лог одну строку:

review context for connection main, owner/repo#7: depth=deep; diff=3 files/4821 chars (~1205 tokens);
context=8 entries/37120 chars (~9280 tokens): bodies=3 extra_files=4 signatures=7 tests=12 unparsed=1;
requests=9 truncated=False took=1.83s

По ней видно, во что обходится ревью: diff — размер самого PR, context — добавка, requests — сколько запросов ушло хостингу, truncated — упёрлись ли в бюджет. Если truncated=True часто, а токенов не жалко — поднимайте CONTEXT_MAX_CHARS; если context съедает больше, чем сам дифф, — опускайте.

Переход с 1.x на 2.0

Формат конфигурации изменился, и старый файл намеренно не принимается: приложение не стартует и подсказывает, что переехало. Значения переносятся так:

Было (1.x) Стало (2.0)
GIT_PROVIDER_CONFIG.GIT_PROVIDER GIT_PROVIDERS.<ключ>.GIT_PROVIDER
GIT_PROVIDER_CONFIG.GIT_PROVIDER_USER_TOKEN GIT_PROVIDERS.<ключ>.GIT_PROVIDER_USER_TOKEN
GIT_PROVIDER_CONFIG.GIT_PROVIDER_URL GIT_PROVIDERS.<ключ>.GIT_PROVIDER_URL
GIT_PROVIDER_CONFIG.GIT_PROVIDER_SECRET_TOKEN GIT_PROVIDERS.<ключ>.GIT_PROVIDER_SECRET_TOKEN
GIT_PROVIDER_CONFIG.GIT_PROVIDER_REVIEWER GIT_PROVIDERS.<ключ>.GIT_PROVIDER_REVIEWER
AI_PROVIDER_CONFIG.AI_PROVIDER AI_PROVIDERS.<имя>.AI_PROVIDER + DEFAULT_AI_PROVIDER: <имя>
AI_PROVIDER_CONFIG.AI_PROVIDER_* AI_PROVIDERS.<имя>.AI_PROVIDER_*
REVIEW_MODE, SYSTEM_PROMPT, MAX_CONCURRENT_REVIEWS остались глобальными, их можно переопределить в подключении
HTTP_CLIENT_*, LOG_*, APP_* без изменений

Дополнительно нужно поменять URL вебхука в настройках Git-провайдера: вместо /webhooks/github, /webhooks/gitea или /webhooks/bitbucket теперь /webhooks/<ключ>. Старых маршрутов больше нет.

GitLab поддержан с версии 2.1.0, переносить для него ничего не нужно: добавьте подключение с GIT_PROVIDER: "gitlab" и пропишите его URL вебхука.

Обновление

Для обновления достаточно выполнить команду:

sudo docker compose pull && sudo docker compose up -d

Ревью в пайплайне

Серверный режим требует входящего HTTPS-адреса, постоянно работающего хоста и отдельной учётной записи для бота. Если этого нет или не хочется — ReVu запускается прямо в пайплайне: код уже склонирован, событие само сообщает, какой это PR, а токен уже выдан job'у.

Тот же образ, другая команда: revu review вместо сервера.

GitHub Actions

Самый короткий путь — готовый экшен: он делает чекаут сам (с fetch-depth: 0, без которого сравнивать не с чем) и запускает образ.

name: ReVu

on:
  pull_request:
    types: [opened, synchronize, reopened]
    paths-ignore:            # экономия: документация ревью не требует
      - "**/*.md"
      - "docs/**"

concurrency:                 # новый push отменяет предыдущее ревью
  group: revu-${{ github.event.pull_request.number }}
  cancel-in-progress: true

jobs:
  review:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write   # без этого GITHUB_TOKEN не сможет комментировать
    steps:
      - uses: proDreams/revu@v2
        with:
          ai-api-key: ${{ secrets.REVU_AI_KEY }}
          ai-model: gpt-4o-mini
          # format: github-annotations   # пометки ещё и прямо в «Files changed»
          # context-depth: deep          # читать окружение изменённых функций
          # fail-on: error               # падать, если ревью не состоялось

Входы экшена называются так же, как переменные из таблицы ниже (ai-model — это AI_PROVIDER_MODEL), плюс image (по умолчанию ghcr.io/prodreams/revu:latest), checkout: false, fail-on, format и args — для всего остального.

Если экшен не подходит (или хостинг не GitHub) — то же самое шагом docker run. В контейнере job'а actions/checkout использовать нельзя: это JS-экшен, которому нужен Node, а в образе ReVu его нет. Поэтому чекаут делает обычный actions/checkout, а образ запускается шагом:

name: ReVu

on:
  pull_request:
    types: [opened, synchronize, reopened]
    paths-ignore:            # экономия: документация ревью не требует
      - "**/*.md"
      - "docs/**"

concurrency:                 # новый push отменяет предыдущее ревью
  group: revu-${{ github.event.pull_request.number }}
  cancel-in-progress: true

jobs:
  review:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write   # без этого GITHUB_TOKEN не сможет комментировать
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0     # нужен общий предок: иначе сравнивать не с чем
      - name: ReVu
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          AI_PROVIDER_API_KEY: ${{ secrets.REVU_AI_KEY }}
          AI_PROVIDER_MODEL: gpt-4o-mini
        run: |
          docker run --rm -v "$PWD:/repo" -w /repo \
            -e GITHUB_TOKEN -e AI_PROVIDER_API_KEY -e AI_PROVIDER_MODEL \
            ghcr.io/prodreams/revu:2.5.0 review

GitLab CI

Контейнер здесь родная модель, а CI_JOB_TOKEN доступен сразу:

revu:
  stage: test
  image: ghcr.io/prodreams/revu:2.5.0
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  variables:
    GIT_DEPTH: 0                      # нужна история до общего предка
    AI_PROVIDER_API_KEY: $REVU_AI_KEY
    AI_PROVIDER_MODEL: gpt-4o-mini
    # GIT_PROVIDER_USER_TOKEN: $REVU_PROJECT_TOKEN   # чтобы писать комментарий в MR
  script:
    - revu review --format gitlab-codequality
  artifacts:
    when: always                      # отчёт нужен и когда job упал
    reports:
      codequality: revu-codequality.json

Про токен: CI_JOB_TOKEN умеет читать merge request и его заметки, но не писать их. Поэтому либо задайте GIT_PROVIDER_USER_TOKEN (project access token с правом api) — и ревью уйдёт ещё и комментарием в MR, либо не задавайте: замечания попадут в отчёт Code Quality, который GitLab покажет в виджете MR, а сам текст ревью — в лог пайплайна. Отчёт не создаёт ни одного комментария и не требует прав на запись вообще.

Gitea Actions

Формат тот же, что у GitHub, отличается расположение воркфлоу и то, что хостинг лучше назвать явно: Gitea Actions повторяет переменные GitHub, и по ним Gitea не всегда отличим от github.com.

# .gitea/workflows/revu.yaml
name: ReVu

on:
  pull_request:

jobs:
  review:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: ReVu
        env:
          GIT_PROVIDER: gitea
          GIT_PROVIDER_URL: https://git.example.com
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          AI_PROVIDER_API_KEY: ${{ secrets.REVU_AI_KEY }}
          AI_PROVIDER_MODEL: gpt-4o-mini
        run: |
          docker run --rm -v "$PWD:/repo" -w /repo \
            -e GIT_PROVIDER -e GIT_PROVIDER_URL -e GITHUB_TOKEN \
            -e AI_PROVIDER_API_KEY -e AI_PROVIDER_MODEL \
            ghcr.io/prodreams/revu:2.5.0 review

Переменные окружения

Хостинг ReVu определяет сам по переменным GITHUB_ACTIONS, GITLAB_CI и GITEA_ACTIONS, а репозиторий, номер PR и ревизии берёт из переменных платформы (GITHUB_EVENT_PATH, CI_MERGE_REQUEST_IID, CI_MERGE_REQUEST_DIFF_BASE_SHA и им подобных). Явно задать нужно только ключ ИИ.

Переменная Что задаёт
AI_PROVIDER вендор: openai (по умолчанию), openai_compatible, gigachat, yandexgpt
AI_PROVIDER_API_KEY ключ провайдера (обязательно)
AI_PROVIDER_MODEL модель (обязательно)
AI_PROVIDER_BASE_URL свой endpoint для openai_compatible
AI_PROVIDER_SCOPE, AI_PROVIDER_FOLDER_ID для GigaChat и YandexGPT
GIT_PROVIDER хостинг: github, gitea, gitlab, bitbucket
GIT_PROVIDER_URL адрес self-hosted хостинга
GIT_PROVIDER_USER_TOKEN токен для чтения и комментирования; иначе берётся токен платформы
REVIEW_MODE inline (по умолчанию) или comment
CONTEXT_DEPTH, CONTEXT_MAX_FILES, CONTEXT_MAX_CHARS глубина контекста и её бюджеты
INCLUDE_PR_DISCUSSION передавать ли ИИ комментарии других участников PR
LOG_LEVEL DEBUG, INFO (по умолчанию), WARNING, ERROR

Имена те же, что в settings.yaml, поэтому знание файла переносится без изменений. Валидация тоже общая: опечатка в REVIEW_MODE останавливает job с тем же сообщением, с каким остановила бы сервер.

Флаги

Флаг Что делает
--diff-source git|api|file откуда брать изменения: git — из чекаута (по умолчанию), api — через хостинг, file — из --diff-file
--diff-file PATH готовый diff; включает --diff-source file
--base, --head ревизии для сравнения, если платформа их не сообщила
--repo, --pr репозиторий и номер PR — для запуска вне CI
--provider хостинг; вне CI обязателен
--depth flat|deep переопределяет CONTEXT_DEPTH
--comment / --stdout комментарий в PR или печать в лог; по умолчанию — комментарий, если токен это позволяет
--format text|json|gitlab-codequality|github-annotations что добавить к выводу: text (по умолчанию), машиночитаемый json, отчёт Code Quality или аннотации в «Files changed»
--output PATH куда писать отчёт (по умолчанию revu-codequality.json)
--fail-on never|error|findings когда ронять job: никогда (по умолчанию), если ревью не состоялось, если есть замечания

Коды возврата: 0 — всё хорошо, 1 — сработал --fail-on findings, 2 — ревью не удалось запустить (в том числе из-за ошибки в аргументах).

Локальный запуск

Тот же CLI работает и на машине разработчика — без CI, вебхука и сервера:

export AI_PROVIDER_API_KEY=...
export AI_PROVIDER_MODEL=gpt-4o-mini
export GIT_PROVIDER=github
export GIT_PROVIDER_USER_TOKEN=...

revu review --base origin/main --head HEAD --stdout

Что стоит знать

  • fetch-depth: 0 обязателен. При shallow-клоне общего предка нет, git diff base...head вернёт пустоту, и ревью выглядело бы так, будто PR ничего не меняет. ReVu это ловит и останавливается с подсказкой, а не ревьюит пустоту.
  • В deep контекст бесплатен. Файлы читаются с диска, дерево — одной командой git ls-tree, запросов к хостингу нет вовсе; на сервере тот же режим стоил бы до 12 запросов.
  • PR из форков не поддерживаются. Ревью запускается для PR внутри репозитория: у PR из форка нет ни секретов, ни права комментировать, а pull_request_target требует отдельной осторожности.
  • Сервер и пайплайн на одном репозитории — два ревью на push. Выберите что-то одно.
  • --fail-on по умолчанию never: пайплайн не краснеет из-за мнения модели. Нужно наоборот — --fail-on error (ревью не состоялось) или --fail-on findings (есть замечания).
  • Замечания можно показать, ничего не публикуя. --format github-annotations печатает аннотации, которые GitHub показывает прямо у изменённых строк, а --format gitlab-codequality пишет отчёт для виджета MR. Ни то, ни другое не создаёт комментариев и не требует прав на запись — это ответ на read-only CI_JOB_TOKEN.
  • О неудачном ревью ReVu скажет в PR. NOTIFY_ON_FAILURE (по умолчанию включено) публикует короткий комментарий с типом ошибки, если есть токен на запись; подробности остаются в логе. Выключите, если не хотите шума.
  • Ревью всегда попадает в лог пайплайна. Если токен позволяет комментировать, оно уходит ещё и комментарием: лог читает тот, кто разбирается в пайплайне, а комментарий — автор изменения.
  • Код репозитория не исполняется. Ревью читает файлы и разбирает их парсером: ни make, ни npm install, ни pip install из ревьюируемого дерева не запускаются.

Разработка

Понадобятся Python 3.13+, uv и Docker — для сборки образа.

make install_dev   # зависимости проекта и git-хуки
make run           # запуск приложения локально
make test          # тесты с проверкой покрытия (сейчас 100%)
make lint          # ruff, форматирование и pre-commit
make typecheck     # mypy с зависимостями проекта

make typecheck — не дубль make lint: хук mypy в pre-commit работает в изолированном окружении без зависимостей проекта и не видит ошибок на границе с FastAPI, pydantic и SDK провайдеров. Именно этот запуск нашёл, например, что создание модели в Yandex AI Studio SDK синхронное, а await перед ним падал в рантайме.

Интеграционные тесты поднимают настоящее приложение и DI-контейнер, остальные работают на моках:

uv run pytest -m integration       # только интеграционные
uv run pytest -m "not integration" # только быстрые

Автор

Программа написана в рамках проекта "Код на салфетке":


Поддержка

Если вам нравится этот проект и вы хотите поддержать его дальнейшее развитие, рассмотрите возможность доната:

Ваша поддержка помогает проекту развиваться и улучшать будущие функции!


Лицензия

Этот проект распространяется под лицензией MIT. Подробности можно найти в файле LICENSE.

About

🤖 Self-hosted AI code review for your Pull Requests.

Topics

Resources

Stars

23 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages