Введение
Упаковку готового релиза — финальный шаг CD (Continuous Deployment/Delivery) — автоматическая публикация этого упакованного релиза в местах, где его получают конечные пользователи: GitHub Releases, магазины приложений, или собственная инфраструктура обновлений.
Концепция
Автоматическая публикация релиза при создании Git-тега (соответствующего номеру версии) устраняет ручной процесс загрузки файлов релиза через веб-интерфейс — CI-задача, срабатывающая именно на создание тега, может автоматически собрать, упаковать, и опубликовать готовый релиз без какого-либо ручного, ручного шага после самого создания тега. Для приложений с собственной системой автообновления это означает не только публикацию файлов, но и обновление метаданных (манифеста версий), которые уже установленные у пользователей экземпляры приложения проверяют для обнаружения наличия нового обновления.
Пример кода
# Триггер на создание тега версии — автоматический релиз
on:
push:
tags:
- 'v*' # срабатывает на теги вида v1.0.0, v2.3.1, и так далее
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# ... шаги сборки и упаковки ...
- name: Создание GitHub Release
uses: softprops/action-gh-release@v1
with:
files: |
release_package/MyQtApp-windows.zip
release_package/MyQtApp-linux.AppImage
release_package/MyQtApp-macos.dmg
generate_release_notes: true # автоматическая генерация описания на основе коммитов
#!/bin/bash
# Обновление манифеста версий для собственной системы автообновления
VERSION="1.2.0"
cat > version_manifest.json <<EOF
{
"version": "${VERSION}",
"release_date": "$(date -I)",
"download_url": "https://releases.example.com/MyQtApp-${VERSION}.zip",
"release_notes_url": "https://github.com/myorg/myapp/releases/tag/v${VERSION}"
}
EOF
# Загрузка манифеста на сервер раздачи обновлений —
# уже установленные у пользователей версии приложения, периодически
# проверяющие этот манифест, обнаружат новую версию и предложат пользователю обновление
aws s3 cp version_manifest.json s3://my-update-server/version_manifest.json
# Многоступенчатый деплой — сначала на staging-канал, реальная публикация только после проверки
jobs:
deploy_staging:
# ... публикация на ограниченный, тестовый канал распространения ...
deploy_production:
needs: deploy_staging
environment:
name: production
# GitHub Environments может требовать РУЧНОГО одобрения перед выполнением этой задачи,
# даже в рамках в остальном полностью автоматизированного пайплайна
steps:
- run: echo "Публикация в production-канал после одобрения..."
Пояснения к коду
on: push: tags: ['v*'] показывает практичный, явный триггер именно для процесса релиза, отличный от обычных триггеров CI (push в ветку, pull request) — создание Git-тега, соответствующего семантическому версионированию (v1.0.0), служит явным, осознанным сигналом «это релиз», запускающим именно процесс публикации, а не обычную проверку CI, выполняемую при каждом обычном изменении кода. Обновление version_manifest.json показывает связь между CI/CD-автоматизацией и собственной системой автообновления приложения — публикация нового релиза должна не только разместить сами файлы для скачивания, но и обновить отдельные метаданные, которые уже работающие у пользователей экземпляры приложения активно проверяют для самостоятельного обнаружения наличия обновления. Многоступенчатый деплой с environment: production, требующим ручного одобрения (поддерживается GitHub Environments), показывает практичный компромисс — даже в полностью автоматизированном в остальном пайплайне для особо критичного, видимого всем пользователям шага (публикация в production) может быть осознанно оставлен явный, ручной контрольный пункт принятия решения человеком, не отменяющий автоматизацию всех предшествующих шагов.
Подводные камни
- Полностью автоматическая публикация в production без какого-либо ручного контрольного пункта, особенно для приложений с большой пользовательской базой, где ошибочный, не до конца протестированный релиз может повлиять на значительное количество реальных пользователей одновременно — для таких случаев осознанное сохранение хотя бы одного, явного ручного шага одобрения (как в примере с
environment: production) перед финальной, видимой пользователям публикацией — разумный, не противоречащий общей идее автоматизации компромисс. - Отсутствие простого, надёжного механизма откэта (rollback) при обнаружении проблемы в уже опубликованном релизе — после автоматизации самого процесса публикации стоит заранее продумать и подготовить (а в идеале — также автоматизировать) обратный процесс быстрого возврата к предыдущей, известно работающей версии при обнаружении критичной проблемы в новом релизе уже после его публикации, а не разрабатывать этот процесс «на ходу» в стрессовой ситуации реального инцидента.
- Несогласованность версии, указанной в коде приложения (отображаемой пользователю, используемой для проверки обновлений), и версии, отражённой в Git-теге/манифесте обновлений — если эти два места (код приложения и инфраструктура релиза) не синхронизированы автоматически (например, версия в коде обновляется вручную, отдельно от процесса создания тега), легко допустить рассогласование, путающее как разработчиков, так и систему автообновления, которая может неправильно определить, является ли конкретная версия более новой по сравнению с уже установленной у пользователя.
- Игнорирование того, что обновление манифеста для системы автообновления и фактическая загрузка файлов релиза должны происходить атомарно или в правильном порядке (манифест должен указывать на уже реально загруженный, доступный файл, а не наоборот) — если манифест обновляется раньше, чем сам файл релиза становится реально доступным по указанному в манифесте URL, уже установленные у пользователей экземпляры приложения, проверяющие манифест именно в этот узкий промежуток времени, могут обнаружить «доступное» обновление, попытка скачивания которого завершится ошибкой, поскольку сам файл ещё не был окончательно загружен на сервер раздачи.