ci-pipeline
Конвейер доставки, у которого логика в скриптах, а не в шагах YAML, и который различает три состояния этапа вместо двух.
Используется в разделе 16 и в проекте 4.
Что демонстрирует
- три кода возврата:
0чисто,1находки,3не проверено; - порядок этапов, выведенный из «время / вероятность отказа»;
- конвейер, запускаемый локально одной командой — без CI;
- неизменяемый тег по SHA коммита с пометкой грязного дерева;
- развёртывание только по digest; развёртывание по тегу отвергается;
- откат на записанный предыдущий digest;
- скрипты конвейера, покрытые тестами.
Запуск
./ci/pipeline.sh
echo "код: $?"
На машине без Docker, trivy и syft:
этап результат
───────────────────────────────
линтер пройдено
тесты пройдено
сборка НЕ ПРОВЕРЕНО
проверка образа НЕ ПРОВЕРЕНО
сканирование НЕ ПРОВЕРЕНО
состав (SBOM) НЕ ПРОВЕРЕНО
пройдено: 2, провалено: 0, не проверено: 4
ИТОГ: конвейер прошёл НЕ ПОЛНОСТЬЮ — 4 этапов не выполнялось
Это не успех. Для публикации образа требуется код 0.
Код возврата — 3. Это и есть главное, что показывает пример: непроверенное не равно пройденному.
Тесты
pytest -q
28 passed
Скрипты конвейера — код, и проверяются как код. Иначе об ошибке в них сообщит только сломанная выкатка.
Тесты проходят и при грязном рабочем дереве:
touch проба.txt && pytest -q; rm проба.txt
Первая редакция тестов тегов работала с репозиторием самого проекта и падала, стоило отредактировать файл теста. Тест, зависящий от состояния репозитория, ломается от собственной правки — поэтому тесты тегов создают временный репозиторий.
Состав
ci/lib.sh коды возврата, вычисление тега, журналирование
ci/lint.sh быстрые проверки — первыми
ci/test.sh тесты; отсутствие базы в CI = отказ
ci/build.sh сборка с кэшем из registry, mode=max
ci/verify-image.sh семь проверок СОБРАННОГО образа
ci/scan.sh сканирование, три кода возврата
ci/sbom.sh состав; частичный помечается как частичный
ci/summary.py сводка по этапам
ci/pipeline.sh порядок и итог
ci/deploy.sh развёртывание по digest
ci/rollback.sh откат
Проверки, которые стоит выполнить
Отсутствие сканера даёт 3, а не 0:
PATH=/usr/bin:/bin ./ci/scan.sh; echo "код: $?"
[12:05:20] НЕ ПРОВЕРЕНО: trivy не установлен; образ … не проверялся
код: 3
Провал перевешивает непроверенное:
printf 'тесты\t1\nсканирование\t3\n' | python3 ci/summary.py; echo "код: $?"
пройдено: 0, провалено: 1, не проверено: 1
ИТОГ: конвейер провален
код: 1
Тег помечает грязное дерево — включая неотслеживаемые файлы:
bash -c 'source ci/lib.sh; echo "$(image_tag)"'
touch новый.txt
bash -c 'source ci/lib.sh; echo "$(image_tag)"'
rm новый.txt
g9c9b7aec7405
g9c9b7aec7405-dirty
git diff неотслеживаемый файл не видит, а в контекст сборки он попадает. Проверять нужно git status --porcelain.
Развёртывание по тегу отвергается:
./ci/deploy.sh "ghcr.io/example/app:v1"; echo "код: $?"
[12:05:20] ОШИБКА: развёртывание по тегу запрещено: укажите digest (ghcr.io/example/app:v1)
код: 1
Откат без записанного digest отказывает:
./ci/rollback.sh; echo "код: $?"
[12:05:21] ОШИБКА: предыдущий digest не записан: откатываться некуда
код: 1
Частичный SBOM
Когда syft недоступен, состав собирается из requirements.txt — но помечается как частичный и возвращает код 3:
./ci/sbom.sh
python3 -c "
import json
d = json.load(open('artifacts/sbom.json'))
print('полный:', d['complete'], '| пакетов:', len(d['packages']))"
полный: False | пакетов: 5
Такой файл не видит системных пакетов базового образа, а уязвимости чаще находят именно там. Файл, выглядящий как SBOM и не являющийся им, хуже отсутствующего: на него сошлются.
Перенос на другую платформу
Из тринадцати шагов pipeline.yaml шесть — вызовы ci/*.sh. Остальные: получение исходников, установка окружения, вход в registry, выгрузка артефактов, публикация.
Ни одна проверка в YAML не живёт. Перенос на GitLab CI или другую платформу — переписывание шести строк вызова, а не логики.
Чего пример не делает
Ни один образ не собран. Docker на машине, где готовился курс, отсутствует. Этапы сборки, проверки образа, сканирования и публикации проверены только на пути «инструмента нет» — то есть их код возврата 3 подтверждён, а поведение при наличии инструмента нет.
Проверьте у себя: правильность семи проверок в verify-image.sh подтверждена документацией, а не запуском. Прогоните их в том числе на заведомо плохом образе.
Digest базового образа — настоящий и проверенный. Получен командой
docker buildx imagetools inspect python:3.13-slim
на 2026-08-04 и подставлен в Dockerfile. Образ с ним собирается.
Оговорка, вытекающая из самой сути закрепления: этот digest устареет. Когда python:3.13-slim обновится, тег будет указывать на другой образ, а закреплённый останется прежним — в этом и смысл. Но если старый образ когда-нибудь удалят из registry, сборка перестанет работать. Обновлять digest — отдельный шаг сопровождения, и его нужно запланировать.
Кэш между запусками не измерен. Требует двух прогонов в настоящем CI.
Навигация
Все примеры
Раздел 16. CI/CD
Проект 4. Production pipeline
Диаграмма конвейера