1. Introduction
2. Avalonia UI
3. Dependency Injection
4. MVVM Design
5. Dialogs and Tools
6. Unit Testing
7. Reactive
8. Deployment
9. Assembly Trimming
10. Multiple Environments
Your Avalonia app is working! Now what? You need to deploy it for Windows, Linux, MacOS, iOS, Android, Blazor...
PupNet Deploy is a great tool to facilitate deployment. As of writing this, it supports AppImage for Linux, setup file for Windows, Flatpak for Linux, Debian binary package, RPM binary package, and plain old zip. PupNet-Deploy will require pupnet.conf in your project.
PupNet-Deploy does not yet have support for MacOS, iOS and Android. That may come later.
The best approach is to create some script files that automate the builds. Create a Publish folder to place your scripts.
I create a script for each target OS that takes 3 parameters: project folder, target runtime and kind. Ex: eg: publish-win Player432Hz win-x64 setup
I then have another script that compiles all versions for a specific OS (x64, x86).
These scripts will parse your project files for project metadata and version, create a folder with the project version, and dump the release file in it.
To publish, I open the terminal and type
# on Windows
publish-win-all.bat
# on Linux
publish-linux-all
publish-osx-all
The releases then come with a long list of assets!
For Windows, you must build it in Windows (or in A Windows VM) because msbuild has a bug where icons and attributes do not get set when published on other platforms.
Here's my script for Windows:
If they fix msbuild on Linux/MacOS, I got a Bash version of the script too
This will create a ZIP file with your binaries plus a Setup file. One file for x86, and one file for x64.
For Linux, you must build it in Linux; or in WSL.
For Linux, the best option is to publish an AppImage. It creates a single file that runs on every Linux distro. You can also post your project on AppImageHub. Other options supported by PupNet Deploy are DBP, RPM and Flatpak.
Here's my script for Linux
It will create an AppImage file. One file for x64, and one file for arm64. You can click on it and your application will run. Your users can install AppImageLauncher for a better experience.
For Arch users, you can also publish your application in the AUR. It's easy to publish from the AppImage content, and your package should end with -appimage
sufix.
After having my account and credentials configured...
How to create a new repo in the AUR
This will fetch the latest Release from your GitHub repo.
First, create the new Git repo. This will create folder yangdownloader-appimage.git
in the current directory.
git clone ssh://aur@aur.archlinux.org/yangdownloader-appimage.git
cd yangdownloader-appimage.git
Second, add a PKGBUILD
file. Here we add a package that requires ffmpeg.
Third, you must generate the security checksum. This will generate/update sha256sums_x86_64
in PKGBUILD.
updpkgsums
Fourth, validate the package by building it
makepkg
If everything is good, publish it
makepkg --printsrcinfo > .SRCINFO
git add PKGBUILD .SRCINFO
git commit -m "useful commit message"
git push
Voilà!
How to update an AUR repo
First, publish a new Release on GitHub.
Second, update pkgver
in PKGBUILD.
Third, generate checksum with updpkgsums
.
Fourth, validate package with makepkg
.
Fifth, publish with the same commands as above.
To run your application on MacOS, it must be contained within an Application Bundle or it won't run at all. Furthermore, the latest arm64 M1 chipsets will refuse to run any code that is not signed! Signing your code requires an Apple Developper account at $99/year.
This tutorial explains how to publish an application to the OSX App Store. The Microsoft tutorial can also be useful.
Overall, it's a $99 per year investment and a LOT of work. Not suitable for free software. Oh, getting that Developer Account also requires a Mac device because your account will be bound to it.
Wait! There IS a work-around. To remove code-signing security warnings, run this command in the terminal:
/Applications % xattr -d com.apple.quarantine Converter432hz.app
Building for OSX requires Linux or MacOS to set the executable flag on the file.
My script will create the application bundle folder structure and zip it:
It also requires App.entitlements, Info.plist, and an icon file in ICNS format.
This will create a ZIP file with your bundle. One file for x64, and optionally one file for arm64. Note that x64 should run fine on arm64 anyway.
If you got native binaries that need to be deployed for all these platforms, I came up with this solution. Again, this is not a solution I've seen anyone recommend but that's how I do it.
Create a new project. For BASS audio libraries, I created BassDlls.csproj with this.
Add or '$(RuntimeIdentifier)'==''"
to the binaries you want at compile-time for development.
Add your binaries in each folder for the respective runtimes. Sample here.
By adding a reference to this project, your will automatically get all needed DLLs directly in your output folder!
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;osx-x64;osx-arm64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)'=='win-x64' or '$(RuntimeIdentifier)'==''">
<Content Include="win-x64\**" >
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)'=='win-x86'">
<Content Include="win-x86\**" >
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)'=='linux-x64' or '$(RuntimeIdentifier)'==''">
<Content Include="linux-x64\**" >
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)'=='linux-arm64'">
<Content Include="linux-arm64\**" >
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)'=='osx-x64'">
<Content Include="osx-x64\**" >
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)'=='osx-arm64'">
<Content Include="osx-x64\**" > <!-- Same binaries for osx-x64 and osx-arm64 -->
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
When referencing the project, set ReferenceOutputAssembly
to false to avoid having an empty project dll file.
<ProjectReference Include="..\..\..\Dll\Bass\BassDlls.csproj" ReferenceOutputAssembly="false" />
You can probably automate these builds with GitHub Actions.
If you come up with a good solution, you can post your instructions! You could also post how to automate the tests at the same time.