Введение
В заключительной статье цикла собираем итоговый, полный пайплайн, объединяющий все темы цикла: от пуша кода через тестирование и статический анализ до кросс-платформенной сборки , упаковки и автоматической публикации релиза , демонстрируя их совместную, согласованную работу в едином, реалистичном процессе.
Концепция
Архитектура итогового пайплайна: при каждом push/pull request выполняется быстрая проверка (сборка, статический анализ критичных категорий, юнит-тесты) на одной платформе для быстрой обратной связи; при создании тега версии запускается полная, кросс-платформенная матричная сборка (Windows/Linux/macOS), полное тестирование, автоматическая упаковка через специализированные инструменты каждой платформы, и публикация готового релиза с обновлением манифеста автообновления, с явным ручным контрольным пунктом перед финальной публикацией для критичных production-релизов.
Пример кода
# .github/workflows/ci.yml — быстрая проверка при каждом push/pull request
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
quick-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: jurplel/install-qt-action@v3
with:
version: '6.6.0'
- name: Конфигурация и сборка
run: |
cmake -B build -S . -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DBUILD_TESTING=ON
cmake --build build
- name: Статический анализ (критичные проверки)
run: run-clang-tidy -p build -checks='bugprone-*' src/*.cpp
- name: Юнит-тесты
run: ctest --test-dir build --output-on-failure
# .github/workflows/release.yml — полный пайплайн релиза, срабатывающий на тег версии
name: Release
on:
push:
tags: ['v*']
jobs:
build-matrix:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: jurplel/install-qt-action@v3
with:
version: '6.6.0'
- name: Сборка
run: |
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
- name: Тесты
run: ctest --test-dir build --output-on-failure
- name: Упаковка (Windows)
if: runner.os == 'Windows'
run: windeployqt build/Release/MyQtApp.exe --dir package
- name: Упаковка (Linux)
if: runner.os == 'Linux'
run: linuxdeployqt build/MyQtApp -appimage
- name: Упаковка (macOS)
if: runner.os == 'macOS'
run: macdeployqt build/MyQtApp.app -dmg
- name: Загрузка артефакта платформы
uses: actions/upload-artifact@v4
with:
name: MyApp-${{ matrix.os }}
path: package/**
publish-release:
needs: build-matrix
runs-on: ubuntu-latest
environment: production # ручное одобрение перед финальной публикацией
steps:
- uses: actions/download-artifact@v4
- uses: softprops/action-gh-release@v1
with:
files: '**/*'
generate_release_notes: true
Пояснения к коду
Разделение на два отдельных workflow-файла (ci.yml для быстрой, частой проверки, release.yml для полного, но значительно более редкого процесса релиза) отражает практичный баланс — не каждое изменение кода требует полной, дорогой по времени кросс-платформенной сборки и упаковки, и быстрая обратная связь для повседневной разработки (через ci.yml) отделена от более тщательного, но редкого процесса подготовки реального релиза (через release.yml), запускаемого только по явному, осознанному действию создания тега версии. Двухступенчатая структура release.yml (build-matrix, затем publish-release с needs: build-matrix) показывает логическую последовательность всего цикла статей в действии — полная матричная сборка и упаковка для всех трёх платформ должны успешно завершиться (включая прохождение тестов на каждой из них) прежде, чем выполнится финальная задача публикации, и явное environment: production сохраняет осознанный, ручной контрольный пункт перед действительно видимой пользователям публикацией готового релиза.
Подводные камни
- Объединение быстрой проверки и полного релизного пайплайна в один, единый workflow без разделения, аналогично примеру — это вынуждает либо выполнять полную, дорогую кросс-платформенную сборку при каждом обычном изменении кода (что значительно замедляет повседневную разработку без соразмерной выгоды для большинства обычных изменений), либо пропускать важные проверки для релизных тегов, если упрощённая, единая конфигурация была изначально рассчитана только на быструю проверку — явное разделение на два различных по назначению пайплайна, как в итоговом примере, даёт оптимальный баланс для обоих сценариев.
- Недостаточное тестирование самого CI/CD пайплайна перед тем, как полагаться на него для реальных, production-релизов — пайплайн, написанный, но никогда реально не «прогнанный» целиком до финальной публикации (например, протестированный только до этапа сборки, без реальной проверки шагов упаковки/публикации на тестовом теге), может содержать необнаруженные ошибки именно в финальных, критичных шагах, обнаруживаемые только при попытке реального, «боевого» релиза — стоит явно протестировать весь пайплайн целиком на тестовом, не предназначенном для реальных пользователей теге перед тем, как довериться ему для настоящего, важного релиза.
- Отсутствие явного плана действий при частичном сбое матричной сборки (например, сборка успешна на Linux и macOS, но не удалась на Windows) — пайплайн должен явно определять, что происходит в таком случае: блокируется ли публикация релиза вовсе (что кажется наиболее безопасным выбором по умолчанию, как и реализовано через
needs: build-matrix, требующий успеха ВСЕХ задач матрицы), или допускается частичная публикация только для успешно собранных платформ — это архитектурное решение должно быть осознанным, а не случайным следствием конфигурации пайплайна. - Игнорирование стоимости (времени и, для платных CI-сервисов, денежных затрат) полного релизного пайплайна, особенно при частых, экспериментальных тегах версий во время активной, нестабильной разработки — для проектов с частыми пред-релизными тегами (например, для внутреннего тестирования) стоит явно продумать, действительно ли каждый такой тег должен запускать полный, дорогой кросс-платформенный пайплайн с реальной публикацией, или для таких целей разумнее использовать отдельный, более лёгкий механизм (например, простую сборку без полной публикации) до момента действительно финального, готового к реальному пользовательскому релизу тега.