DevOps

Автоматический деплой: публикация релизов, обновление каналов распространения

2 просмотров

Введение

Упаковку готового релиза — финальный шаг 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, уже установленные у пользователей экземпляры приложения, проверяющие манифест именно в этот узкий промежуток времени, могут обнаружить «доступное» обновление, попытка скачивания которого завершится ошибкой, поскольку сам файл ещё не был окончательно загружен на сервер раздачи.