Skip to main content
Glama

gitl

Action self-test

ИИ-ревьюер истории git для CLI и CI. gitl (git-log-lens) читает историю git репозитория и превращает её в структурированный инженерный артефакт с помощью LLM:

  • gitl review <range> — ИИ-ревью диапазона коммитов / PR с машиночитаемой оценкой риска (low|medium|high) для гейтинга в CI (--fail-on=high → код выхода 2); потоковая передача токенов в терминал в реальном времени; кэш ответов LLM на диске с опциональным общим удалённым кэшем для CI; пользовательские шаблоны системного промпта; --staged ревьюит staged (незакоммиченные) изменения перед git commit (также доступно как pre-commit hook).

  • gitl changelog [<range>] — журнал изменений в стиле Keep a Changelog, сгруппированный по conventional commits (по умолчанию от последнего тега до HEAD); детерминированный по умолчанию, --ai опционально переписывает его моделью в читаемый текст релиз-ноутов;

  • gitl digest [--days=N] [--repos=a,b,c] — сводка активности по автору/теме/файлу, включая несколько репозиториев параллельно; интерактивный TUI-просмотрщик (--tui).

Чистый CLI-бинарник плюс обёртка для GitHub Action — без сервера, без базы данных, без хостинга ключей. BYOK (принеси свой ключ) с поддержкой нескольких провайдеров: OpenAI-совместимый API, Ollama (локальный/самохостинг), Azure OpenAI, нативный Anthropic (Claude), Google Gemini. Без телеметрии.

Статус: выпущена v0.6.2 — все три команды работают на реальных репозиториях со всеми тремя форматами вывода (md|text|json). Action публикует ИИ-ревью как закреплённые комментарии к PR и гейтит по оценке риска. Релизные бинарники кросс-скомпилированы, подписаны cosign и покрыты SLSA L3 provenance сборки (см. VERIFY.md).

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

Требуется Go 1.22+ и git в PATH.

# build
go build ./...

# AI review of a commit range — streams tokens to the terminal in real time
GITL_API_KEY=sk-... go run ./cmd/gitl review HEAD~5..HEAD

# no key = deterministic offline review (heuristic risk, no network call)
go run ./cmd/gitl review HEAD~5..HEAD

# review staged (not yet committed) changes before `git commit`
go run ./cmd/gitl review --staged

# review a GitHub PR by number — requires the `gh` CLI (installed + authenticated);
# resolves base/head via gh, fetches `pull/N/head` locally when needed, and reviews
# the merge-base diff (base...head), same as GitHub shows
go run ./cmd/gitl review pr/42

# machine-readable output for CI + risk gating
go run ./cmd/gitl review HEAD~5..HEAD --format=json
go run ./cmd/gitl review HEAD~5..HEAD --fail-on=high   # exit code 2 on high risk
# exit codes: 0 = ok (risk below --fail-on), 1 = tool/runtime error (git/LLM/
# config failure), 2 = the --fail-on risk gate triggered — CI can branch on 2

# estimate cost without making an API call
go run ./cmd/gitl review HEAD~5..HEAD --dry-run

# custom system-prompt template (e.g. your team's review policy) — set via
# config only (prompt.system_template_file); there is no --system-template flag
# see Configuration → Custom templates below

# skip the on-disk LLM cache (always call the model)
go run ./cmd/gitl review HEAD~5..HEAD --no-cache

# disable streaming (non-interactive, buffered output)
go run ./cmd/gitl review HEAD~5..HEAD --no-stream

# suppress the informational offline-mode notice on stderr (errors and the
# review output are unaffected) — also via GITL_QUIET=1 or output.quiet: true
go run ./cmd/gitl review HEAD~5..HEAD --quiet

# changelog from last tag (or full history if no tags) — no LLM by default
go run ./cmd/gitl changelog
go run ./cmd/gitl changelog v1.2.0..HEAD --format=json

# AI changelog: the model rewrites the grouped result as release-note prose and
# reclassifies significant non-conventional commits out of "Other". Without an API
# key (or on a malformed model response) it falls back to the deterministic
# changelog with a warning — never fails. --dry-run/--max-cost-usd/--no-cache work
# the same as for review.
GITL_API_KEY=sk-... go run ./cmd/gitl changelog --ai

# activity summary for the last N days — no LLM
go run ./cmd/gitl digest --days=14

# multi-repo digest: runs in parallel; one unreachable repo does not fail the rest
go run ./cmd/gitl digest --repos=../service-a,../service-b --format=json

# interactive TUI viewer for digest (requires a TTY)
go run ./cmd/gitl digest --days=14 --tui

go run ./cmd/gitl version
go run ./cmd/gitl --help

# tests
go test ./...

Установка:

# Go toolchain
go install github.com/akomyagin/gitl/cmd/gitl@latest

# Homebrew (macOS/Linux)
brew install akomyagin/tap/gitl

# npm — downloads the prebuilt binary for your platform from GitHub Releases
# and verifies its SHA256 checksum (no Go toolchain needed).
npx gitl-cli review HEAD~5..HEAD   # or: npm install -g gitl-cli

# Or download a signed release binary from GitHub Releases (see VERIFY.md)

Дополнения для оболочки

gitl поставляет сгенерированные cobra дополнения для bash, zsh, fish и PowerShell.

Homebrew устанавливает дополнения для bash/zsh/fish автоматически (релизные архивы также содержат их в completions/). В противном случае включите их по требованию:

# bash (current shell)
source <(gitl completion bash)
# bash (persistent) — Linux
gitl completion bash > /etc/bash_completion.d/gitl
# zsh (persistent)
gitl completion zsh > "${fpath[1]}/_gitl"
# fish
gitl completion fish > ~/.config/fish/completions/gitl.fish
# PowerShell
gitl completion powershell | Out-String | Invoke-Expression

Флаги с фиксированными наборами значений — --format (md|text|json), --fail-on (never|low|medium|high) и --provider — дополняют свои допустимые значения.

Локальный тест с несколькими провайдерами (Ollama)

docker-compose.yml запускает только dev-зависимость — локальный экземпляр Ollama для тестирования мультипровайдерного LLM-клиента (gitl сам не контейнеризирован):

docker compose up ollama

Related MCP server: grippy-code-review

Конфигурация

Быстрый путь: gitl init записывает стартовый .gitl.yaml с комментариями в корень репозитория (отказываясь перезаписывать существующий без --force; --output записывает в другое место). Отредактируйте его вместо копирования из этого раздела — остальное ниже является полным справочником.

Два уровня, объединяются по приоритету: флаг > env > .gitl.yaml (репозиторий) > ~/.config/gitl/config.yaml (личный). Репозиторный .gitl.yaml коммитится как общая командная политика (порог риска, исключённые пути, категории журнала изменений). Без ключа gitl работает в детерминированном офлайн-режиме.

В офлайн-режиме — или когда реальная модель пропускает валидный блок риска и gitl откатывается к эвристике — заголовок риска помечается *(heuristic)* (и "heuristic": true в --format=json), чтобы детерминированная оценка никогда не принималась за собственное суждение модели.

Провайдеры (llm.provider)

# OpenAI-compatible API (default)
llm:
  provider: "openai"
  api_key: ""            # or env GITL_API_KEY
  base_url: "https://api.openai.com/v1"
  model: "gpt-4o-mini"

# Ollama — local/self-hosted, no key, free
llm:
  provider: "ollama"
  base_url: "http://localhost:11434/v1"
  model: "llama3.1"

# Azure OpenAI — custom auth/endpoint format
llm:
  provider: "azure_openai"
  api_key: ""             # or env GITL_API_KEY
  model: "gpt-4o-mini"    # used only for cost estimation
  azure_openai:
    endpoint: "https://<resource>.openai.azure.com"
    deployment: "<deployment-name>"
    api_version: "2024-08-01-preview"

# Anthropic (native Claude Messages API)
llm:
  provider: "anthropic"
  api_key: ""            # or env GITL_API_KEY
  model: "claude-sonnet-4-6"
  # base_url optional; defaults to https://api.anthropic.com

# Google Gemini (Google AI Studio)
llm:
  provider: "gemini"
  api_key: ""            # or env GITL_API_KEY
  model: "gemini-2.5-flash"
  # base_url optional; defaults to https://generativelanguage.googleapis.com/v1beta

Потоковая передача (output.stream)

При интерактивном ревью (md или text формат на TTY) gitl передаёт токены в терминал по мере их поступления — без ожидания полного ответа. Потоковая передача включена по умолчанию и автоматически отключается в CI (stdout не TTY), при --format=json и при настройке пользовательского output.template_file (шаблону нужен полный ответ, поэтому ревью буферизуется и рендерится через него).

Потоковая передача в настоящее время реализована только для OpenAI-совместимого провайдера (openai / ollama / azure_openai). С нативным провайдером anthropic или gemini gitl прозрачно выдаёт то же ревью как единый буферизованный ответ (без посимвольного вывода) независимо от output.stream / --no-stream.

output:
  stream: true   # default; set false to always buffer

Отключить для одного вызова: gitl review HEAD~5..HEAD --no-stream

Цвет (output.color)

На интерактивном терминале gitl review раскрашивает уровень риска в заголовке (HIGH красным, MEDIUM жёлтым, LOW зелёным). Цвет автоматически отключается, когда stdout не является TTY (пайпы, логи CI), и никогда не появляется в выводе --format=json. Приоритет, от высшего к низшему:

  1. Установлена переменная окружения NO_COLOR (любое значение, даже пустое) — цвет выключен (no-color.org);

  2. output.color: false в конфиге (или GITL_OUTPUT_COLOR=false) — цвет выключен;

  3. stdout не является TTY — цвет выключен;

  4. в противном случае — цвет включён.

output:
  color: true   # default; set false to disable ANSI color

Тихий режим (output.quiet)

Без API-ключа review выводит информационное уведомление "using deterministic offline review" в stderr при каждом запуске (а changelog --ai выводит аналогичное уведомление об откате). В заведомо офлайн-контекстах — особенно в pre-commit hook, который срабатывает при каждом коммите — этот баннер является шумом. Подавите его любым из способов (каждый слой может независимо включить подавление):

  1. флаг --quiet для review / changelog;

  2. установленная переменная окружения GITL_QUIET (любое значение, даже пустое);

  3. output.quiet: true в конфиге (или GITL_OUTPUT_QUIET=true).

--quiet подавляет только информационный баннер: ошибки, отрендеренное ревью/журнал изменений на stdout и гейт --fail-on никогда не затрагиваются.

output:
  quiet: false   # default; set true to suppress the offline notices

Кэш ответов LLM (cache)

gitl review кэширует ответы модели на диске (SHA-256 от провайдера + модели + промпта). Идентичные диффы мгновенно используют кэшированный результат без вызова API и затрат.

cache:
  enabled: true    # default
  ttl_hours: 24    # entries older than this are ignored

Кэш хранится в ~/.cache/gitl/review/ (совместимо с XDG). Отключить для одного вызова: gitl review HEAD~5..HEAD --no-cache

В --format=json каждый артефакт ревью несёт аддитивные метаданные запуска (schema_version остаётся 1; потребители, созданные до этого, видят тот же документ плюс два новых ключа):

{
  "duration_ms": 1234,
  "cache": { "hit": true, "tier": "local" }
}
  • duration_ms — время всего запуска ревью в миллисекундах (попадание в кэш всё равно сообщает реальное, обычно крошечное число).

  • cache.hit — было ли это ревью обслужено из кэша ответов LLM вместо свежего вызова модели.

  • cache.tier — топология кэша, действующая для запуска: none (офлайн-режим, --no-cache, cache.enabled: false или ttl_hours <= 0), local (только диск) или tiered (диск + удалённый). Сообщает сконфигурированный режим, а не то, какой бэкенд обслужил конкретное попадание.

Намеренно нет поля usage (количество токенов): gitl не парсит использование провайдера из ответов, и постоянно пустое поле было бы хуже, чем отсутствующее. Оно будет добавлено — аддитивно, без изменения схемы — когда появится парсинг использования.

Общий удалённый кэш (cache.remote) — opt-in

Opt-in, выключен по умолчанию, BYO-бэкенд: gitl никогда не хостит сервис и не делает сетевых запросов к какому-либо кэшу, пока вы его не настроите. Полезно для холодных стартов CI — каждый раннер начинает с пустого диска, но общая HTTP KV-конечная точка позволяет одному раннеру переиспользовать ревью другого для того же диффа.

cache:
  enabled: true
  ttl_hours: 24
  remote:                     # opt-in shared cache for CI cold starts (off by default)
    url: https://cache.example.com/gitl   # your endpoint; gitl hosts nothing
    token_env: GITL_REMOTE_CACHE_TOKEN    # env var holding an optional bearer token
    timeout_ms: 3000

При настройке локальный дисковый кэш остаётся первым уровнем: чтение проверяет диск, затем удалённый (попадание в удалённый кэш записывается на диск); запись идёт в оба.

Протокол — простое хранилище ключ-значение по HTTP — подойдёт любой статический объектный стор или крошечный обработчик:

  • GET {url}/{key} → 200 с телом JSON-записи, или 404 = промах. Любой другой статус, сетевая ошибка или таймаут считаются промахом.

  • PUT {url}/{key} с JSON-записью в теле запроса (Content-Type: application/json) → любой 2xx = сохранено.

  • Если token_env указывает на переменную окружения с непустым значением, оба запроса несут Authorization: Bearer <token>. Сам токен никогда не читается из файла конфигурации (та же дисциплина, что и GITL_API_KEY).

  • Ключи — 64-символьные hex-строки SHA-256; значения непрозрачны для сервера.

Контракт безопасности: любой удалённый сбой (таймаут, 5xx, недоступная конечная точка) молча деградирует до локального кэша / без кэша — он никогда не приводит к сбою ревью. Хранимые записи содержат только ответ модели, ключ — непрозрачный хэш: ни дифф, ни текст промпта никогда не попадают в удалённый кэш. Записи старше ttl_hours игнорируются на стороне клиента независимо от того, что возвращает сервер.

Тренд риска (policy.risk_log_enabled)

Каждый запуск gitl review добавляет свой результат риска (уровень, диапазон, провайдер, временная метка) в локальный JSONL-лог: $XDG_DATA_HOME/gitl/risk-history.jsonl (по умолчанию ~/.local/share/gitl/risk-history.jsonl; %AppData%\gitl\ на Windows). gitl digest читает его и показывает секцию "Risk trend (last N days)" для каждого репозитория — количество ревью по уровням, направление высокого риска (последняя половина окна против более ранней) и последние несколько ревью. В --format=json это появляется как опциональное поле risk_trend (schema_version остаётся 1; потребители, созданные до этого, видят тот же документ, что и раньше). Репозитории без истории просто опускают секцию.

Ревью соотносятся с репозиторием по URL удалённого origin (при отсутствии origin — по пути рабочей копии).

Ограничение: история локальна для вашей машины — она не сохраняется между раннерами CI (каждый начинает с холодного диска), поэтому тренд — это функция для локальной разработки, а не для CI.

Отказ в конфиге (без CLI-флага):

policy:
  risk_log_enabled: false

Пользовательские шаблоны (prompt.*_template_file / output.template_file)

Независимые переопределения только через конфиг (ни для одного из них нет CLI-флага):

  • prompt.system_template_file — ваш собственный системный промпт для ревью, чтобы направить фокус модели (чек-лист безопасности, архитектурные ограничения, командные правила). Используется только gitl review:

    prompt:
      system_template_file: "./review-policy.md"   # path relative to CWD

    Шаблон системного промпта ревью имеет доступ к {{ .Commits }}, {{ .Diff }}, {{ .Range }}, {{ .Staged }} (см. internal/prompt/templates.go).

  • prompt.changelog_system_template_file — ваш собственный системный промпт для журнала изменений, используется только gitl changelog --ai:

    prompt:
      changelog_system_template_file: "./changelog-policy.md"   # path relative to CWD

    Шаблон системного промпта журнала изменений имеет доступ к {{ .Commits }}, {{ .Range }}, {{ .Grouped }} — не {{ .Diff }}: changelog --ai работает с метаданными коммитов, диффа нет, и шаблон в форме ревью, использующий .Diff, здесь не сработает. Именно поэтому два ключа раздельны: каждая команда читает только свой ключ, и любой из них может быть установлен без другого.

  • output.template_file — ваш собственный шаблон рендера в формате md для готового артефакта ревью:

    output:
      template_file: "./review-output.tmpl"   # path relative to CWD

    Шаблон вывода имеет функции рендера из internal/render/render.go (render.TemplateFuncs()).

Примечание о доверии: ключи prompt.*_template_file/output.template_file могут быть установлены репозиторным .gitl.yaml, а не только вашим личным конфигом — поэтому запуск gitl review против клонированного репозитория, которым вы не управляете, может указать на шаблон внутри того же репозитория. Это предполагаемый механизм для общей командной политики ревью, а не ошибка: text/template здесь не может читать произвольные файлы или выполнять код, но относитесь к .gitl.yaml недоверенного репозитория с той же осторожностью, что и к его .git/hooks или скриптам сборки.

GitHub Action

gitl можно подключить как GitHub Action: он ИИ-ревьюит коммиты pull request и публикует комментарий с оценкой риска, опционально блокируя слияние выше порога. Action собирает gitl из исходников (go install на закреплённой версии). Также доступен в GitHub Marketplace, если вы предпочитаете добавить его оттуда.

Добавьте .github/workflows/gitl-review.yml в ваш репозиторий:

name: gitl review
on:
  pull_request:

permissions:
  contents: read          # for checkout
  pull-requests: write    # to post the review comment

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          fetch-depth: 0    # required: without full history base..head won't resolve

      - uses: akomyagin/gitl@v0.6.2
        with:
          gitl-api-key: ${{ secrets.GITL_API_KEY }}   # BYOK, see below
          fail-on: high                               # optional: block merge on high risk

Рекомендации по безопасности:

  • Ключ — только через secrets.*. gitl-api-key берётся из secrets.GITL_API_KEY (задаётся в Settings → Secrets and variables → Actions) и никогда не захардкожен в YAML и не закоммичен. Если секрет не задан, Action работает в детерминированном offline-режиме (без сети, без затрат).

  • Минимальные permissions:. Нужны только pull-requests: write (для публикации комментария) и contents: read (для checkout) — не выдавайте более широких прав.

  • fetch-depth: 0 обязателен. GitHub предоставляет SHA-хэши base/head в событии pull_request, но при shallow-клоне не получится разрешить base.sha..head.sha.

  • fail-on по умолчанию равен never. Action только оставляет комментарий; он не блокирует merge, если вы явно не включите это (fail-on: high и т. п.) — тот же принцип «WARN по умолчанию, жёсткий гейт — явный opt-in», что и в CLI (--fail-on). Когда гейт срабатывает, job завершается с кодом выхода gitl 2 (risk gate) — настоящая ошибка инструмента завершается с кодом 1, так что downstream-шаги могут отличить «рискованное изменение» от «gitl сломался».

  • Конфиденциальность диффа. В CI дифф отправляется тому LLM-провайдеру, который настроен (по умолчанию: OpenAI-совместимый API). Для приватного кода используйте self-hosted/enterprise-провайдера (Ollama, Azure OpenAI) — см. Providers выше.

  • Выбор провайдера. По умолчанию Action использует провайдера из вашего конфига (OpenAI-совместимый, если не задан). Чтобы нацелиться на нативного провайдера, передайте provider: (openai|ollama|azure_openai|anthropic|gemini), и опционально model: и base-url:, вместе с gitl-api-key:. Все три параметра необязательны и, если опущены, подхватываются из вашего .gitl.yaml/личного конфига и встроенных умолчаний gitl — см. Providers выше. Пример: provider: anthropic с ключом Claude в secrets.GITL_API_KEY.

  • Маскирование секретов. GitHub автоматически маскирует значения secrets.* в логах runner'а как ***, но это не повод печатать ключ в собственных шагах workflow.

Сводка рисков в описании PR (opt-in)

С update-pr-description: true (по умолчанию false) Action дополнительно поддерживает компактный блок сводки рисков в конце описания PR — строку риска плюс ссылку на полный комментарий ревью, обновляемую при каждом запуске:

      - uses: akomyagin/gitl@v0.6.2
        with:
          gitl-api-key: ${{ secrets.GITL_API_KEY }}
          update-pr-description: true

Это opt-in, потому что редактирование описания PR более интрузивно, чем sticky-комментарий; дополнительные права не нужны — pull-requests: write, уже требуемое для комментария, покрывает и тело PR. Блок ограничен парой маркеров <!-- gitl-review-summary -->, и заменяется только текст между маркерами — всё, что вы пишете вне их, никогда не затрагивается. Пока только для GitHub (игнорируется в Gitea Actions).

Gitea Actions (экспериментально)

Тот же action.yml работает и в Gitea Actions — runner Gitea исполняет GitHub-совместимые composite-actions, и action gitl определяет платформу во время выполнения по переменной GITEA_ACTIONS=true, которую act_runner Gitea внедряет в каждый job. Единственная платформо-зависимая часть — публикация sticky-комментария к PR — идёт через REST API Gitea (POST/PATCH /api/v1/repos/{owner}/{repo}/issues/...) с помощью curl вместо CLI gh, который говорит только с API GitHub. Пользователи GitHub не затронуты: без GITEA_ACTIONS action ведёт себя ровно как раньше.

Добавьте .gitea/workflows/gitl-review.yml в ваш репозиторий (полный пример с комментариями: .gitea/workflows/gitl-review.yml в этом репозитории):

name: gitl review
on:
  pull_request:

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: https://github.com/actions/checkout@v7
        with:
          fetch-depth: 0
      - uses: https://github.com/akomyagin/gitl@v0.6.2
        with:
          gitl-api-key: ${{ secrets.GITL_API_KEY }}   # BYOK; omit for offline mode

Требования: включённые Actions, свежий act_runner (с поддержкой node24) и образ runner'а с bash, git, curl, jq и node. GITL_API_KEY кладётся в секреты Actions Gitea, никогда в YAML — те же правила BYOK, что и на GitHub.

Статус проверки — прочтите, прежде чем полагаться на это. REST-вызовы на curl (список комментариев, create, patch, sticky-детекция) были прогнаны против реального инстанса Gitea (gitea/gitea в Docker) end-to-end — list-empty → POST-create → re-list-finds-it → PATCH-update → по-прежнему ровно один комментарий. Эта часть работает как написано. Что ещё не проверено — окружающий CI-контекст act_runner: выглядят ли GITEA_ACTIONS/GITHUB_API_URL/payload события PR ровно так, как предполагается, внутри живого запуска workflow (это сверялось с исходниками Gitea/ act_runner/act-fork, а не прогонялось внутри реального job). Относитесь к CI-триггерному пути как к экспериментальному, пока кто-нибудь не подтвердит зелёный прогон end-to-end в настоящих Gitea Actions; баг-репорты с реальных инстансов очень приветствуются.

GitLab CI (экспериментально)

gitl также поставляет компонент GitLab CI/CD — templates/gitl-review.yml — зеркало GitHub Action: он устанавливает gitl через go install на закреплённой версии, ревьюит диапазон merge request'а ($CI_MERGE_REQUEST_DIFF_BASE_SHA..$CI_COMMIT_SHA), рендерит комментарий через общий платформо-нейтральный ci/comment.sh и создаёт/обновляет sticky-заметку MR через REST API GitLab (тот же маркер <!-- gitl-review -->, что и на GitHub/Gitea). Job выполняется только в пайплайнах merge request.

Компонент опубликован в каталоге GitLab CI/CD через зеркало этого репозитория, обновляемое при релизах, по адресу gitlab.com/alkom68/gitl (одностороннее GitHub → GitLab, пушится на каждый релизный тег). На gitlab.com подключайте его как компонент каталога:

# .gitlab-ci.yml (gitlab.com)
include:
  - component: gitlab.com/alkom68/gitl/gitl-review@v0.6.2
    inputs:
      fail_on: "never"      # default; set "high" to block risky MRs
      # max_cost_usd: "0.50"
      # gitl_version: "v0.6.2"

На self-hosted-инстансе GitLab include:component разрешает компоненты только с того же инстанса — потребляйте шаблон через include:remote напрямую с GitHub (inputs работают и с remote-подключением):

# .gitlab-ci.yml (self-hosted GitLab)
include:
  - remote: "https://raw.githubusercontent.com/akomyagin/gitl/v0.6.2/templates/gitl-review.yml"
    inputs:
      fail_on: "never"

Настройка — две переменные CI/CD (Settings → CI/CD → Variables, обе masked, никогда в YAML):

  • GITL_API_KEY — BYOK-ключ LLM. Необязателен: без него gitl выполняет детерминированное offline-ревью (без сети, без затрат). Достаточно определить переменную проекта — она имеет приоритет над пустым умолчанием input'а gitl_api_key компонента. Если используете input, передавайте ссылку на переменную (gitl_api_key: $MY_LLM_KEY), никогда не литеральное значение ключа: значения input'ов интерполируются в конфиг пайплайна.

  • GITL_GITLAB_TOKEN — токен для публикации заметки MR (project access token или PAT, скоуп api, роль Reporter или выше; отправляется как PRIVATE-TOKEN). Если не задан, job откатывается на CI_JOB_TOKEN (заголовок JOB-TOKEN) — но в большинстве конфигураций GitLab CI_JOB_TOKEN не авторизован для Notes API, так что откат, как ожидается, завершится ошибкой (с явным сообщением, а не тихим пропуском). Явный GITL_GITLAB_TOKEN — надёжный путь.

Полный самопроверочный пайплайн с комментариями — он же ближайший аналог полного примера использования — это .gitlab-ci-selftest.yml (запускается как .gitlab-ci.yml в зеркале этого репозитория на GitLab).

Статус проверки — прочтите, прежде чем полагаться на это. REST-вызовы GitLab (список заметок MR + sticky-маркер-детекция, POST create, PUT update) и сам YAML компонента (spec:/inputs:-интерполяция, include:local с inputs, через CI Lint API) были проверены end-to-end против реального локального инстанса GitLab CE (gitlab/gitlab-ce 19.2.0 в Docker) на настоящем merge request — list-empty → POST-create → re-list-finds-it → PUT-update → по-прежнему ровно одна заметка — с использованием ровно тех команд curl/jq, что в шаблоне. Что ещё не проверено — живой прогон пайплайна: значения CI_MERGE_REQUEST_DIFF_BASE_SHA/CI_COMMIT_SHA/CI_JOB_URL внутри реального пайплайна merge request взяты из документации GitLab, а не наблюдены, а отклонение отката на CI_JOB_TOKEN описано по документации GitLab об allowlist job-токенов, а не воспроизведено. Относитесь к пайплайн-пути как к экспериментальному, пока кто-нибудь не подтвердит зелёный end-to-end прогон; баг-репорты приветствуются.

Заметка о доверии. Компонент скачивает ci/comment.sh из зеркала GitLab (gitlab.com/alkom68/gitl) на версии gitl_version и исполняет его — без проверки контрольной суммы/подписи, та же граница доверия, что и у строки go install ...@${gitl_version} прямо над ней (тот же репозиторий, тот же ref). Эта загрузка происходит независимо от того, как подключён компонент — Catalog или include:remote — потому что подключение компонента доставляет только YAML-шаблон, а не файлы репозитория компонента, так что загрузку нельзя обойти механически. Скачивание с того же инстанса GitLab, который публикует компонент (а не с GitHub), сохраняет тот же namespace/ref — более честная модель доверия, чем кросс-хостовое скачивание. Если это важно для вашей threat model, закрепите gitl_version на SHA коммита, а не на теге (теги перемещаемы).

Bitbucket Pipelines (экспериментально)

Интеграция с Bitbucket поставляется как Pipe — а pipes по определению являются Docker-образами, так что, в отличие от action для GitHub/Gitea и компонента GitLab (обычные YAML-обёртки), это самодостаточный образ: bitbucket-pipe/Dockerfile собирает статический бинарник gitl и встраивает общий рендерер ci/comment.sh плюс entrypoint bitbucket-pipe/pipe.sh. Pipe вычисляет диапазон PR ($BITBUCKET_PR_DESTINATION_COMMIT..$BITBUCKET_COMMIT), запускает gitl review --format=json и создаёт/обновляет sticky-комментарий PR через REST API Bitbucket Cloud (тот же маркер <!-- gitl-review -->, что и на других платформах). Справочник переменных: bitbucket-pipe/pipe.yml.

Статус образа. Опубликован на Docker Hub как alkom68/gitl-review-pipe начиная с v0.5.2 — job docker-publish релизного workflow пушит :<version> и :latest на каждый релизный тег. В реестре существуют только 0.5.2 и новее: более ранние релизы предшествуют публикации (теги 0.5.0/0.5.1 никогда не пушились), так что не закрепляйте их.

# bitbucket-pipelines.yml
pipelines:
  pull-requests:
    '**':
      - step:
          name: gitl review
          clone:
            depth: full   # the default depth-50 clone may not contain the PR base commit
          script:
            - pipe: docker://alkom68/gitl-review-pipe:0.6.2
              variables:
                GITL_API_KEY: $GITL_API_KEY                    # BYOK; omit for offline review
                GITL_BITBUCKET_TOKEN: $GITL_BITBUCKET_TOKEN    # posts the PR comment
                # FAIL_ON: "high"        # default "never" — comment only, no gate
                # MAX_COST_USD: "0.50"

Настройка — две защищённые переменные репозитория/workspace (Repository settings → Pipelines → Repository variables; всегда ссылайтесь как $VAR, никогда не литеральными значениями в YAML):

  • GITL_API_KEY — BYOK-ключ LLM. Необязателен: без него gitl выполняет детерминированное offline-ревью (без сети, без затрат).

  • GITL_BITBUCKET_TOKEN — учётные данные для публикации комментария PR: access token репозитория/проекта/workspace со скоупом pullrequest:write, отправляемый как Authorization: Bearer. Альтернатива: задайте GITL_BITBUCKET_USER + GITL_BITBUCKET_APP_PASSWORD (app password со скоупом pullrequest:write) для Basic-аутентификации. Если не настроено ни то ни другое, pipe быстро завершается с явным сообщением — до траты любого LLM-бюджета.

Заметка о цепочке поставок (почему это отличается от компонента GitLab). Pipe не исполняет ничего, скачанного во время выполнения: бинарник gitl, ci/comment.sh и entrypoint встроены в версионированный образ из одного дерева исходников. Компоненту GitLab приходится скачивать ci/comment.sh по сети без проверки целостности (см. его заметку о доверии выше); pipe закрывает этот пробел по построению.

Статус проверки — прочтите, прежде чем полагаться на это. Сборка образа и полный внутриконтейнерный поток были проверены локально: docker build из этого репозитория, затем docker run на реальном тестовом git-репозитории с эмулированными переменными BITBUCKET_* — офлайн-ревью → корректный sticky comment.md → создание комментария (POST), sticky-обновление (PUT, по-прежнему ровно один комментарий) и распространение кода выхода --fail-on, проверенные end-to-end на локальном моке API комментариев Bitbucket; fail-fast пути (отсутствующие переменные учётных данных/PR) и запасное уведомление на плохом диапазоне также были проверены в контейнере. Что ещё не проверено: всё, что касается реальной инфраструктуры Bitbucket — REST-вызовы к api.bitbucket.org (формы взяты из документации Atlassian API), точные предопределённые переменные внутри живого PR-пайплайна (BITBUCKET_PR_DESTINATION_COMMIT и т.д. — документированные предположения, а не наблюдаемые значения), и то, как Pipelines монтирует клон в pipe-контейнеры. Относитесь к пути живого пайплайна как к экспериментальному, пока кто-нибудь не подтвердит зелёный прогон на реальном Bitbucket-воркспейсе; баг-репорты приветствуются.

Pre-commit хук (локально)

gitl поставляется с хуком фреймворка pre-commit, так что gitl review --staged --quiet запускается автоматически перед каждым коммитом — локально, офлайн и бесплатно по умолчанию (--quiet включён по умолчанию в манифесте хука, поэтому офлайн-уведомление не печатается при каждом коммите).

Добавьте в .pre-commit-config.yaml вашего репозитория:

repos:
  - repo: https://github.com/akomyagin/gitl
    rev: v0.6.2   # pin to a released tag
    hooks:
      - id: gitl-review

затем выполните pre-commit install. Фреймворк сам собирает бинарник gitl (language: golang) и кэширует окружение в ~/.cache/pre-commit/, так что стоимость сборки оплачивается один раз, а не при каждом коммите.

Чтобы включить блокирующий хук с ограничением стоимости:

hooks:
  - id: gitl-review
    args: [--fail-on=high, --max-cost-usd=0.05]   # opt-in: block on high risk, cap cost

Экспортируйте GITL_API_KEY в вашем окружении для реального AI-ревью; без него хук выполняет детерминированное офлайн-ревью (без сети, без затрат).

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

  • Офлайн по умолчанию. Без API-ключа, без сети, без затрат на коммит. Установите GITL_API_KEY, чтобы включить реальное AI-ревью.

  • Неблокирующий по умолчанию. Хук выводит ревью, но не проваливает коммит — тот же принцип «WARN по умолчанию, жёсткий шлюз — явное согласие», что и в CLI/Action. Добавьте args: [--fail-on=high] для блокировки.

  • Задержка. Реальное API-ревью занимает несколько секунд; держите его вне горячего пути, оставив офлайн, или ограничьте с помощью --max-cost-usd.

  • Конфиденциальность диффа. С реальным ключом staged-дифф уходит вашему настроенному LLM-провайдеру — для приватного кода используйте self-hosted/enterprise-провайдера (Ollama, Azure OpenAI), см. Providers выше.

  • Подавление офлайн-уведомления. Манифест передаёт --quiet по умолчанию, поэтому уведомление «using deterministic offline review» в stderr при каждом коммите замалчивается; тот же переключатель доступен в review/changelog как --quiet / GITL_QUIET, или на уровне репозитория через output.quiet: true (MCP-сервер учитывает только output.quiet/GITL_OUTPUT_QUIET — у него нет флагов, поэтому короткий алиас GITL_QUIET там не работает). Ошибки и сам вывод ревью не затрагиваются.

Без фреймворка pre-commit

Подойдёт и обычный git-хук:

# .git/hooks/pre-commit  (chmod +x)
#!/usr/bin/env bash
set -euo pipefail
# Offline, non-blocking review of staged changes (WARN by default); --quiet
# suppresses the per-commit offline notice on stderr.
gitl review --staged --quiet || true
# To block the commit on high risk instead, replace the line above with:
#   gitl review --staged --quiet --fail-on=high

MCP-сервер

gitl mcp запускает gitl как stdio-сервер Model Context Protocol — отдельный дополнительный канал по сравнению с использованием CLI/CI выше, для интерактивного использования gitl внутри агентской сессии (Claude Desktop, Cursor, Windsurf и т.д.) вместо вызова из командной строки. Он предоставляет два инструмента:

  • gitl_review — тот же движок ревью, что и gitl review: range/pr/staged (ровно один), опциональное переопределение model на каждый вызов. Провайдер и endpoint фиксируются при запуске сервера по замыслу: вызывающий инструмент — это AI-агент, которым можно управлять через prompt injection внутри ревьюируемого содержимого — per-call base_url позволил бы вредоносному коммиту перенаправить запрос и утечь реальный API-ключ. Всегда возвращает структурированный JSON-артефакт (без md/text-рендеринга, без стриминга — результат инструмента атомарен). risk.level возвращается как данные; в MCP-режиме нет --fail-on, поскольку нет кода выхода процесса, который можно было бы шлюзовать.

  • gitl_digest — то же, что gitl digest: days (по умолчанию 7), опциональный repos. Без явного аргумента repos инструмент обрабатывает только рабочую директорию сервера (плюс digest.repos из .gitl.yaml, если настроено) — он никогда не обходит произвольные пути по собственной инициативе. Явный аргумент repos учитывается как есть (вызывающий агент уже имеет доступ к файловой системе через свои инструменты; это не граница контроля доступа, а просто дефолт «не удивляй пользователя»).

Добавьте в конфиг вашего MCP-клиента (Claude Desktop, Cursor и т.д.):

{
  "mcpServers": {
    "gitl": {
      "command": "gitl",
      "args": ["mcp"]
    }
  }
}

Конфиг загружается один раз при запуске так же, как и для обычных команд (.gitl.yaml + личный конфиг + переменные окружения GITL_*, из директории, в которой запущен gitl mcp). Без ключа вызовы инструментов работают в том же детерминированном офлайн-режиме, что и CLI. stdout зарезервирован под протокол MCP — туда никогда не пишется ничего человекочитаемого; предупреждения идут в stderr.

Лицензия

MIT.

Related MCP Connectors

Related MCP Servers