Один ключ на всю команду
Для локального ревью каждому разработчику нужен ключ к модели — и на каждого нового сотрудника приходится заводить ещё один. Есть способ проще: CLI разработчика к модели не обращается вовсе, а отправляет изменения боту, и ревью проводит бот — с тем же ключом, с которым уже проверяет MR.
Нового пропуска разработчику не нужно: бот пускает его по личному токену GitLab или GitHub. Кого пускать, решает членство в группе. Добавили человека в группу — доступ появился, убрали — пропал. Своего списка пользователей у бота нет, поэтому при уходе сотрудника отзывать отдельно ничего не нужно.
Как это устроено
Ключ остаётся у бота — к нему приезжает код. CLI сразу отправляет боту дифф и изменённые файлы целиком: они понадобятся наверняка. Остальное бот запрашивает у CLI по ходу ревью, по одному файлу: .reviewgate/config.yml из рабочей копии, файлы с замечаниями и то, что они импортируют. Движок у CLI и у бота один и тот же, так что ревью получается таким же, как на MR: код уходит к тем же моделям, а находки проверяет тот же судья. У самого бота после прогона не остаётся ничего: сессия живёт в памяти, пока открыто соединение, а в логах и метриках нет ни строчки кода.
Локальные прогоны не займут бота целиком. Прогонам разработчиков бот отдаёт только часть своих слотов, по умолчанию один из трёх, а остальные держит в резерве для MR: их локальные прогоны не займут, сколько бы разработчиков ни запустило ревью одновременно. Если свободного слота нет, локальный прогон ждёт его, по умолчанию минуту, а после истечения времени получает отказ server_busy. Увеличить ожидание можно переменной окружения бота REVIEW_CLI_QUEUE_WAIT_MS (в миллисекундах). У одного разработчика одновременно идёт только один прогон: пока он не закончится, бот сразу отклоняет новые.
Брошенный прогон останавливается. Если разработчик нажал Ctrl-C или прогон упёрся в предел сессии (по умолчанию 15 минут, REVIEW_CLI_SESSION_TIMEOUT_MS), бот обрывает запросы к модели, а не доводит ревью до конца за счёт компании. В метриках такой прогон получает статус cancelled. Пока ревью идёт, CLI показывает только, сколько прошло времени: отчёт приходит целиком в конце, а пульс каждые 15 секунд нужен, чтобы прокси не оборвали молчащее соединение.
Чего бот не видит: истории git и других репозиториев — файлы за пределами репозитория CLI не отдаёт вовсе. Личный токен GitLab или GitHub нужен боту лишь для проверки допуска: кто это, состоит ли человек в группе и видит ли проект. Бот его не хранит, в логи не пишет и модели не отправляет.
Что решить при настройке
Настройка небольшая: несколько переменных в окружении бота и три строки в конфиге разработчика, всё со значениями по умолчанию — в справочнике. Здесь — только то, что придётся решить вам.
Кому давать доступ. Это решают группы GitLab, перечисленные через запятую в REVIEWGATE_CLI_GROUPS. Членство в GitLab наследуется сверху вниз. Назовёте подгруппу — пройдут и её участники, и участники родительских групп. Назовёте родительскую — пройдут только её участники, а тех, кто добавлен лишь в подгруппу, бот не пустит. По той же причине исключение из подгруппы не закроет доступ тому, кто состоит в родительской. Роль в группе не важна, но в закрытом проекте нужна хотя бы Reporter. Называйте группу, которая существует не ради бота, — например, «все, у кого есть право коммита», — а не заведённую под него: такую перестают обновлять через месяц, и доступ теряет смысл.
Сколько слотов отдать разработчикам. Одновременно бот ведёт не больше REVIEW_CONCURRENCY ревью — MR и локальные прогоны вместе. Сколько из них могут быть локальными, задаёт REVIEW_CLI_CONCURRENCY; остальные слоты разработчикам недоступны и всегда остаются за MR. По умолчанию слотов три, и локальным прогонам из них достаётся один. Чем больше слотов вы отдадите разработчикам, тем реже они будут ждать, но тем меньше запас для MR. Значение REVIEW_CLI_CONCURRENCY должно быть меньше REVIEW_CONCURRENCY хотя бы на единицу, иначе бот не запустится, так что всего слотов нужно не меньше двух.
Личный токен разработчика. Токену GitLab хватит права read_api, а держать его в файле не обязательно — конфиг может получать его командой. Адрес сервера — только https://: токен уходит с каждым запросом.
server:
url: https://bot.your-company.com
gitlab_token_command: pass show gitlab/tokengitlab_token_command — команда, которую CLI запускает через shell при каждом старте; токеном считается то, что она напечатала. Подойдёт любой менеджер секретов с командной строкой: pass, связка ключей macOS (security find-generic-password -s gitlab -w), 1Password (op read …). Команда должна печатать только токен, одной строкой: если в записи pass есть что-то ещё, добавьте | head -1. Если заданы и gitlab_token, и команда, победит gitlab_token, а переменная окружения REVIEWGATE_GITLAB_TOKEN главнее обоих.
Проверить настройку можно командой reviewgate doctor в репозитории на машине разработчика. Он не просто читает конфиг, а проходит на сервере ту же проверку допуска, что и прогон, и показывает результат: кого пустили, в какой проект и откуда взят токен, а при отказе — причину. Модель бота и занятость слотов он не проверяет: их покажет первый настоящий прогон.
Учёт расходов: что есть и чего нет
Если у бота подключён Postgres (DATABASE_URL), каждый прогон через сервер попадает в таблицу метрик: логин разработчика, модели, токены и исход. Строки хранятся 90 дней (METRICS_RETENTION_DAYS). «Кто сколько потратил за месяц» — один SQL-запрос, но считать придётся в токенах: денег в таблице нет. Две дыры стоит знать заранее: у брошенных (cancelled) и упавших прогонов токенов в строке нет, хотя модель к тому моменту могла уже поработать, а прогоны с --local на своём ключе не пишутся вовсе.
Лимита расходов на человека нет — есть только правило «один прогон за раз». Бюджет в продукте есть только на прогон: LLM_BUDGET_TOKENS в окружении бота, по умолчанию выключен. На 80 и 100 % лимита бот предупреждает в отчёте, а останавливает ревью только при трёхкратном превышении: это защита от катастрофы вроде зациклившегося агента, а не от переплаты. Квот вида «этому человеку пять ревью в день» нет, как нет и дашборда: метрики — это таблица в Postgres, а примеры запросов есть в справочнике.
И модель выбирает разработчик. Бот берёт .reviewgate/config.yml из рабочей копии, вместе с незакоммиченными правками, а этот файл задаёт модели, судью и состав ансамбля. Платит за них ключ бота, так что правку «дам себе модель посильнее» сервер не остановит. Заметить её можно только задним числом, по колонкам моделей в метриках.
Рычагов у вас два, и оба грубые. Первый — группа: уберите человека из неё, и следующий прогон ему уже не дадут, а идущий доработает. Но если группа нужна не только боту, вместе с доступом к серверу человек потеряет и всё, что она даёт. Второй рычаг — вне продукта: лимит расходов на сам ключ бота в консоли провайдера, если провайдер такой даёт; но он остановит всё сразу, включая ревью MR.
Как подключить всю команду
Ключ решён — осталось, чтобы ревью вообще запускалось на машинах людей. Для этого хуки кладут в репозиторий: папку с ними коммитят, как любой код, и направляют в неё git настройкой core.hooksPath. Эту настройку git из репозитория не берёт, её нужно включить в каждом клоне: git config core.hooksPath .githooks. В JS-проектах это делает husky — скрипт prepare включает хуки при ближайшей установке зависимостей, так устроено и у нас. В других стеках ту же команду кладут в шаг настройки проекта. Тогда ревью приезжает вместе с кодом, а не с инструкцией, которую никто не читает.
Чтобы хуки не мешали работать, им нужны два свойства. Первое: если CLI не установлен, хук пропускает коммит, а не падает — коллега, который ещё не поставил CLI, не должен упираться в ваш хук:
command -v reviewgate >/dev/null 2>&1 || {
echo "reviewgate: binary not found — commit allowed unchecked" >&2
exit 0
}Второе: любой исход, кроме «найдены блокирующие замечания», тоже пропускает коммит. Не отвечает сервер, протух токен, сервер занят или у разработчика уже идёт другой прогон — CLI завершается с кодом 1, а хук пишет об этом строку в stderr и выходит с нулём. Коммит останавливает только код 2. Это осознанный fail-open: сломанное ревью не должно блокировать команду. Но поэтому и пропущенный коммит не доказывает, что ревью прошло: отличить одно от другого можно только по строке в stderr.
Дальше
- Справочник по серверу ревью— все переменные и занятость слотов в реальном времени
- Лесенка хуков— какие хуки и с какими порогами
- Куда уходят токены— сколько стоит прогон, когда за все платит ключ бота