Как прокидывать зависимости и передавать артефакты между джобами в CI/CD
Что проверяют
Различаете ли вы кэш и артефакты и умеете ли собрать пайплайн, где деплоится ровно то, что прошло тесты. Следом обычно спрашивают, как передать между джобами не файл, а значение, например версию или тег образа.
Как отвечать
Разведите понятия:
- зависимости: пакеты из PyPI фиксируются lock-файлом,
uv.lock,poetry.lockилиrequirements.txtизpip-compileс хешами. Каждая джоба ставит их заново, ускоряет это кэш; - кэш:
~/.cache/pipили~/.cache/uv. Он не гарантирован, может не найтись, и джоба обязана работать без него. Ключ строят от хеша lock-файла и версии Python; - артефакт: результат джобы, который гарантированно нужен следующей. Колесо из
dist/, отчёт покрытия, собранная документация.
Как это выглядит в инструментах:
- GitLab CI:
artifacts: paths: [dist/]сexpire_in. По умолчанию джоба скачивает артефакты всех джоб предыдущих стадий,dependencies: [build]сужает список,needs:строит граф без ожидания всей стадии. Значения передаются черезartifacts: reports: dotenv; - GitHub Actions:
actions/upload-artifactиactions/download-artifact, порядок черезneeds:, значения через outputs джобы и$GITHUB_OUTPUT. Для кэша pip хватаетcache: pipвactions/setup-python; - Jenkins:
stashиunstashвнутри одного прогона,archiveArtifactsдля хранения после него.
Правильный поток для Python: джоба build один раз делает python -m build, тесты ставят именно это колесо, деплой публикует тот же файл. Docker-образ между джобами не гоняют архивом, а пушат в registry с тегом по SHA коммита.
Где спотыкаются
Путают кэш и артефакты и кладут колесо в кэш. Потом деплой не находит файл, потому что кэш остался на другом раннере.
Пересобирают пакет в джобе деплоя. В прод уходит сборка, которую никто не тестировал, возможно, с другими версиями зависимостей.
Не ставят expire_in и копят гигабайты артефактов на каждый коммит. Рядом обычно живёт вторая беда: зависимости без lock-файла, из-за которых сборки в разные дни получаются разными.