Пакування та розгортання Avalonia додатків
Пакування та розгортання Avalonia додатків
Створення додатку — це лише половина справи. Щоб ваш додаток потрапив до користувачів, його потрібно правильно зібрати, запакувати та розповсюдити. Кожна платформа має свої конвенції та очікування щодо формату розповсюдження: Windows користувачі очікують MSI або MSIX інсталятори, macOS користувачі звикли до DMG образів з .app bundles, а Linux користувачі цінують AppImage, Flatpak або пакети для своїх дистрибутивів.
У цій статті ми детально розглянемо весь процес підготовки Avalonia-додатку до розповсюдження: від базової збірки через dotnet publish до створення platform-specific інсталяторів, від вибору між self-contained та framework-dependent deployment до оптимізації розміру через trimming та AOT compilation. Ми також обговоримо автоматичні оновлення, code signing для різних платформ, та налаштування CI/CD pipeline для автоматичної збірки на всіх підтримуваних платформах.
Правильне пакування та розгортання — це не просто технічна задача, а важлива частина user experience. Користувач повинен мати можливість легко встановити ваш додаток, отримувати автоматичні оновлення, та бути впевненим у безпеці (через code signing). Давайте розберемося, як це зробити правильно для кожної платформи.
- Deployment — розгортання, процес підготовки додатку для розповсюдження
- Self-contained — автономний додаток, який включає .NET Runtime
- Framework-dependent — додаток, який вимагає встановленого .NET Runtime
- Trimming — видалення невикористаного коду для зменшення розміру
- AOT (Ahead-of-Time) — компіляція у нативний код до запуску
- Runtime Identifier (RID) — ідентифікатор платформи (win-x64, linux-x64, osx-arm64)
- Code Signing — цифровий підпис додатку для підтвердження автентичності
- Notarization — процес перевірки Apple для macOS додатків
- CI/CD — Continuous Integration / Continuous Deployment, автоматизація збірки та розгортання
dotnet publish — базова збірка
Основний інструмент для збірки .NET додатків — команда dotnet publish. Вона компілює додаток, збирає всі залежності та готує його до розповсюдження.
Базова команда publish
dotnet publish -c Release
Ця команда:
- Компілює проєкт у Release конфігурації (з оптимізаціями)
- Збирає всі NuGet залежності
- Копіює runtime assets (якщо потрібно)
- Створює output у папці
bin/Release/net8.0/publish/
Проте для production deployment потрібно більше налаштувань.
Runtime Identifier (RID) — вказівка платформи
Щоб зібрати додаток для конкретної платформи, використовуйте параметр -r (runtime):
# Windows 64-bit
dotnet publish -c Release -r win-x64
# Windows ARM64 (Surface Pro X, Windows on ARM)
dotnet publish -c Release -r win-arm64
# Linux 64-bit
dotnet publish -c Release -r linux-x64
# Linux ARM64 (Raspberry Pi 4, ARM servers)
dotnet publish -c Release -r linux-arm64
# macOS Intel (x64)
dotnet publish -c Release -r osx-x64
# macOS Apple Silicon (M1/M2/M3)
dotnet publish -c Release -r osx-arm64
Повний список RID доступний у .NET RID Catalog.
dotnet --info
RID: osx-arm64
- Windows:
win-x64(обов'язково),win-arm64(опціонально) - Linux:
linux-x64(обов'язково),linux-arm64(для ARM серверів/Raspberry Pi) - macOS:
osx-x64таosx-arm64(обидва обов'язкові для підтримки Intel та Apple Silicon)
Output path — вказівка папки виводу
За замовчуванням dotnet publish створює output у bin/Release/net8.0/{RID}/publish/. Для зручності можна вказати власну папку:
dotnet publish -c Release -r win-x64 -o ./dist/windows
dotnet publish -c Release -r linux-x64 -o ./dist/linux
dotnet publish -c Release -r osx-arm64 -o ./dist/macos
Це зручно для CI/CD pipelines та для організації artifacts.
Self-contained vs Framework-dependent
Один з найважливіших виборів при deployment — це тип розгортання: self-contained або framework-dependent.
Framework-dependent deployment (FDD)
Що це: Додаток вимагає встановленого .NET Runtime на машині користувача.
Переваги:
- Малий розмір (~3-10 MB для простого додатку)
- Швидше завантажується та встановлюється
- Автоматичні security patches через оновлення .NET Runtime
- Спільний runtime для всіх .NET додатків (економія диску)
Недоліки:
- Користувач повинен встановити .NET Runtime (додатковий крок)
- Залежність від версії runtime (може не працювати з новішою/старішою версією)
- Складніше для non-technical користувачів
Збірка:
dotnet publish -c Release -r win-x64 --self-contained false
Або у .csproj:
<PropertyGroup>
<SelfContained>false</SelfContained>
</PropertyGroup>
Розмір: ~3-10 MB (залежно від кількості залежностей)
Self-contained deployment (SCD)
Що це: Додаток включає .NET Runtime, не вимагає встановленого runtime на машині користувача.
Переваги:
- Працює "out of the box" — користувач просто запускає
- Не залежить від встановленого runtime
- Контроль над версією runtime (не зламається при оновленні системного runtime)
- Краще для non-technical користувачів
Недоліки:
- Великий розмір (~60-100 MB для простого додатку)
- Повільніше завантажується та встановлюється
- Потрібно вручну оновлювати для security patches
- Кожен додаток має свою копію runtime (марнування диску)
Збірка:
dotnet publish -c Release -r win-x64 --self-contained true
Або у .csproj:
<PropertyGroup>
<SelfContained>true</SelfContained>
</PropertyGroup>
Розмір: ~60-100 MB (залежно від платформи та trimming)
Порівняльна таблиця
| Характеристика | Framework-dependent | Self-contained |
|---|---|---|
| Розмір | 3-10 MB | 60-100 MB |
| Вимоги | .NET Runtime встановлений | Немає |
| Складність установки | Середня (потрібен runtime) | Низька (просто запустити) |
| Security updates | Автоматичні (через runtime) | Вручну (rebuild додатку) |
| Контроль версії | Низький | Високий |
| Цільова аудиторія | Розробники, IT professionals | Звичайні користувачі |
Коли що обирати?
Framework-dependent — якщо:
- Ваша аудиторія — розробники або IT professionals
- Додаток розповсюджується через package managers (apt, brew, chocolatey)
- Важливий малий розмір
- Додаток часто оновлюється
Self-contained — якщо:
- Ваша аудиторія — звичайні користувачі
- Важлива простота установки
- Потрібен контроль над версією runtime
- Додаток рідко оновлюється
Рекомендація: Для більшості desktop-додатків рекомендується self-contained deployment. Користувачі очікують, що додаток "просто працює" без додаткових кроків.
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true
- Не всі бібліотеки підтримують single-file (особливо з native dependencies)
- При запуску файл розпаковується у temp папку (повільніший старт)
- Avalonia може мати проблеми з single-file через assets (XAML, images)
Trimming — зменшення розміру
Self-contained deployment включає весь .NET Runtime, навіть якщо ваш додаток використовує тільки малу його частину. Trimming дозволяє видалити невикористаний код та зменшити розмір.
Увімкнення trimming
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
</PropertyGroup>
Або через CLI:
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishTrimmed=true
Як працює trimming
Trimmer аналізує ваш код та визначає, які частини .NET Runtime та бібліотек використовуються. Невикористаний код видаляється з output.
Приклад:
- Без trimming: 80 MB
- З trimming: 40-50 MB (економія ~40-50%)
Trim modes
link (агресивний):
<TrimMode>link</TrimMode>
Видаляє невикористані типи та методи з усіх assemblies (включаючи ваш код та NuGet пакети).
copyused (консервативний):
<TrimMode>copyused</TrimMode>
Видаляє тільки повністю невикористані assemblies, але не видаляє код всередині assemblies.
Рекомендація: Використовуйте link для максимальної економії, але ретельно тестуйте додаток.
Проблеми з trimming
Trimming може зламати додаток, якщо код використовує reflection, dynamic loading або інші runtime features:
// Цей код може зламатися після trimming
var type = Type.GetType("MyNamespace.MyClass");
var instance = Activator.CreateInstance(type);
Trimmer не може визначити, що MyClass використовується, і може видалити його.
Рішення 1: Trim warnings
Увімкніть trim warnings:
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>
Компілятор покаже warnings для потенційно проблемного коду.
Рішення 2: DynamicallyAccessedMembers attribute
Вкажіть, які члени типу використовуються через reflection:
public void CreateInstance(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
Type type)
{
var instance = Activator.CreateInstance(type);
}
Рішення 3: Trim root assemblies
Вкажіть assemblies, які не повинні триматися:
<ItemGroup>
<TrimmerRootAssembly Include="MyApp.Core" />
</ItemGroup>
Avalonia та trimming
Avalonia підтримує trimming, але потрібні додаткові налаштування:
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
<!-- Avalonia-specific -->
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<!-- Не тримати Avalonia assemblies -->
<TrimmableAssembly Include="Avalonia.Base" />
<TrimmableAssembly Include="Avalonia.Controls" />
<TrimmableAssembly Include="Avalonia.Markup.Xaml" />
</ItemGroup>
Важливо: Використовуйте Compiled Bindings замість Reflection Bindings для сумісності з trimming.
- Зберіть з trimming:
dotnet publish -p:PublishTrimmed=true - Запустіть додаток та протестуйте всі features
- Перевірте trim warnings:
dotnet publish -p:TrimmerSingleWarn=false - Виправте проблеми через attributes або TrimmerRootAssembly
- Повторіть тестування
NativeAOT — компіляція у нативний код
NativeAOT (Ahead-of-Time compilation) компілює .NET код у нативний машинний код до запуску. Це дає кілька переваг:
Переваги:
- Швидший старт (немає JIT compilation)
- Менший розмір (немає JIT compiler у runtime)
- Менше використання пам'яті
- Складніше reverse-engineer (нативний код замість IL)
Недоліки:
- Довша збірка
- Не підтримує всі .NET features (reflection, dynamic code generation)
- Експериментальна підтримка у Avalonia
Увімкнення NativeAOT
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
dotnet publish -c Release -r win-x64 -p:PublishAot=true
Avalonia та NativeAOT
На момент написання статті (2024) Avalonia має експериментальну підтримку NativeAOT. Основні проблеми:
- XAML parsing використовує reflection
- Деякі Avalonia features не працюють з AOT
- Потрібні додаткові налаштування
Рекомендація: Для production використовуйте trimming замість NativeAOT. NativeAOT для Avalonia ще не готовий для широкого використання.
Слідкуйте за Avalonia GitHub для оновлень щодо NativeAOT підтримки.
Кросплатформна розробка з Avalonia
Створення додатків, що працюють на Windows, Linux, macOS, Mobile та WebAssembly: структура проєкту, platform-specific код, native API та deployment
Розгортання WPF застосунків
Підготовка WPF-додатку для розповсюдження на Windows: ClickOnce, MSIX, MSI інсталятори, Squirrel для auto-updates, code signing та best practices