Files
rustdesk-windows-build/.gitea/workflows/windows.yml
T
2026-09-13 02:38:21 +03:00

846 lines
23 KiB
YAML

name: RustDesk Windows x64
on:
workflow_dispatch:
env:
RUSTDESK_VERSION: "1.4.9"
jobs:
generate-bridge:
name: Generate Flutter Rust Bridge
runs-on: rustdesk-android
steps:
- name: Clone RustDesk 1.4.9
shell: bash
run: |
set -euxo pipefail
rm -rf rustdesk bridge-artifact
git clone \
--branch "${RUSTDESK_VERSION}" \
--depth 1 \
https://github.com/rustdesk/rustdesk.git \
rustdesk
git -C rustdesk submodule update \
--init \
--recursive
- name: Generate bridge with Flutter 3.22.3
shell: bash
run: |
set -euxo pipefail
export PATH="/opt/flutter-3.22.3/bin:/opt/rust/cargo/bin:${PATH}"
flutter --version
rustc --version
flutter_rust_bridge_codegen --version
cd rustdesk
# RustDesk 1.4.9 bridge is generated with Flutter 3.22.3.
# extended_text 14 requires newer Dart, therefore upstream
# temporarily uses 13.0.0 for bridge generation.
sed -i \
's/extended_text: 14.0.0/extended_text: 13.0.0/g' \
flutter/pubspec.yaml
rm -rf flutter/.dart_tool
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
test -f ./src/bridge_generated.rs
test -f ./src/bridge_generated.io.rs
test -f ./flutter/lib/generated_bridge.dart
test -f ./flutter/lib/generated_bridge.freezed.dart
- name: Prepare bridge artifact
shell: bash
run: |
set -euxo pipefail
rm -rf bridge-artifact
mkdir -p bridge-artifact/src
mkdir -p bridge-artifact/flutter/lib
mkdir -p bridge-artifact/flutter/macos/Runner
mkdir -p bridge-artifact/flutter/ios/Runner
cp rustdesk/src/bridge_generated.rs \
bridge-artifact/src/
cp rustdesk/src/bridge_generated.io.rs \
bridge-artifact/src/
cp rustdesk/flutter/lib/generated_bridge.dart \
bridge-artifact/flutter/lib/
cp rustdesk/flutter/lib/generated_bridge.freezed.dart \
bridge-artifact/flutter/lib/
cp rustdesk/flutter/macos/Runner/bridge_generated.h \
bridge-artifact/flutter/macos/Runner/
cp rustdesk/flutter/ios/Runner/bridge_generated.h \
bridge-artifact/flutter/ios/Runner/
- name: Upload bridge artifact
uses: https://github.com/actions/upload-artifact@v7
with:
name: rustdesk-bridge-1.4.9
path: bridge-artifact/
if-no-files-found: error
retention-days: 7
build-windows:
name: Build Windows x64
needs:
- generate-bridge
runs-on: rustdesk-windows
env:
RUSTUP_HOME: C:\BuildTools\rustup
CARGO_HOME: C:\BuildTools\cargo
FLUTTER_ROOT: C:\BuildTools\flutter-3.24.5
PUB_CACHE: C:\BuildTools\pub-cache
LIBCLANG_PATH: C:\Program Files\LLVM\bin
VCPKG_ROOT: C:\vcpkg
steps:
- name: Validate Windows builder
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
Write-Host "===== Runner user ====="
whoami
Write-Host "`n===== Rust ====="
rustc --version
cargo --version
rustfmt --version
Write-Host "`n===== Flutter ====="
flutter --version
Write-Host "`n===== LLVM ====="
clang --version
Write-Host "`n===== Python ====="
python --version
Write-Host "`n===== NASM ====="
nasm -v
Write-Host "`n===== vcpkg ====="
vcpkg version
if (-not (Test-Path "$env:LIBCLANG_PATH\libclang.dll")) {
throw "libclang.dll not found"
}
if (-not (Test-Path "$env:FLUTTER_ROOT\bin\flutter.bat")) {
throw "Flutter not found"
}
if (-not (Test-Path "$env:VCPKG_ROOT\vcpkg.exe")) {
throw "vcpkg not found"
}
$target = rustup target list --installed
if ($target -notcontains "x86_64-pc-windows-msvc") {
throw "x86_64-pc-windows-msvc Rust target missing"
}
$flutterPatch = git `
-C "$env:FLUTTER_ROOT" `
status --short
if ($flutterPatch -notmatch "dropdown_menu.dart") {
throw "RustDesk Flutter patch is missing"
}
$engine = Join-Path `
$env:FLUTTER_ROOT `
"bin\cache\artifacts\engine\windows-x64-release"
foreach ($file in @(
"flutter_windows.dll",
"flutter_windows.dll.lib",
"gen_snapshot.exe"
)) {
if (-not (Test-Path (Join-Path $engine $file))) {
throw "Missing RustDesk custom Flutter engine file: $file"
}
}
Write-Host "Windows build environment OK"
- name: Clone RustDesk 1.4.9
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
Remove-Item `
".\rustdesk" `
-Recurse `
-Force `
-ErrorAction SilentlyContinue
Remove-Item `
".\bridge-download" `
-Recurse `
-Force `
-ErrorAction SilentlyContinue
git -c core.longpaths=true clone `
--branch "$env:RUSTDESK_VERSION" `
--depth 1 `
https://github.com/rustdesk/rustdesk.git `
rustdesk
if ($LASTEXITCODE -ne 0) {
throw "RustDesk clone failed"
}
git `
-C rustdesk `
-c core.longpaths=true `
submodule update `
--init `
--recursive
if ($LASTEXITCODE -ne 0) {
throw "RustDesk submodule checkout failed"
}
Write-Host "`n===== Checking Flutter Windows project ====="
$windowsCmake = "rustdesk\flutter\windows\CMakeLists.txt"
if (-not (Test-Path $windowsCmake)) {
throw "RustDesk checkout does not contain $windowsCmake"
}
Get-Item $windowsCmake
Write-Host "Flutter Windows project exists after clone"
- name: Restore generated bridge
uses: https://github.com/actions/download-artifact@v8
with:
name: rustdesk-bridge-1.4.9
path: bridge-download
- name: Copy generated bridge into RustDesk
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$files = @{
"bridge-download\src\bridge_generated.rs" =
"rustdesk\src\bridge_generated.rs"
"bridge-download\src\bridge_generated.io.rs" =
"rustdesk\src\bridge_generated.io.rs"
"bridge-download\flutter\lib\generated_bridge.dart" =
"rustdesk\flutter\lib\generated_bridge.dart"
"bridge-download\flutter\lib\generated_bridge.freezed.dart" =
"rustdesk\flutter\lib\generated_bridge.freezed.dart"
}
foreach ($source in $files.Keys) {
$destination = $files[$source]
if (-not (Test-Path $source)) {
throw "Bridge artifact file missing: $source"
}
Copy-Item `
$source `
$destination `
-Force
Write-Host "$source -> $destination"
}
Write-Host "`nChecking Windows Flutter project after bridge restore..."
if (-not (Test-Path "rustdesk\flutter\windows\CMakeLists.txt")) {
throw "flutter/windows disappeared after bridge restore"
}
Write-Host "flutter/windows/CMakeLists.txt OK"
- name: Verify bridge
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$files = @(
"rustdesk\src\bridge_generated.rs",
"rustdesk\src\bridge_generated.io.rs",
"rustdesk\flutter\lib\generated_bridge.dart",
"rustdesk\flutter\lib\generated_bridge.freezed.dart"
)
foreach ($file in $files) {
if (-not (Test-Path $file)) {
throw "Missing bridge file: $file"
}
Write-Host "$file OK"
}
- name: Patch RustDesk server configuration
shell: pwsh
env:
ID_SERVER: ${{ secrets.RUSTDESK_ID_SERVER }}
PUB_KEY: ${{ secrets.RUSTDESK_KEY }}
run: |
$ErrorActionPreference = "Stop"
if ([string]::IsNullOrWhiteSpace($env:ID_SERVER)) {
throw "RUSTDESK_ID_SERVER secret is empty"
}
if ([string]::IsNullOrWhiteSpace($env:PUB_KEY)) {
throw "RUSTDESK_KEY secret is empty"
}
@'
import json
import os
import re
from pathlib import Path
path = Path(r"rustdesk/libs/hbb_common/src/config.rs")
if not path.exists():
raise SystemExit(f"config.rs not found: {path}")
server = os.environ["ID_SERVER"].strip()
key = os.environ["PUB_KEY"].strip()
text = path.read_text(encoding="utf-8")
rendezvous_pattern = (
r"pub const RENDEZVOUS_SERVERS\s*:\s*[^=]+?="
r"\s*&\[(?:.|\n)*?\];"
)
rendezvous_replacement = (
"pub const RENDEZVOUS_SERVERS: &[&str] = &["
+ json.dumps(server)
+ "];"
)
text, rendezvous_count = re.subn(
rendezvous_pattern,
rendezvous_replacement,
text,
count=1,
)
key_pattern = (
r"pub const RS_PUB_KEY\s*:\s*[^=]+?="
r"\s*.*?;"
)
key_replacement = (
"pub const RS_PUB_KEY: &str = "
+ json.dumps(key)
+ ";"
)
text, key_count = re.subn(
key_pattern,
key_replacement,
text,
count=1,
flags=re.S,
)
if rendezvous_count != 1:
raise SystemExit(
f"RENDEZVOUS_SERVERS replacement count: {rendezvous_count}"
)
if key_count != 1:
raise SystemExit(
f"RS_PUB_KEY replacement count: {key_count}"
)
path.write_text(text, encoding="utf-8")
print("RENDEZVOUS_SERVERS patched")
print("RS_PUB_KEY patched")
'@ | python -
if ($LASTEXITCODE -ne 0) {
throw "RustDesk configuration patch failed"
}
- name: Verify pinned vcpkg
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$expected = "120deac3062162151622ca4860575a33844ba10b"
$actual = git `
-C "$env:VCPKG_ROOT" `
rev-parse HEAD
Write-Host "vcpkg commit: $actual"
if ($actual -ne $expected) {
throw "Wrong vcpkg commit: $actual"
}
- name: Install RustDesk vcpkg dependencies
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
Set-Location rustdesk
$env:VCPKG_DEFAULT_HOST_TRIPLET = "x64-windows-static"
& "$env:VCPKG_ROOT\vcpkg.exe" `
install `
--triplet x64-windows-static `
--x-install-root="$env:VCPKG_ROOT\installed"
if ($LASTEXITCODE -ne 0) {
throw "vcpkg install failed"
}
- name: Build RustDesk
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$repo = (Resolve-Path "rustdesk").Path
$windowsDir = Join-Path $repo "flutter\windows"
$windowsCmake = Join-Path $windowsDir "CMakeLists.txt"
if (-not (Test-Path $windowsCmake)) {
Write-Host "flutter/windows missing before build, restoring from Git..."
git `
-C $repo `
restore `
--source=HEAD `
-- `
flutter/windows
if ($LASTEXITCODE -ne 0) {
throw "Unable to restore flutter/windows before build"
}
}
if (-not (Test-Path $windowsCmake)) {
throw "flutter/windows/CMakeLists.txt is missing before build"
}
Write-Host "===== Windows Flutter project before Rust build ====="
Get-Item $windowsCmake
# Reproduce the Windows part of RustDesk 1.4.9 build.py manually.
# This lets us restore flutter/windows AFTER `flutter pub get` and
# BEFORE CMake is started.
Write-Host "`n===== Build virtual display dylib ====="
Push-Location (Join-Path $repo "libs\virtual_display\dylib")
try {
cargo build --locked --release
if ($LASTEXITCODE -ne 0) {
throw "virtual_display dylib build failed"
}
}
finally {
Pop-Location
}
Write-Host "`n===== Build RustDesk Rust library ====="
Push-Location $repo
try {
cargo build `
--locked `
--features "hwcodec,vram,flutter" `
--lib `
--release
if ($LASTEXITCODE -ne 0) {
throw "RustDesk Rust library build failed"
}
if (-not (Test-Path ".\target\release\librustdesk.dll")) {
throw "target\release\librustdesk.dll was not produced"
}
}
finally {
Pop-Location
}
Write-Host "`n===== Flutter pub get ====="
$flutterDir = Join-Path $repo "flutter"
Push-Location $flutterDir
try {
flutter pub get
if ($LASTEXITCODE -ne 0) {
throw "flutter pub get failed"
}
}
finally {
Pop-Location
}
Write-Host "`n===== Restore Windows project after pub get ====="
# The failing runs showed that flutter/windows existed before
# build.py, but CMake could no longer see it after Flutter's
# dependency-resolution phase. Restore it at the last possible
# point, then build with --no-pub so Flutter does not run pub get
# again before invoking CMake.
git `
-C $repo `
restore `
--source=HEAD `
-- `
flutter/windows
if ($LASTEXITCODE -ne 0) {
throw "Unable to restore flutter/windows after flutter pub get"
}
if (-not (Test-Path $windowsCmake)) {
throw "flutter/windows/CMakeLists.txt is missing after restore"
}
Write-Host "Windows Flutter project immediately before CMake:"
Get-Item $windowsCmake
Write-Host "`n===== Flutter Windows release build ====="
Push-Location $flutterDir
try {
flutter build windows --release --no-pub
if ($LASTEXITCODE -ne 0) {
throw "Flutter Windows build failed"
}
}
finally {
Pop-Location
}
$release = Join-Path `
$repo `
"flutter\build\windows\x64\runner\Release"
if (-not (Test-Path (Join-Path $release "rustdesk.exe"))) {
throw "rustdesk.exe was not produced"
}
# build.py normally copies this DLL into the Flutter Release bundle.
$virtualDisplayDll = Join-Path `
$repo `
"target\release\deps\dylib_virtual_display.dll"
if (-not (Test-Path $virtualDisplayDll)) {
throw "dylib_virtual_display.dll was not produced"
}
Copy-Item `
$virtualDisplayDll `
(Join-Path $release "dylib_virtual_display.dll") `
-Force
$packageDir = Join-Path $repo "dist\rustdesk"
Remove-Item `
$packageDir `
-Recurse `
-Force `
-ErrorAction SilentlyContinue
New-Item `
-ItemType Directory `
-Force `
-Path $packageDir | Out-Null
Copy-Item `
"$release\*" `
"$packageDir\" `
-Recurse `
-Force
Write-Host ""
Write-Host "====================================="
Write-Host "RustDesk main Windows build completed"
Write-Host "====================================="
Write-Host "Release: $release"
- name: Build WindowInjection.dll
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
Set-Location rustdesk
$source = `
".\third-party\RustDeskTempTopMostWindow"
Remove-Item `
$source `
-Recurse `
-Force `
-ErrorAction SilentlyContinue
New-Item `
-ItemType Directory `
-Force `
-Path ".\third-party" | Out-Null
git clone `
https://github.com/rustdesk-org/RustDeskTempTopMostWindow `
$source
if ($LASTEXITCODE -ne 0) {
throw "RustDeskTempTopMostWindow clone failed"
}
git `
-C $source `
checkout `
ecd8d6a139eee76845ea66423fb739af450fda90
if ($LASTEXITCODE -ne 0) {
throw "RustDeskTempTopMostWindow checkout failed"
}
$vswhere = `
"${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
$msbuild = & $vswhere `
-latest `
-products * `
-find "MSBuild\**\Bin\MSBuild.exe" |
Select-Object -First 1
if (-not $msbuild) {
throw "MSBuild.exe not found"
}
Write-Host "MSBuild: $msbuild"
& $msbuild `
"$source\WindowInjection\WindowInjection.vcxproj" `
"-p:Configuration=Release" `
"-p:Platform=x64" `
"/p:TargetVersion=Windows10"
if ($LASTEXITCODE -ne 0) {
throw "WindowInjection.dll build failed"
}
$dll = `
"$source\WindowInjection\x64\Release\WindowInjection.dll"
if (-not (Test-Path $dll)) {
throw "WindowInjection.dll not found"
}
Copy-Item `
$dll `
".\dist\rustdesk\WindowInjection.dll" `
-Force
- name: Add usbmmidd
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
Set-Location rustdesk
$zip = ".\third-party\usbmmidd_v2.zip"
$extract = ".\third-party\usbmmidd"
Remove-Item `
$extract `
-Recurse `
-Force `
-ErrorAction SilentlyContinue
Invoke-WebRequest `
-Uri "https://github.com/rustdesk-org/rdev/releases/download/usbmmidd_v2/usbmmidd_v2.zip" `
-OutFile $zip
Expand-Archive `
-Path $zip `
-DestinationPath $extract `
-Force
$usb = Join-Path $extract "usbmmidd_v2"
if (-not (Test-Path $usb)) {
throw "usbmmidd_v2 directory not found"
}
Remove-Item `
"$usb\Win32" `
-Recurse `
-Force `
-ErrorAction SilentlyContinue
foreach ($file in @(
"deviceinstaller64.exe",
"deviceinstaller.exe",
"usbmmidd.bat"
)) {
Remove-Item `
(Join-Path $usb $file) `
-Force `
-ErrorAction SilentlyContinue
}
Copy-Item `
$usb `
".\dist\rustdesk\usbmmidd_v2" `
-Recurse `
-Force
- name: Prepare portable packer
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
Set-Location rustdesk
$runnerRes = Get-ChildItem `
".\flutter\build\windows\x64" `
-Filter "Runner.res" `
-Recurse `
-ErrorAction SilentlyContinue |
Select-Object -First 1
if ($runnerRes) {
Write-Host "Runner.res: $($runnerRes.FullName)"
Copy-Item `
$runnerRes.FullName `
".\libs\portable\Runner.res" `
-Force
}
else {
Write-Host "Runner.res not found, continuing"
}
# Same manifest adjustment used by upstream before
# generating the self-extracted portable executable.
$manifest = ".\res\manifest.xml"
$manifestContent = Get-Content $manifest
$manifestContent |
Where-Object { $_ -notmatch "dpiAware" } |
Set-Content `
$manifest `
-Encoding utf8
Push-Location ".\libs\portable"
try {
python -m pip install `
--disable-pip-version-check `
-r requirements.txt
if ($LASTEXITCODE -ne 0) {
throw "portable requirements installation failed"
}
python `
.\generate.py `
-f ..\..\dist\rustdesk\ `
-o . `
-e ..\..\dist\rustdesk\rustdesk.exe
if ($LASTEXITCODE -ne 0) {
throw "portable packer generation failed"
}
}
finally {
Pop-Location
}
$packer = `
".\target\release\rustdesk-portable-packer.exe"
if (-not (Test-Path $packer)) {
throw "rustdesk-portable-packer.exe not found"
}
$output = `
".\dist\rustdesk-1.4.9-custom-windows-x86_64.exe"
Copy-Item `
$packer `
$output `
-Force
$hash = Get-FileHash `
$output `
-Algorithm SHA256
$hashLine = `
"$($hash.Hash.ToLower()) $(Split-Path $output -Leaf)"
$hashLine | Set-Content `
"$output.sha256" `
-Encoding ASCII
Write-Host ""
Write-Host "============================================"
Write-Host "Windows portable build completed successfully"
Write-Host "============================================"
Write-Host $output
Write-Host $hashLine
- name: Upload Windows build
uses: https://github.com/actions/upload-artifact@v7
with:
name: rustdesk-1.4.9-custom-windows-x86_64
path: |
rustdesk/dist/rustdesk-1.4.9-custom-windows-x86_64.exe
rustdesk/dist/rustdesk-1.4.9-custom-windows-x86_64.exe.sha256
rustdesk/dist/rustdesk/
if-no-files-found: error
retention-days: 14