8. Git в CI/CD и автоматизации
Цель темы: понимать, как Git используется в пайплайнах: клонирование (в том числе shallow, по ветке/тегу), теги для релизов, типовые сценарии checkout → build и переменные окружения в CI; основы подмодулей и безопасность (токены, секреты).
Определения терминов
Shallow clone (неглубокий клон)
Shallow clone — клонирование с ограниченной глубиной истории (например, только последний коммит или последние N коммитов). Задаётся опцией --depth. Ускоряет clone и экономит место на агентах CI, но не все операции (например, полный git log или некоторые merge) могут быть доступны без «углубления» репозитория.
Тег (tag)
Тег — именованная ссылка на коммит, которая не двигается (в отличие от ветки). Обычно используется для релизов: v1.0.0, release-2024-01. В CI по тегу часто собирают воспроизводимый артефакт и деплоят.
Подмодуль (submodule)
Подмодуль — отдельный Git-репозиторий, подключённый как подкаталог в другом репозитории. Родительский репо хранит только ссылку на конкретный коммит подмодуля. В CI при клонировании нужно явно инициализировать и обновить подмодули (git submodule update --init).
Sparse checkout
Sparse checkout — проверка только выбранных каталогов/файлов репозитория. Уменьшает объём данных при clone и ускоряет работу в больших репо. Настраивается через git sparse-checkout (Git 2.25+).
Клонирование в CI
Обычный clone
git clone https://github.com/org/repo.git
cd repo
В CI часто используют URL с токеном или SSH-ключом для доступа к приватному репо (см. раздел «Безопасность»).
Shallow clone (ограничение глубины)
git clone --depth 1 https://github.com/org/repo.git
Только последний коммит; история не подтягивается. Для сборки по текущей ветке этого обычно достаточно.
git clone --depth 50 https://github.com/org/repo.git
Последние 50 коммитов. Удобно, если в пайплайне нужен неглубокий, но не единственный коммит (например, для git describe или неглубокого лога).
Практика
В CI по умолчанию используйте shallow clone для скорости. Если нужна полная история (bisect, полный changelog), клонируйте без --depth или увеличьте глубину.
Клон конкретной ветки или тега
git clone --branch main https://github.com/org/repo.git
git clone --branch v1.0.0 https://github.com/org/repo.git
Сразу после clone будет проверена указанная ветка или тег. Комбинируют с --depth 1 для экономии времени.
Checkout по коммиту (воспроизводимая сборка)
Пайплайн часто получает хеш коммита из триггера (push, merge request). Проверка этого коммита даёт воспроизводимую сборку:
git fetch origin
git checkout $CI_COMMIT_SHA
# или после shallow clone по ветке:
git fetch --depth 1 origin $CI_COMMIT_SHA
git checkout $CI_COMMIT_SHA
Артефакт можно тегировать хешем: app:$CI_COMMIT_SHA или app:${CI_COMMIT_SHORT_SHA}.
Sparse checkout (только нужные каталоги)
В больших репозиториях можно вытянуть только часть дерева:
git clone --filter=blob:none --sparse https://github.com/org/repo.git
cd repo
git sparse-checkout set path/to/app docs
Дальнейшая работа только с указанными путями; остальное не загружается. Поддерживается в Git 2.25+; в старых версиях — через core.sparseCheckout и .git/info/sparse-checkout.
Теги (tags)
Создание и отправка тегов
git tag v1.0.0
git tag -a v1.0.0 -m "Release 1.0.0"
git push origin v1.0.0
Тег без -a — «лёгкий» (lightweight), только указатель на коммит. С -a — аннотированный тег (сообщение, автор, дата); для релизов обычно используют аннотированные.
Семантическое версирование
Распространённая схема: vMAJOR.MINOR.PATCH (например, v1.2.3). В CI по такому тегу собирают релизный артефакт и деплоят.
Удаление тега
git tag -d v0.9
git push origin --delete v0.9
Checkout по тегу в CI
git clone --depth 1 --branch v1.0.0 https://github.com/org/repo.git
# или после обычного clone:
git fetch origin tag v1.0.0
git checkout v1.0.0
Production
Релизный пайплайн запускается при push тега v*. Скрипт клонирует репо по этому тегу, собирает образ/артефакт с версией из тега и публикует в registry. Деплой идёт из того же тега — воспроизводимость гарантирована.
Подмодули (submodules) — кратко
Добавление подмодуля
git submodule add https://github.com/org/lib.git path/to/lib
git add path/to/lib
git commit -m "Add lib as submodule"
В репозитории сохраняется путь и фиксированный коммит подмодуля.
Клонирование репо с подмодулями в CI
git clone --recurse-submodules https://github.com/org/repo.git
# или после обычного clone:
git submodule update --init --recursive
Без --init/--recurse-submodules каталоги подмодулей будут пустыми, сборка может упасть.
Внимание
В CI при shallow clone родительского репо подмодули по умолчанию клонируются полностью. Для очень больших подмодулей можно задать глубину: git submodule update --init --depth 1.
Переменные и сценарии в CI
Типовые переменные окружения
В пайплайнах (GitHub Actions, GitLab CI, Jenkins и др.) после checkout часто доступны переменные, связанные с Git:
| Переменная (пример) | Описание |
|---|---|
GIT_COMMIT / CI_COMMIT_SHA |
Хеш коммита |
GIT_BRANCH / CI_COMMIT_REF_NAME |
Имя ветки или тега |
GIT_TAG |
Тег (если сборка по тегу) |
CI_COMMIT_SHORT_SHA |
Короткий хеш (7–8 символов) |
Точные имена зависят от системы (см. документацию вашего CI).
Типовой сценарий в пайплайне
- Checkout — клонирование по ветке или получение коммита из триггера.
- Подмодули (если есть) —
git submodule update --init --recursive. - Сборка — сборка с передачей версии из Git (хеш или тег) в артефакт.
- Публикация — образ/артефакт с тегом, например
app:${CI_COMMIT_SHORT_SHA}илиapp:v1.0.0.
Пример передачи версии в сборку (Docker):
docker build -t app:${CI_COMMIT_SHORT_SHA} --build-arg VERSION=${CI_COMMIT_SHORT_SHA} .
Безопасность
Не коммитить секреты
Пароли, ключи API, токены не должны попадать в репозиторий. Используйте .gitignore для локальных конфигов (.env, *.pem), секреты передавайте через переменные окружения CI и хранилища секретов (GitHub Secrets, GitLab CI Variables, Vault).
Токены для clone/push в CI
- GitHub: Personal Access Token (PAT) или встроенный
GITHUB_TOKENв Actions; сохранять в Secrets, не в коде. - GitLab: Deploy token или CI Job Token; переменные с флагом «masked» для логов.
- URL с токеном:
https://oauth2:${TOKEN}@gitlab.com/org/repo.git— токен только в переменной окружения, не в скрипте.
Проверка истории на утёкшие секреты
В пайплайне можно запускать сканеры (например, gitleaks, truffleHog), которые ищут паттерны секретов в коде и истории. Лучше предотвращать попадание секретов через pre-commit и обучение, а сканер использовать как дополнительную проверку.
Паттерны использования
| Паттерн | Описание |
|---|---|
| Теги для релизов | Везде использовать теги вида v1.0.0 для сборки и деплоя релизов; не деплоить «просто с main» без привязки к тегу/коммиту. |
| Shallow clone в CI | По умолчанию клонировать с --depth 1 (или небольшим depth), чтобы ускорить пайплайн. |
| Версия артефакта из Git | Вшивать в образ/артефакт хеш коммита или тег (через build-arg, метаданные), чтобы в проде было видно, что развёрнуто. |
| Триггер по ветке/тегу | Запускать сборку только для нужных веток (main, release/*) или при push тега. |
Антипаттерны
| Антипаттерн | Почему плохо | Что делать |
|---|---|---|
| Хранить бинарники и артефакты в репо | Раздувание репо, медленный clone. | Артефакты собирать в CI и хранить в registry/артефактном хранилище. |
| Секреты в коде или в коммитах | Утечка при доступе к репо и в логах. | Секреты — только в переменных CI и .gitignore для локальных файлов. |
| Полный clone без необходимости | Долго и тяжело на агентах. | Использовать shallow clone или sparse checkout. |
| Деплой «с ветки» без фиксации коммита | Ветка двигается, деплой невоспроизводим. | Деплоить по тегу или по явному хешу коммита. |
Примеры из production
Деплой по тегу
При push тега v* запускается release-пайплайн: clone по тегу → build → публикация образа с тегом v1.0.0 → деплой в staging/prod. Один тег = одна воспроизводимая версия.
Версирование артефактов
В метаданные Docker-образа или в приложение записывают GIT_COMMIT и при необходимости GIT_TAG. В проде по app --version или по лейблу образа видно точный коммит/тег.
Разные триггеры для веток
Сборка и тесты — на каждый push в main и в ветки MR. Деплой в prod — только при push тега или при merge в main с последующим созданием тега из пайплайна.