Настройка автоматического CI/CD для сборки десктоп-приложения
Десктоп-приложение на Electron, Tauri или Qt — это не веб-сервис: каждый релиз нужно собрать под Windows (x64, arm64), macOS (Intel, Apple Silicon), Linux (deb, rpm, AppImage). Делать это руками означает восемь-десять сборок вручную, потенциальные ошибки при подписи и трудоёмкую публикацию. CI/CD убирает эту механическую работу.
Архитектура пайплайна
Типичный пайплайн для кроссплатформенного десктоп-приложения:
push to main / tag v*
│
├── lint & unit tests (ubuntu-latest, быстро)
│
├── build:windows (windows-latest)
├── build:macos (macos-latest, Intel + arm64)
└── build:linux (ubuntu-latest)
│
└── sign & notarize (macOS)
│
└── draft GitHub Release / upload to S3
│
└── notify (Slack/Telegram)
GitHub Actions: матричная сборка
# .github/workflows/build.yml
name: Build Desktop App
on:
push:
tags:
- 'v*'
workflow_dispatch:
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
platform: win
arch: x64
- os: macos-latest
platform: mac
arch: x64
- os: macos-latest
platform: mac
arch: arm64
- os: ubuntu-22.04
platform: linux
arch: x64
runs-on: ${{ matrix.os }}
name: Build ${{ matrix.platform }}-${{ matrix.arch }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build & package (Electron)
env:
# Переменные для подписи кода
CSC_LINK: ${{ secrets.APPLE_CERT_P12 }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
WIN_CSC_LINK: ${{ secrets.WIN_CERT_P12 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CERT_PASSWORD }}
run: |
npx electron-builder \
--${{ matrix.platform }} \
--${{ matrix.arch }} \
--publish never
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: dist-${{ matrix.platform }}-${{ matrix.arch }}
path: dist/
retention-days: 7
Подпись кода
Без подписи Windows показывает SmartScreen «Файл загружен из интернета», macOS — блокирует Gatekeeper.
Windows: сертификат Code Signing (EV или OV) от DigiCert/Sectigo/Certum. EV даёт мгновенную репутацию в SmartScreen.
macOS: Developer ID Application сертификат из Apple Developer Program ($99/год) + нотаризация через Apple.
Секреты хранятся в GitHub Secrets, никогда в репозитории:
# Добавление сертификата в GitHub Secrets
base64 -i certificate.p12 | pbcopy
# Вставляем в APPLE_CERT_P12 через GitHub UI
Нотаризация macOS занимает 1–5 минут — этим объясняется дополнительный шаг wait-for-notarization в пайплайне.
Tauri: альтернатива Electron
Для Tauri (Rust backend) сборка через официальный tauri-action:
- name: Build Tauri app
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
tagName: ${{ github.ref_name }}
releaseName: 'App v__VERSION__'
releaseBody: 'See CHANGELOG for details'
releaseDraft: true
Tauri умеет встроенное обновление (updater) — подписанные update.json и бинари автоматически публикуются в релиз.
Авто-обновление
Electron + electron-updater (часть electron-builder):
// main.js
const { autoUpdater } = require('electron-updater');
autoUpdater.setFeedURL({
provider: 'github',
owner: 'your-org',
repo: 'your-app',
private: false
});
app.whenReady().then(() => {
autoUpdater.checkForUpdatesAndNotify();
});
autoUpdater.on('update-downloaded', () => {
autoUpdater.quitAndInstall();
});
electron-builder публикует latest.yml / latest-mac.yml / latest-linux.yml в GitHub Releases. Приложение проверяет эти файлы при старте.
Версионирование
Версия берётся из package.json. При создании тега v1.2.3 CI автоматически проставляет версию в сборку:
# В workflow — перед сборкой
- name: Set version from tag
run: |
VERSION="${GITHUB_REF_NAME#v}"
npm version $VERSION --no-git-tag-version
Кэширование зависимостей
Для Electron-проекта node_modules весит 500 МБ+. Кэш actions/setup-node с cache: 'npm' сокращает время установки с 3–4 минут до 30–60 секунд при cache hit.
Для Rust/Tauri — кэш Cargo:
- uses: Swatinem/rust-cache@v2
with:
workspaces: './src-tauri -> target'
Публикация релиза
publish:
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: dist-all/
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
draft: true
generate_release_notes: true
files: |
dist-all/**/*.exe
dist-all/**/*.dmg
dist-all/**/*.AppImage
dist-all/**/*.deb
dist-all/**/*.rpm
Draft-релиз позволяет проверить артефакты перед публичной публикацией.
Время сборки
| Платформа | Без кэша | С кэшем |
|---|---|---|
| Windows x64 | 8–12 мин | 4–6 мин |
| macOS x64 + нотаризация | 10–15 мин | 6–8 мин |
| macOS arm64 | 10–14 мин | 6–7 мин |
| Linux x64 | 5–8 мин | 2–4 мин |
Весь пайплайн (параллельно) укладывается в 12–18 минут. Для Tauri — немного дольше из-за компиляции Rust.
Сроки настройки
Базовый пайплайн без подписи — 1 день. Подпись Windows + нотаризация macOS + auto-updater — ещё 1–2 дня (большая часть времени уходит на получение и настройку сертификатов).







