Files
rustdesk/.gitea/workflows/build-windows.yml
T
2026-05-04 19:46:47 +02:00

282 lines
13 KiB
YAML

name: build-windows
on:
push:
branches: [pro-features]
workflow_dispatch:
inputs:
version_suffix:
description: "Version suffix (e.g. 'cst', 'beta1'). Empty = vanilla."
type: string
default: "cst"
env:
RUST_VERSION: "1.75"
LLVM_VERSION: "15.0.6"
FLUTTER_VERSION: "3.24.5"
VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b"
CARGO_EXPAND_VERSION: "1.0.95"
FLUTTER_RUST_BRIDGE_VERSION: "1.80.1"
# Numeric base, must match Cargo.toml's <major>.<minor>.<patch>.
# MSI ProductVersion is forced to this (Windows Installer rejects non-numeric).
VERSION_BASE: "1.4.6"
# Default suffix on push events. workflow_dispatch can override per-run.
VERSION_SUFFIX: ${{ inputs.version_suffix || 'cst' }}
jobs:
build-x64:
name: build-windows-x64
runs-on: [self-hosted, windows-10]
timeout-minutes: 240
env:
VCPKG_ROOT: C:\vcpkg
VCPKG_BINARY_SOURCES: "clear;files,C:\\vcpkg-cache,readwrite"
steps:
- name: Checkout source
uses: actions/checkout@v4
with:
submodules: recursive
- name: Verify host toolchain
shell: pwsh
run: |
$required = 'node','pwsh','git','bash','python','rustc','cargo','rustup','clang','flutter','nuget','cmake','ninja','dotnet'
$missing = @()
foreach ($tool in $required) {
$cmd = Get-Command $tool -ErrorAction SilentlyContinue
if (-not $cmd) { $missing += $tool; continue }
$ver = & $tool --version 2>&1 | Select-Object -First 1
Write-Host ("{0,-10} {1} ({2})" -f $tool, $cmd.Source, $ver)
}
if ($missing.Count -gt 0) {
Write-Error ("Missing tools on runner: {0}. Re-run provision.ps1 or install manually." -f ($missing -join ', '))
exit 1
}
if (-not $env:VCPKG_ROOT -or -not (Test-Path "$env:VCPKG_ROOT\vcpkg.exe")) {
Write-Error "VCPKG_ROOT not set or vcpkg.exe missing at $env:VCPKG_ROOT"
exit 1
}
Write-Host "VCPKG_ROOT $env:VCPKG_ROOT"
- name: Compute version strings
shell: pwsh
run: |
$base = "${env:VERSION_BASE}"
$suffix = "${env:VERSION_SUFFIX}"
if ($base -notmatch '^\d+\.\d+\.\d+$') {
Write-Error "VERSION_BASE '$base' must be major.minor.patch numeric"
exit 1
}
if ($suffix) { $display = "$base-$suffix" } else { $display = $base }
Write-Host ("Base : {0} (used for MSI ProductVersion)" -f $base)
Write-Host ("Suffix : {0}" -f $suffix)
Write-Host ("Display : {0} (used for exe filename + Cargo.toml)" -f $display)
"VERSION_DISPLAY=$display" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Patch Cargo.toml with display version
shell: bash
run: |
# Cargo accepts SemVer-style suffix with a hyphen (e.g. 1.4.6-cst).
sed -i -E "0,/^version[[:space:]]*=/{s/^version[[:space:]]*=[[:space:]]*\"${VERSION_BASE}\"/version = \"${VERSION_DISPLAY}\"/}" Cargo.toml
echo "--- Cargo.toml [package] ---"
awk '/^\[package\]/{f=1} f; /^\[/&&!/^\[package\]/{f=0}' Cargo.toml | head -10
- name: Ensure Rust toolchain configured for runner user
shell: pwsh
run: |
# provision.ps1 ran rustup as an admin user; act_runner runs as LocalSystem
# which has its own (empty) $RUSTUP_HOME. Bootstrap the default toolchain
# for LocalSystem on first run -- subsequent runs are no-ops.
rustup --version
rustup toolchain install $env:RUST_VERSION --profile minimal --component rustfmt
if ($LASTEXITCODE -ne 0) { throw "rustup toolchain install failed ($LASTEXITCODE)" }
rustup default $env:RUST_VERSION
if ($LASTEXITCODE -ne 0) { throw "rustup default failed ($LASTEXITCODE)" }
rustup target add x86_64-pc-windows-msvc
rustc --version
cargo --version
- name: Install flutter_rust_bridge codegen tools
shell: pwsh
run: |
# Pin the install destination with --root so we don't have to guess at
# the runner's $USERPROFILE (act_runner runs as LocalSystem on this host,
# which makes $USERPROFILE a non-obvious system path).
$tools = 'C:\cargo-tools'
New-Item -ItemType Directory -Force -Path "$tools\bin" | Out-Null
cargo install --root $tools cargo-expand --version "$env:CARGO_EXPAND_VERSION" --locked
if ($LASTEXITCODE -ne 0) { throw "cargo install cargo-expand failed ($LASTEXITCODE)" }
cargo install --root $tools flutter_rust_bridge_codegen --version "$env:FLUTTER_RUST_BRIDGE_VERSION" --features uuid --locked
if ($LASTEXITCODE -ne 0) { throw "cargo install flutter_rust_bridge_codegen failed ($LASTEXITCODE)" }
Write-Host "--- $tools\bin ---"
Get-ChildItem "$tools\bin" | Format-Table Name, Length, LastWriteTime
$expected = Join-Path $tools 'bin\flutter_rust_bridge_codegen.exe'
if (-not (Test-Path $expected)) { throw "missing: $expected" }
Add-Content -Path $env:GITHUB_PATH -Value "$tools\bin"
- name: Generate Rust <-> Dart bridge
shell: bash
run: |
# Belt-and-suspenders: also prepend cargo-tools to PATH here, in case
# GITHUB_PATH propagation from the previous step doesn't take effect.
export PATH="/c/cargo-tools/bin:$PATH"
command -v flutter_rust_bridge_codegen || { echo "tool not on PATH; PATH=$PATH"; exit 1; }
pushd flutter && flutter pub get && popd
flutter_rust_bridge_codegen \
--rust-input ./src/flutter_ffi.rs \
--dart-output ./flutter/lib/generated_bridge.dart \
--c-output ./flutter/macos/Runner/bridge_generated.h
cp ./flutter/macos/Runner/bridge_generated.h ./flutter/ios/Runner/bridge_generated.h
- name: Replace Flutter engine with rustdesk custom engine
shell: pwsh
run: |
flutter precache --windows
Invoke-WebRequest -Uri https://github.com/rustdesk/engine/releases/download/main/windows-x64-release.zip -OutFile windows-x64-release.zip
Expand-Archive -Force -Path windows-x64-release.zip -DestinationPath windows-x64-release
$engineDir = "C:\tools\flutter\bin\cache\artifacts\engine\windows-x64-release"
New-Item -ItemType Directory -Force -Path $engineDir | Out-Null
Move-Item -Force windows-x64-release\* $engineDir\
- name: Patch Flutter (dropdown_menu enableFilter)
shell: bash
run: |
patch_file=".github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff"
if [ -f "$patch_file" ]; then
flutter_root="$(dirname "$(dirname "$(which flutter)")")"
cp "$patch_file" "$flutter_root/"
(cd "$flutter_root" && git apply "$(basename "$patch_file")" || true)
fi
- name: vcpkg install dependencies (x64-windows-static)
shell: bash
env:
VCPKG_DEFAULT_HOST_TRIPLET: x64-windows-static
run: |
mkdir -p /c/vcpkg-cache
if ! "$VCPKG_ROOT/vcpkg" install \
--triplet x64-windows-static \
--x-install-root="$VCPKG_ROOT/installed"; then
find "$VCPKG_ROOT/" -name "*.log" -exec sh -c 'echo "===== {} ====="; cat "{}"' \;
exit 1
fi
- name: Build RustDesk (build.py --portable --hwcodec --flutter --vram)
shell: pwsh
run: |
python .\build.py --portable --hwcodec --flutter --vram --skip-portable-pack
Move-Item -Force .\flutter\build\windows\x64\runner\Release .\rustdesk
- name: Bundle usbmmidd_v2 + printer driver
shell: pwsh
continue-on-error: true
run: |
Invoke-WebRequest -Uri https://github.com/rustdesk-org/rdev/releases/download/usbmmidd_v2/usbmmidd_v2.zip -OutFile usbmmidd_v2.zip
Expand-Archive -Force usbmmidd_v2.zip -DestinationPath .
Remove-Item -Recurse -Force usbmmidd_v2\Win32
Remove-Item -Force usbmmidd_v2\deviceinstaller64.exe, usbmmidd_v2\deviceinstaller.exe, usbmmidd_v2\usbmmidd.bat
Move-Item -Force .\usbmmidd_v2 .\rustdesk\
Invoke-WebRequest -Uri https://github.com/rustdesk/hbb_common/releases/download/driver/rustdesk_printer_driver_v4-1.4.zip -OutFile printer.zip
Invoke-WebRequest -Uri https://github.com/rustdesk/hbb_common/releases/download/driver/printer_driver_adapter.zip -OutFile printer_adapter.zip
Expand-Archive -Force printer.zip -DestinationPath .
Expand-Archive -Force printer_adapter.zip -DestinationPath .
New-Item -ItemType Directory -Force -Path .\rustdesk\drivers | Out-Null
Move-Item -Force .\rustdesk_printer_driver_v4-1.4 .\rustdesk\drivers\RustDeskPrinterDriver
Move-Item -Force .\printer_driver_adapter.dll .\rustdesk\
- name: Copy Runner.res for portable packer
shell: bash
continue-on-error: true
run: |
runner_res=$(find . -name Runner.res | head -1)
[ -n "$runner_res" ] && cp "$runner_res" ./libs/portable/Runner.res
- name: Build portable self-extracting exe
shell: bash
run: |
sed -i '/dpiAware/d' res/manifest.xml
pushd ./libs/portable
pip install -r requirements.txt
python ./generate.py -f ../../rustdesk/ -o . -e ../../rustdesk/rustdesk.exe
popd
mkdir -p ./SignOutput
mv ./target/release/rustdesk-portable-packer.exe "./SignOutput/rustdesk-${VERSION_DISPLAY}-x86_64.exe"
- name: Build MSI installer
shell: pwsh
run: |
Push-Location .\res\msi
# Pass numeric VERSION_BASE explicitly: MSI ProductVersion must be numeric,
# so we cannot let preprocess.py auto-detect from rustdesk.exe (which now
# carries the suffixed VERSION_DISPLAY).
python preprocess.py --arp -d ..\..\rustdesk -v "${env:VERSION_BASE}"
# Resolve MSBuild from the installed VS Build Tools.
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
$msbuild = & $vswhere -latest -requires Microsoft.Component.MSBuild `
-find "MSBuild\**\Bin\MSBuild.exe" | Select-Object -First 1
if (-not $msbuild) { throw "MSBuild not found via vswhere" }
# Two-stage restore covers both project flavors in this solution:
# - CustomActions.vcxproj uses old-style packages.config -> nuget restore
# - Package.wixproj is SDK-style PackageReference -> msbuild -t:Restore
nuget restore msi.sln
& $msbuild msi.sln -t:Restore -p:Configuration=Release -p:Platform=x64
if ($LASTEXITCODE -ne 0) { throw "MSBuild restore failed ($LASTEXITCODE)" }
& $msbuild msi.sln -p:Configuration=Release -p:Platform=x64 /p:TargetVersion=Windows10
if ($LASTEXITCODE -ne 0) { throw "MSBuild build failed ($LASTEXITCODE)" }
Move-Item -Force .\Package\bin\x64\Release\en-us\Package.msi "..\..\SignOutput\rustdesk-${env:VERSION_DISPLAY}-x86_64.msi"
Pop-Location
- name: Report signing status of build artifacts
shell: pwsh
run: |
$artifacts = Get-ChildItem .\SignOutput -Include *.exe,*.msi -File
if (-not $artifacts) {
Write-Warning "No artifacts found in SignOutput\"
return
}
$unsigned = @()
foreach ($f in $artifacts) {
$sig = Get-AuthenticodeSignature -FilePath $f.FullName
$size = '{0,8:N0}' -f $f.Length
switch ($sig.Status) {
'Valid' {
Write-Host ("[ SIGNED ] {0} ({1} bytes) signed by: {2}" -f $f.Name, $size, $sig.SignerCertificate.Subject)
}
'NotSigned' {
Write-Host ("[UNSIGNED] {0} ({1} bytes)" -f $f.Name, $size)
$unsigned += $f.Name
}
default {
Write-Host ("[ {0,-7} ] {1} ({2} bytes) -- {3}" -f $sig.Status, $f.Name, $size, $sig.StatusMessage)
$unsigned += $f.Name
}
}
}
if ($unsigned.Count -gt 0) {
# Render a Gitea/GHA-style annotation so it shows up prominently in the run summary.
$list = $unsigned -join ', '
Write-Host "::warning title=Unsigned artifacts::$list -- SmartScreen will warn end users. Wire up signing before distributing."
}
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: rustdesk-windows-x64-${{ github.sha }}
path: |
SignOutput/rustdesk-*.exe
SignOutput/rustdesk-*.msi
if-no-files-found: warn
retention-days: 14