Что было. Сборки проекта внезапно падают на шаге получения кода с git@…: Permission denied (publickey). Никто ничего не менял, репозиторий тот же, вчера всё собиралось.
Причина и решение (подтверждено). Ключ развёртывания, который CI использует для доступа к репозиторию, перестал быть действительным: его удалили в настройках репозитория, отозвали при ротации, либо он был привязан к другому репозиторию. Решение: в настройках проекта в CI удалить старый ключ, создать новый (сервис сам генерирует пару и добавляет публичную часть в репозиторий) и убедиться, что ключ появился в списке ключей развёртывания на стороне хостинга кода. Следующая сборка прошла.
Почему это случается «само».
- Плановая ротация или чистка ключей в организации (политика «ключи старше N дней удаляются»).
- Хостинг кода прекратил поддержку слабых алгоритмов (например, подписи RSA/SHA-1 для старых ключей): ключ есть, но сервер его больше не принимает. Лечится перевыпуском ключа в
ed25519. - Репозиторий перенесли или переименовали: ключ привязан к старому пути.
- Ключ развёртывания разрешён только на чтение, а конвейер начал делать
push(теги версий, обновление документации). - Кто-то использовал один ключ для нескольких репозиториев: хостинг кода запрещает это для deploy keys, и при добавлении во второй репозиторий он отваливается от первого.
Диагностика.
- В логе шага
checkoutвключить подробность:ssh -vT git@hostв отдельном шаге покажет, какой ключ предлагается и принимается ли. - На стороне хостинга кода: настройки репозитория → ключи развёртывания: есть ли ключ с тем же отпечатком, дата последнего использования, права.
- Журнал аудита организации: кто и когда удалил ключ.
Как устроить надёжно.
- Один ключ на пару «репозиторий + конвейер»,
ed25519, права только чтение, если запись не нужна. - Хранить приватную часть только в секретах CI, никогда в репозитории.
- Для нескольких репозиториев одного проекта: «машинный пользователь» с ограниченным членством вместо десятка deploy keys, либо токены установленного приложения хостинга, которые CI-сервис выпускает сам.
- Мониторинг: сборка, падающая на checkout, должна оповещать иначе, чем падающие тесты.
Ключи развёртывания описаны в документации GitHub.