Главная/Материалы/Материал

ci-pipeline

Конвейер доставки, у которого логика в скриптах, а не в шагах YAML, и который различает три состояния этапа вместо двух.

Используется в разделе 16 и в проекте 4.

Что демонстрирует

  • три кода возврата: 0 чисто, 1 находки, 3 не проверено;
  • порядок этапов, выведенный из «время / вероятность отказа»;
  • конвейер, запускаемый локально одной командой — без CI;
  • неизменяемый тег по SHA коммита с пометкой грязного дерева;
  • развёртывание только по digest; развёртывание по тегу отвергается;
  • откат на записанный предыдущий digest;
  • скрипты конвейера, покрытые тестами.

Запуск

bash
./ci/pipeline.sh
echo "код: $?"

На машине без Docker, trivy и syft:

text
этап             результат
───────────────────────────────
линтер           пройдено
тесты            пройдено
сборка           НЕ ПРОВЕРЕНО
проверка образа  НЕ ПРОВЕРЕНО
сканирование     НЕ ПРОВЕРЕНО
состав (SBOM)    НЕ ПРОВЕРЕНО

пройдено: 2, провалено: 0, не проверено: 4
ИТОГ: конвейер прошёл НЕ ПОЛНОСТЬЮ — 4 этапов не выполнялось
Это не успех. Для публикации образа требуется код 0.

Код возврата — 3. Это и есть главное, что показывает пример: непроверенное не равно пройденному.

Тесты

bash
pytest -q
text
28 passed

Скрипты конвейера — код, и проверяются как код. Иначе об ошибке в них сообщит только сломанная выкатка.

Тесты проходят и при грязном рабочем дереве:

bash
touch проба.txt && pytest -q; rm проба.txt

Первая редакция тестов тегов работала с репозиторием самого проекта и падала, стоило отредактировать файл теста. Тест, зависящий от состояния репозитория, ломается от собственной правки — поэтому тесты тегов создают временный репозиторий.

Состав

text
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:

bash
PATH=/usr/bin:/bin ./ci/scan.sh; echo "код: $?"
text
[12:05:20] НЕ ПРОВЕРЕНО: trivy не установлен; образ … не проверялся
код: 3

Провал перевешивает непроверенное:

bash
printf 'тесты\t1\nсканирование\t3\n' | python3 ci/summary.py; echo "код: $?"
text
пройдено: 0, провалено: 1, не проверено: 1
ИТОГ: конвейер провален
код: 1

Тег помечает грязное дерево — включая неотслеживаемые файлы:

bash
bash -c 'source ci/lib.sh; echo "$(image_tag)"'
touch новый.txt
bash -c 'source ci/lib.sh; echo "$(image_tag)"'
rm новый.txt
text
g9c9b7aec7405
g9c9b7aec7405-dirty

git diff неотслеживаемый файл не видит, а в контекст сборки он попадает. Проверять нужно git status --porcelain.

Развёртывание по тегу отвергается:

bash
./ci/deploy.sh "ghcr.io/example/app:v1"; echo "код: $?"
text
[12:05:20] ОШИБКА: развёртывание по тегу запрещено: укажите digest (ghcr.io/example/app:v1)
код: 1

Откат без записанного digest отказывает:

bash
./ci/rollback.sh; echo "код: $?"
text
[12:05:21] ОШИБКА: предыдущий digest не записан: откатываться некуда
код: 1

Частичный SBOM

Когда syft недоступен, состав собирается из requirements.txt — но помечается как частичный и возвращает код 3:

bash
./ci/sbom.sh
python3 -c "
import json
d = json.load(open('artifacts/sbom.json'))
print('полный:', d['complete'], '| пакетов:', len(d['packages']))"
text
полный: False | пакетов: 5

Такой файл не видит системных пакетов базового образа, а уязвимости чаще находят именно там. Файл, выглядящий как SBOM и не являющийся им, хуже отсутствующего: на него сошлются.

Перенос на другую платформу

Из тринадцати шагов pipeline.yaml шесть — вызовы ci/*.sh. Остальные: получение исходников, установка окружения, вход в registry, выгрузка артефактов, публикация.

Ни одна проверка в YAML не живёт. Перенос на GitLab CI или другую платформу — переписывание шести строк вызова, а не логики.

Чего пример не делает

Ни один образ не собран. Docker на машине, где готовился курс, отсутствует. Этапы сборки, проверки образа, сканирования и публикации проверены только на пути «инструмента нет» — то есть их код возврата 3 подтверждён, а поведение при наличии инструмента нет.

Проверьте у себя: правильность семи проверок в verify-image.sh подтверждена документацией, а не запуском. Прогоните их в том числе на заведомо плохом образе.

Digest базового образа — настоящий и проверенный. Получен командой

bash
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
Диаграмма конвейера

Markdown на GitHub ↗