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

Подпись и проверка коммитов

Согласно ADR 0004 каждый коммит контрибьютора обязан быть:

  1. криптографически подписан личным GPG-ключом (git commit -S), ключ которого совпадает с pgp_key_id в CONTRIBUTORS.json;
  2. снабжен трейлером Signed-off-by: (git commit -s) — формальным заявлением о согласии с условиями вклада.

Это два независимых механизма: -S (большая) дает криптографическую подпись, -s (маленькая) добавляет текстовую строку Signed-off-by: Имя <email> в сообщение коммита. Нужны оба.

Предполагается, что ключ уже создан и зарегистрирован — если нет, начните со статьи Создание ключа.

1. Настройка git

Выполните один раз (для репозитория Netgap или глобально с --global):

# Каким ключом подписывать (long key ID вашего ключа)
git config user.signingkey 556B41333653BD5A

# Подписывать каждый коммит автоматически (не нужно помнить про -S)
git config commit.gpgsign true

# Подписывать аннотированные теги
git config tag.gpgSign true

# Email автора коммита — тот же, что в UID ключа и CONTRIBUTORS.json
git config user.email andrey@ganyushkin.ru
git config user.name "Andrei Ganiushkin"

Если в системе несколько версий gpg, укажите явно, какую использовать:

git config gpg.program "$(command -v gpg)"
внимание

Согласованность email

CI сопоставляет автора коммита с записью в CONTRIBUTORS.json. Если git config user.email, email в UID ключа и email в CONTRIBUTORS.json различаются — коммит будет отклонен, даже с валидной подписью.

2. Создание подписанного коммита

Рекомендуемая команда фиксации изменения:

git commit -S -s -m "Implement CSV export for reports"
  • -S — подписать коммит GPG-ключом (не обязателен, если включен commit.gpgsign true);
  • -s — добавить трейлер Signed-off-by: (его автоматика не добавляет — указывайте всегда).

При первом коммите gpg-agent запросит passphrase ключа и закеширует ее на время сессии.

3. Проверка собственной подписи

Перед отправкой изменений убедитесь, что подпись на месте и валидна:

# Развернутая информация о подписи последнего коммита
git log --show-signature -1

# Краткая проверка конкретного коммита
git verify-commit HEAD

# Статус подписи по истории: %G? выводит однобуквенный код
git log --pretty="format:%h %G? %aN %s" -10

Коды статуса %G?:

КодЗначение
GПодпись валидна (Good)
BПодпись невалидна (Bad) — коммит изменен после подписи
UПодпись валидна, но ключ не имеет установленного доверия
EПодпись не может быть проверена (например, нет публичного ключа)
X/YПодпись валидна, но ключ истек / истек на момент подписи
NПодписи нет

В Signed-off-by проверьте совпадение имени и email с вашими данными:

git log -1 --format=%B | grep Signed-off-by

4. Проверка подписей чужих коммитов

Чтобы git мог проверить подпись другого участника, его публичный ключ должен быть в вашей связке:

  1. получите и импортируйте ключ участника — способы описаны в статье Где взять ключ участника;
  2. сверьте отпечаток с pgp_key_id в CONTRIBUTORS.json;
  3. проверьте интересующие коммиты:
# Подписи всех коммитов ветки относительно develop
git log --show-signature develop..feature-branch

# Быстрый обзор: кто и чем подписал
git log --pretty="format:%h %G? %GK %aN %s" develop..feature-branch

%GK выводит key ID подписи — его удобно глазами сверить с pgp_key_id автора в CONTRIBUTORS.json (в CI это делает check-gpg-signature автоматически).

Статус U («ключ без установленного доверия») для валидной подписи — это нормально при модели доверия проекта: вы сверяете отпечаток с реестром вручную, а не строите web of trust. Чтобы убрать предупреждение локально, можно после сверки отпечатка подписать ключ локальной (неэкспортируемой) подписью: gpg --lsign-key <KEYID>.

5. Rebase, squash, amend — подпись слетает

GPG-подпись покрывает содержимое коммита целиком (дерево, родителя, автора, дату, сообщение). Любая операция, переписывающая коммит — rebase, squash, amend, cherry-pickделает старую подпись недействительной, и коммит нужно подписывать заново:

# При включенном commit.gpgsign=true rebase переподписывает коммиты сам
git rebase develop

# Явно попросить rebase подписывать результат
git rebase -S develop

# Переподписать последний коммит после amend
git commit --amend --no-edit -S

# Переподписать серию коммитов (например, последние 3)
git rebase --exec 'git commit --amend --no-edit -n -S' -i HEAD~3

После любой такой операции повторите проверку из раздела 3.

6. Что проверяет CI

CI отклоняет коммит, если: нет GPG-подписи; подпись невалидна; key ID подписи не совпадает с pgp_key_id автора; нет трейлера Signed-off-by:; автор отсутствует в CONTRIBUTORS.json. Ревью maintainer при этом остается обязательным — подпись проверяет личность автора, ревью проверяет содержимое.

7. Типичные проблемы и решения

СимптомПричина и решение
error: gpg failed to sign the dataЧаще всего gpg не может запросить passphrase. Добавьте в профиль оболочки export GPG_TTY=$(tty) и перезапустите терминал
Окно ввода passphrase не появляется (macOS)Установите pinentry-mac (brew install pinentry-mac) и укажите его в ~/.gnupg/gpg-agent.conf: pinentry-program /opt/homebrew/bin/pinentry-mac, затем gpgconf --kill gpg-agent
gpg: signing failed: No secret keyuser.signingkey указывает не на тот ключ. Сверьте с gpg --list-secret-keys --keyid-format=long
Подпись есть, но CI отклоняет коммитEmail автора коммита не совпадает с CONTRIBUTORS.json или ключ не совпадает с pgp_key_id. Проверьте git config user.email и %GK
Подпись валидна локально, у коллеги EУ коллеги нет вашего публичного ключа — направьте его к статье Где взять ключ участника
Ключ истекПродлите срок действия и переопубликуйте ключ — см. Обслуживание ключа

Чеклист: перед отправкой изменений

  • git config user.signingkey указывает на ваш зарегистрированный ключ.
  • commit.gpgsign true включен (или каждый коммит создается с -S).
  • Каждый коммит создан с -s — трейлер Signed-off-by: присутствует.
  • git config user.email совпадает с UID ключа и CONTRIBUTORS.json.
  • git log --pretty="format:%h %G? %s" показывает G для всех отправляемых коммитов.
  • После rebase/squash/amend коммиты переподписаны.