Выпуск инкрементальных обновлений программ с помощью Jenkins и PowerShell
Сегодня мы поговорим об инкрементальных обновлениях (в первую очередь применительно к веб-проектам, однако при желании данный механизм можно модифицировать и для других задач).
При использовании современных идей непрерывной интеграции (continous integration) и — особенно — непрерывного развёртывания (continous delivery) обновления программного обеспечения выпускаются очень часто. В моей практике бывает по несколько обновлений в день — и это для крупного проекта, который уже не первый месяц в продакшене.
Поначалу кажется, что ничего страшного не случится, если мы будем каждый раз готовить полный пакет со всеми необходимыми ресурсами для разворачивания сайта. Но через некоторое время (как обычно, всегда внезапно) оказывается, что полный пакет обновления весит 30 МиБ, а техническая поддержка сайта отдана на аутсорс каким-нибудь индусам с совершенно убитым интернетом (вопросы о целесообразности такой организации оставим пока в стороне — поверьте, я их поднимал). И в полный рост встаёт вопрос подготовки инкрементальных обновлений сайта (или другой единицы развёртывания на ваш выбор).
Поскольку к тому моменту, как эта проблема возникла, мы уже перевели свою CI-среду на Jenkins, то пришлось разбираться, как же организовать обновление.
Постановка задачи такова: после каждого пуша в ветку master
в центральном репозитории я хочу, чтобы код из этой ветки был скомпилирован и полученный артефакт заархивирован на Jenkins целиком (на всякий случай — вдруг нам придётся разворачиваться с нуля где-нибудь, где нет безумных индусов). Также после этого необходимо сравнить только что скомпилированную версию с последней успешно развёрнутой, и подготовить отдельный инкрементальный пакет обновления, который бы содержал только изменившиеся файлы, а также специальный файлик Deletes.txt
со списком файлов, которые следует удалить.
Понятно, что сама по себе компиляция ветки на CI-сервере не должна вызывать проблем или вопросов. А вот как сравнить несколько последних артефактов и сгенерировать инкрементальное обновление?
Будем решать эту задачу, создав две отдельных сборки на Jenkins (как мне кажется, в таком режиме ими управлять намного удобнее). Первая задача будет отвечать только за полноценные сборки ветки master
; она не будет отвечать за инкрементальные обновления. А вот вторая задача будет следить за выполнением первой, и после её успешного завершения будет готовить инкрементальные обновления.
Создаём новую задачу (назовём её incremental
), и в параметрах этой задачи указываем опцию "Build after other projects are built", а в появившемся поле вводим название задачи, которая отвечает за основную сборку приложения.
И теперь, наконец, начинается самое интересное! Время подготовить процесс для сборки инкрементальных обновлений. Для этого я написал небольшой пак скриптов на PowerShell, которые делают следующее:
- Скачивают последние две сборки с указанного сервера.
- Распаковывают во временный каталог.
- Проводят сравнение MD5-хэшей всех файлов, входящих в состав сборок.
- Собирают отличающиеся файлы в архив инкрементального обновления.
Полученный архив рекомендуется сохранить в качестве выходного артефакта задачи на Jenkins.
Если кто-то пропустил, то вот ссылка на скрипты ещё раз: jincremental-packer.