windows: Add installer build

For distribution of Radicle on Windows machines, Windows Installer
provides a well-known solution, that comes built in to the operating
system. It also integrates with the Windows package manager WinGet.

Provide an initial version of an `*.msi` build, and integrate it into
GitHub actions to allow downloads via build artifacts.
This commit is contained in:
Lorenz Leutgeb 2026-02-13 00:25:37 +01:00 committed by Fintan Halpenny
parent c06b00e330
commit 5974fa32d0
6 changed files with 122 additions and 1 deletions

View File

@ -19,7 +19,11 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v6
with:
fetch-depth: 512
fetch-tags: true
filter: 'tree:0'
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
@ -30,6 +34,17 @@ jobs:
path: target path: target
key: ${{ matrix.os }}-${{ runner.arch }}-${{ hashFiles('Cargo.lock') }}-cargo-target key: ${{ matrix.os }}-${{ runner.arch }}-${{ hashFiles('Cargo.lock') }}-cargo-target
- run: cargo build --workspace --verbose --all-features - run: cargo build --workspace --verbose --all-features
- if: runner.os == 'Windows'
working-directory: windows
run: |
$env:RADICLE_VERSION = "$(.\version.ps1)"
echo $env:RADICLE_VERSION
dotnet build
- if: runner.os == 'Windows'
uses: actions/upload-artifact@v4
with:
name: radicle-installer-${{ runner.arch }}-${{ github.sha }}-${{ github.job }}
path: windows/bin/*/*.msi
- if: runner.os != 'Windows' - if: runner.os != 'Windows'
run: cargo test --workspace --verbose --all-features -- --nocapture run: cargo test --workspace --verbose --all-features -- --nocapture
- if: runner.os == 'Windows' - if: runner.os == 'Windows'

2
windows/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
bin
obj

62
windows/Package.wxs Normal file
View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8" ?>
<?ifndef env.CARGO_PROFILE ?>
<?define CARGO_PROFILE = "debug" ?>
<?else?>
<?define CARGO_PROFILE = $(env.CARGO_PROFILE) ?>
<?endif?>
<?ifndef env.CARGO_TARGET_DIR ?>
<?define CARGO_TARGET_DIR = "target" ?>
<?else?>
<?define CARGO_TARGET_DIR = $(env.CARGO_TARGET_DIR) ?>
<?endif?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Package
Id="xyz.radicle"
Name="Radicle"
Manufacturer="Radicle Team"
Version="$(env.RADICLE_VERSION)"
UpgradeCode="24fb0aa5-a2a7-4c6f-8283-4f2c8aaf5dd7"
Scope="perUser"
InstallerVersion="500"
Compressed="yes"
>
<MediaTemplate EmbedCab="yes" />
<MajorUpgrade DowngradeErrorMessage="A newer version of Radicle is already installed." />
<Property Id="ARPURLINFOABOUT" Value="https://www.radicle.xyz/" />
<StandardDirectory Id="LocalAppDataFolder">
<Directory Id="INSTALLFOLDER" Name="Radicle">
<Component Id="InstallFolderComponent" Guid="*">
<RemoveFolder On="uninstall" />
<RegistryKey Root="HKCU" Key="Software\Radicle\Components">
<RegistryValue Name="InstallFolder" Type="integer" Value="0" KeyPath="yes" />
</RegistryKey>
</Component>
<Directory Id="BinFolder" Name="bin">
<Component Id="BinFolderComponent" Guid="9d24e7f1-4c00-4cb4-9644-14ed3e0e7ebf">
<RemoveFolder Id="RemoveDirectoryOnUninstall" On="uninstall" />
<File Source="..\$(CARGO_TARGET_DIR)\$(CARGO_PROFILE)\rad.exe" />
<File Source="..\$(CARGO_TARGET_DIR)\$(CARGO_PROFILE)\radicle-node.exe" />
<File Source="..\$(CARGO_TARGET_DIR)\$(CARGO_PROFILE)\git-remote-rad.exe" />
<RegistryKey Root="HKCU" Key="Software\Radicle\Components">
<RegistryValue Name="BinFolder" Type="integer" Value="0" KeyPath="yes" />
</RegistryKey>
</Component>
<Component Id="BinFolderInPathComponent" Guid="c37b7983-5f94-4c07-a1bd-40fe31b9e468">
<Environment Id="envPath" Name="PATH" Value="[BinFolder]" Action="set" System="no" Part="last" Permanent="no" />
<RegistryKey Root="HKCU" Key="Software\Radicle\Components">
<RegistryValue Name="BinFolderInPath" Type="integer" Value="0" KeyPath="yes" />
</RegistryKey>
</Component>
</Directory>
</Directory>
</StandardDirectory>
<Feature Id="Main" Title="Radicle" Level="1">
<ComponentRef Id="InstallFolderComponent" />
<ComponentRef Id="BinFolderComponent" />
<ComponentRef Id="BinFolderInPathComponent" />
</Feature>
</Package>
</Wix>

17
windows/README.md Normal file
View File

@ -0,0 +1,17 @@
# Radicle for Windows
## Prerequisites
### Code-Signing
For `signtool.exe`, install [Windows SDK].
### WiX Toolset
Requires [.NET] SDK in version 6.0 or higher.
See [WiX Toolset Tutorial].
[Windows SDK]: https://learn.microsoft.com/windows/apps/windows-sdk/downloads
[WiX Toolset Tutorial]: https://docs.firegiant.com/wix/tutorial/
[.NET]: https://dotnet.microsoft.com/download/dotnet/

5
windows/Radicle.wixproj Normal file
View File

@ -0,0 +1,5 @@
<Project Sdk="WixToolset.Sdk/6.0.0">
<PropertyGroup>
<SuppressIces>ICE91</SuppressIces>
</PropertyGroup>
</Project>

20
windows/version.ps1 Normal file
View File

@ -0,0 +1,20 @@
$prefix = "releases"
$describe = git describe --match "$prefix/*.*.*" --exclude "$prefix/*.*.*-*.*" --candidates=1 2>$null
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($describe)) {
Write-Error -Category ObjectNotFound "tag not found"
exit 1
}
if (-not ($describe.Trim() -match "^$prefix/(\d+)\.(\d+)\.(\d+)(?:-(\d+)-g[0-9a-f]+)?$")) {
Write-Error -Category ParserError "tag not recognized"
exit 1
}
$major = [int]$matches[1]
$minor = [int]$matches[2]
$patch = [int]$matches[3]
$commits = if ([string]::IsNullOrEmpty($matches[4])) { 0 } else { [int]$matches[4] }
Write-Output "$major.$minor.$patch.$commits"