Подпись и проверка коммитов
Согласно ADR 0004 каждый коммит контрибьютора обязан быть:
- криптографически подписан личным GPG-ключом (
git commit -S), ключ которого совпадает сpgp_key_idв CONTRIBUTORS.json; - снабжен трейлером
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 мог проверить подпись другого участника, его публичный ключ должен быть в вашей связке:
- получите и импортируйте ключ участника — способы описаны в статье Где взять ключ участника;
- сверьте отпечаток с
pgp_key_idвCONTRIBUTORS.json; - проверьте интересующие коммиты:
# Подписи всех коммитов ветки относительно 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 key | user.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 коммиты переподписаны.