Соберите wheel Python вне сети и опубликуйте его внутри
Соберите wheel там, где уже лежит исходный код, а затем перенесите именно этот файл внутреннему публикатору. Дайджесты и квитанция закрывают передачу.
У вас уже есть задача сборки, которая производит wheel, и приватный репозиторий, который должен его получить. Ошибка — позволить второй задаче пересобрать всё из ветки и назвать результат тем же релизом. Переносите файл, который прошёл проверку, называйте его по дайджесту и держите учётные данные для загрузки на внутренней стороне. Ниже разбирается один артефакт на пути от публичного CI-раннера к внутреннему публикатору.
Собирайте там, где уже лежит исходный код
Запускайте сборку в той задаче, где уже есть исходный код, инструменты и набор тестов. Приватный репозиторий вовсе не требует, чтобы эта задача имела к нему доступ. Установите фронтенд сборки в окружение, которое проект уже использует, и затем соберите оба формата дистрибутива, если проект поставляет оба.
python -m build --sdist --wheel --outdir dist
python -m twine check dist/*
Держите каталог вывода небольшим и одноразовым. Переносить дальше стоит только файлы, которые опубликует следующая задача, и их хеши. Всё остальное, включая виртуальные окружения и кеши, можно восстановить из записанной ревизии исходного кода. Компактный артефакт ещё и проще проверять, потому что каждый файл в нём — это файл, который кто-то может осмотреть.
Запишите, что их породило: имя пакета и версию из имён файлов, ревизию исходного кода и запуск workflow, который их создал. Одна строка версии не идентифицирует файл, потому что одну и ту же версию можно собрать дважды из разных коммитов. Запишите ревизию рядом с именами файлов в описании артефакта и считайте эту запись частью передачи.
Переносите байты, а не инструкции по пересборке
Вторая сборка — это второй продукт. Метки времени, включённые зависимости и версии инструментов могут изменить байты, поэтому пересборке нужны собственные доказательства тестов и собственное ревью. Продвигайте вместо этого проверенные файлы.
Загрузите файлы дистрибутива в хранилище артефактов, которое предоставляет ваша CI-платформа, и дайте внутренней задаче ссылку на именно этот артефакт, а не на имя ветки.
sha256sum dist/*.whl dist/*.tar.gz
# keep the digests beside the run id in the artifact description
Если ваша платформа поддерживает подписанное происхождение, попросите её заверить артефакт, а позже проверьте ожидаемый репозиторий, workflow и ревизию исходного кода. Документация по аттестациям GitHub описывает утверждения такой формы. Действительная подпись — это свидетельство происхождения, а не сканирование уязвимостей и не решение об авторизации.
Проверьте внутри перед загрузкой
Внутренняя задача начинается с проверки, что полученный файл — это тот файл, который был одобрен. Сравнивайте дайджесты с записью из задачи сборки, а не со значением, пересчитанным из того, что пришло.
sha256sum -c digests.txt
python -m twine check dist/*
Затем прочитайте метаданные. Имя пакета, версия и объявленные зависимости должны совпадать с тем ревью, которое вы одобрили. Wheel, чьё имя выглядит правильным, но чьи закрепления зависимостей изменились, — это другой пакет под той же меткой. Если метаданные сдвинулись, считайте артефакт неодобренным и выясните, какая сборка его произвела.
Если дайджесты не совпадают, остановитесь. Не загружайте и не чините файл, пересобирая его на месте; это стирает свидетельство того, что что-то изменилось в пути. Несовпадение означает, что хранилище артефактов, шаг загрузки или запись неверны, и каждое из этого заслуживает объяснения до публикации релиза.
Разберитесь с ответом о повторяющейся версии
Приватные репозитории обычно отказываются перезаписывать существующую версию, и этот отказ — это возможность, а не помеха. Когда загрузка не удаётся из-за того, что версия существует, изучите запись репозитория для этой версии и сравните хранящиеся файлы с дайджестами, которые вы собирались опубликовать.
Существующая версия может означать, что предыдущая загрузка удалась, а ответ потерялся, или что кто-то опубликовал другое содержимое под тем же номером.
Не удаляйте версию и не придумывайте новый номер, чтобы обойти ошибку. Решите вместе с владельцем релиза, приемлемо ли сохранённое содержимое, и запишите результат. Если содержимое отличается, у вас проблема версионирования, а не проблема конвейера.
Держите учётные данные для загрузки на одной стороне
Учётные данные репозитория нужны только внутреннему публикатору и только на время загрузки. Ограничьте токен целевым репозиторием и операциями загрузки, храните его в хранилище секретов внутренней системы и держите полностью вне задачи сборки. Если задача сборки может публиковать, то публиковать может любой, кто может изменить сборку.
Ограничьте и то, какие внутренние workflow могут запускать публикатор. Ручной запуск с обязательным одобрением — разумное значение по умолчанию для релизов; push в любую ветку — нет.
Готовый релиз должен оставить три вещи: файлы дистрибутива, их дайджесты вместе с ревизией исходного кода и запись репозитория, показывающую ту же версию.