Files
rdgen/.github/workflows/sh-generator-windows.yml
T

672 lines
32 KiB
YAML

name: Custom Windows Client Generator
run-name: Custom Windows Client Generator
on:
workflow_dispatch:
inputs:
version:
description: 'version to buld'
required: true
default: ''
type: string
zip_url:
description: 'url to zip of json'
required: true
default: ''
type: string
env:
SCITER_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503, also 1.78 has ABI change which causes our sciter version not working, https://blog.rust-lang.org/2024/03/30/i128-layout-update.html
RUST_VERSION: "1.75" # sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html
CARGO_NDK_VERSION: "3.1.2"
SCITER_ARMV7_CMAKE_VERSION: "3.29.7"
SCITER_NASM_DEBVERSION: "2.15.05-1"
LLVM_VERSION: "15.0.6"
FLUTTER_VERSION: "3.24.5"
ANDROID_FLUTTER_VERSION: "3.24.5"
# for arm64 linux because official Dart SDK does not work
FLUTTER_ELINUX_VERSION: "3.16.9"
TAG_NAME: "${{ inputs.upload-tag }}"
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
# vcpkg version: 2024.07.12
VCPKG_COMMIT_ID: "${{ inputs.version == 'master' && '9e593bb18ea69cc5095e012465dcd675a822ed0d' || '120deac3062162151622ca4860575a33844ba10b' }}"
ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836"
VERSION: "${{ inputs.version }}"
NDK_VERSION: "r28c"
#signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}"
UPLOAD_ARTIFACT: 'true'
SIGN_BASE_URL: "${{ secrets.SIGN_BASE_URL }}"
STATUS_URL: "${{ secrets.GENURL }}/updategh"
jobs:
setup:
uses: ./.github/workflows/fetch-encrypted-secrets.yml
with:
zip_url_json: ${{ inputs.zip_url }}
generate-bridge:
uses: ./.github/workflows/bridge.yml
with:
version: ${{ inputs.version }}
build-RustDeskTempTopMostWindow:
needs: setup
uses: ./.github/workflows/third-party-RustDeskTempTopMostWindow.yml
with:
upload-artifact: true
target: windows-2022
configuration: Release
platform: x64
target_version: Windows10
secrets: inherit
strategy:
fail-fast: false
build-for-windows-flutter:
name: Build Windows
needs: [build-RustDeskTempTopMostWindow, generate-bridge, setup]
runs-on: self-hosted
strategy:
fail-fast: false
matrix:
job:
# - { target: i686-pc-windows-msvc , os: windows-2022 }
# - { target: x86_64-pc-windows-gnu , os: windows-2022 }
- {
target: x86_64-pc-windows-msvc,
os: windows-2022,
arch: x86_64,
vcpkg-triplet: x64-windows-static,
}
# - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 }
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.github/actions/decrypt-secrets
with:
zip_password: ${{ secrets.ZIP_PASSWORD }}
- name: Finalize and Cleanup zip/json
if: always() # Run even if previous steps fail
continue-on-error: true
uses: fjogeleit/http-request-action@v1
with:
url: "${{ secrets.GENURL }}/cleanzip"
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ env.uuid }}"}'
- name: Export GitHub Actions cache environment variables
uses: actions/github-script@v6
with:
script: |
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Set rdgen value
if: ${{ env.rdgen == 'true' }}
run: |
echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $env:GITHUB_ENV
- name: Set rdgen value
if: ${{ env.rdgen == 'false' }}
run: |
echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $env:GITHUB_ENV
- name: Checkout source code
if: ${{ env.VERSION != 'master' }}
uses: actions/checkout@v4
with:
repository: rustdesk/rustdesk
ref: refs/tags/${{ env.VERSION }}
submodules: recursive
- name: Checkout source code
if: ${{ env.VERSION == 'master' }}
uses: actions/checkout@v4
with:
repository: rustdesk/rustdesk
submodules: recursive
- name: Restore bridge files
uses: actions/download-artifact@master
with:
name: bridge-artifact
path: ./
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Setup NuGet
uses: NuGet/setup-nuget@v2
with:
nuget-version: 'latest'
- name: change appname to custom
if: env.appname != 'rustdesk'
continue-on-error: true
shell: bash
run: |
# ./Cargo.toml
sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./Cargo.toml
# ./libs/portable/Cargo.toml
sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./libs/portable/Cargo.toml
sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./libs/portable/Cargo.toml
sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./libs/portable/Cargo.toml
sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./libs/portable/Cargo.toml
# ./libs/portable/src/main.rs
sed -i -e 's|const APP_PREFIX: \&str = "rustdesk";|const APP_PREFIX: \&str = "${{ env.appname }}";|' ./libs/portable/src/main.rs
# ./flutter/windows/runner/Runner.rc
sed -i -e 's|"RustDesk Remote Desktop"|"${{ env.appname }}"|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|VALUE "InternalName", "rustdesk" "\0"|VALUE "InternalName", "${{ env.appname }}" "\0"|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|"rustdesk.exe"|"${{ env.filename }}"|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|"RustDesk"|"${{ env.appname }}"|' ./flutter/windows/runner/Runner.rc
# ./src/lang/en.rs
# change powered by rustdek to powered by compname
if [ ! -z "${{ env.compname }}" ]; then
find ./src/lang -name "*.rs" -exec sed -i '/powered_by_me/s|RustDesk|${{ env.compname }}|g' {} \;
fi
find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \;
sed -i -e 's|RustDesk|${{ env.appname }}|' ./res/msi/Package/License.rtf
- name: fix registry if appname has a space
if: contains(env.appname, ' ')
continue-on-error: true
shell: bash
run: |
#./src/platform/windows.rs
sed -i -e 's|reg add {}|reg add \\\"{}\\\"|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext} /f|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext}\\\\DefaultIcon /f|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\\DefaultIcon\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell /f|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\\open /f|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\\open\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\\open\\\\command|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\\open\\\\command\\\"|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\{ext} /f|reg add \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\{ext}\\\\shell /f|reg add \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\\open /f|reg add \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\\open\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\\open\\\\command /f|reg add \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\\open\\\\command\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|{subkey}|\\\"{subkey}\\\"|' ./src/platform/windows.rs
sed -i -e 's|reg delete HKEY_CLASSES_ROOT\\\\.{ext} /f|reg delete \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg delete HKEY_CLASSES_ROOT\\\\{ext} /f|reg delete \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\" /f|' ./src/platform/windows.rs
- name: change company name
if: env.compname != 'Purslane Ltd'
continue-on-error: true
shell: bash
run: |
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./res/msi/preprocess.py
sed -i -e 's|r"Purslane(?: Tech Pte\\.)? Ltd"|"${{ env.compname }}"|' ./res/msi/preprocess.py
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./Cargo.toml
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./libs/portable/Cargo.toml
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./res/msi/Package/License.rtf
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./src/main.rs
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart
sed -i -e 's|PURSLANE|${{ env.compname }}|' ./res/msi/preprocess.py
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./res/msi/preprocess.py
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./Cargo.toml
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./libs/portable/Cargo.toml
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./res/msi/Package/License.rtf
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./src/main.rs
- name: change url to custom
if: env.urlLink != 'https://rustdesk.com'
continue-on-error: true
shell: bash
run: |
sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ env.urlLink }}|' ./build.py
sed -i -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ env.urlLink }}'));|" ./flutter/lib/common.dart
sed -i -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ env.urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart
sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart
sed -i -e "s|const url = 'https://rustdesk.com/';|const url = '${{ env.urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart
sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart
sed -i -e "s|https://rustdesk.com/privacy.html|${{ env.urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart
sed -i -e "s|rustdesk.com|${{ env.urlLink }}|" ./res/msi/Package/License.rtf
- name: change download link to custom
if: env.downloadLink != 'https://rustdesk.com/download'
continue-on-error: true
shell: bash
run: |
sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart
sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart
sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis
sed -i -e 's|&download_url|"${{ env.downloadLink }}"|' ./src/updater.rs
sed -i -e 's|$downloadUrl/$downloadFile|${{ env.downloadLink }}|' ./flutter/lib/desktop/widgets/update_progress.dart
- name: set server, serverPort, key, and apiserver
continue-on-error: true
shell: bash
env:
SERVER_DOMAIN: ${{ env.server }}
SERVER_PORT_INPUT: ${{ env.serverPort }}
SERVER_KEY: ${{ env.key }}
API_SERVER: ${{ env.apiServer }}
run: |
# Pass secrets via bash env vars to avoid quoting issues
if [ ! -f "./libs/hbb_common/src/config.rs" ]; then
echo "Error: config.rs not found!"
exit 1
fi
if [ ! -f "./src/common.rs" ]; then
echo "Error: common.rs not found!"
exit 1
fi
# Port must be a number; views.py defaults it to 21116
if ! [[ "$SERVER_PORT_INPUT" =~ ^[0-9]+$ ]]; then
echo "Error: serverPort must be a number, got: '$SERVER_PORT_INPUT'"
exit 1
fi
# Derive the port block from the rendezvous port
# (defaults 21116/21117/21118/21119)
SERVER_PORT=$SERVER_PORT_INPUT
RELAY_PORT=$((SERVER_PORT + 1))
WS_RENDEZVOUS_PORT=$((RELAY_PORT + 1))
WS_RELAY_PORT=$((WS_RENDEZVOUS_PORT + 1))
echo "Setting server: $SERVER_DOMAIN"
echo "Setting ports: $SERVER_PORT, $RELAY_PORT, $WS_RENDEZVOUS_PORT, $WS_RELAY_PORT"
sed -i -e "s|rs-ny.rustdesk.com|$SERVER_DOMAIN|" ./libs/hbb_common/src/config.rs
# Match on numeric value so the step is independent of defaults
sed -i -e "s|pub const RENDEZVOUS_PORT: i32 = [0-9][0-9]*;|pub const RENDEZVOUS_PORT: i32 = $SERVER_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|pub const RELAY_PORT: i32 = [0-9][0-9]*;|pub const RELAY_PORT: i32 = $RELAY_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|pub const WS_RENDEZVOUS_PORT: i32 = [0-9][0-9]*;|pub const WS_RENDEZVOUS_PORT: i32 = $WS_RENDEZVOUS_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|pub const WS_RELAY_PORT: i32 = [0-9][0-9]*;|pub const WS_RELAY_PORT: i32 = $WS_RELAY_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|$SERVER_KEY|" ./libs/hbb_common/src/config.rs
sed -i -e "s|https://admin.rustdesk.com|$API_SERVER|" ./src/common.rs
echo "=== Verification ==="
grep -nE "RENDEZVOUS_PORT|RELAY_PORT|WS_" ./libs/hbb_common/src/config.rs
# ./flutter/pubspec.yaml
#sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml
- name: allow custom.txt
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
command: |
Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.py -OutFile allowCustom.py
python allowCustom.py
# Remove Setup Server Tip
Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff -OutFile removeSetupServerTip.diff
git apply removeSetupServerTip.diff
- name: Patch remote printer port to spool file
shell: python
run: |
import os, re
src = "."
app = os.environ.get("appname") or "RustDesk"
literal = "C:\\\\ProgramData\\\\%s\\\\printer-spool\\\\job.prn" % app
# atch Rust remote_printer lib.rs
rs = os.path.join(src, "libs", "remote_printer", "src", "lib.rs")
if os.path.isfile(rs):
with open(rs, "r", encoding="utf-8") as f:
t = f.read()
if "printer-spool" in t:
print("Rust printer port already points to spool file")
else:
old = ('fn get_port_name(app_name: &str) -> Vec<u16> {\n'
' format!("{} Printer", app_name)')
new = ('fn get_port_name(app_name: &str) -> Vec<u16> {\n'
' let base = std::env::var("ProgramData")\n'
' .unwrap_or_else(|_| "C:\\\\ProgramData".to_string());\n'
' let dir = format!("{}\\\\{}\\\\printer-spool", base, app_name);\n'
' let _ = std::fs::create_dir_all(&dir);\n'
' format!("{}\\\\job.prn", dir)')
if old in t:
with open(rs, "w", encoding="utf-8") as f:
f.write(t.replace(old, new, 1))
print("Successfully patched Rust printer port")
else:
print("Warning: get_port_name() signature not matched in Rust source")
# patch C++ MSI Custom Action
cpp = os.path.join(src, "res", "msi", "CustomActions", "RemotePrinter.cpp")
if os.path.isfile(cpp):
with open(cpp, "r", encoding="utf-8") as f:
t = f.read()
if "printer-spool" in t:
print("MSI printer port already points to spool file")
else:
pattern = r'(RD_PRINTER_PORT\s*=\s*)L"[^"]*"'
new_t, n = re.subn(pattern, lambda m: m.group(1) + 'L"%s"' % literal, t, count=1)
if n == 1:
with open(cpp, "w", encoding="utf-8") as f:
f.write(new_t)
print("Successfully patched MSI CustomAction printer port")
else:
print("Warning: RD_PRINTER_PORT not matched in RemotePrinter.cpp")
- name: magick stuff
if: ${{ env.iconlink_url != 'false' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
command: |
Invoke-WebRequest -Uri ${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}"&"uuid=${{ env.iconlink_uuid }} -OutFile ./res/iconx.png
mv ./res/icon.ico ./res/icon.ico.bak
mv ./res/icon.png ./res/icon.png.bak
mv ./res/tray-icon.ico ./res/tray-icon.ico.bak
mv ./res/iconx.png ./res/icon.png
mv ./res/32x32.png ./res/32x32.png.bak
mv ./res/64x64.png ./res/64x64.png.bak
mv ./res/128x128.png ./res/128x128.png.bak
mv ./res/128x128@2x.png ./res/128x128@2x.png.bak
magick ./res/icon.png -define icon:auto-resize=256,64,48,32,16 ./res/icon.ico
cp ./res/icon.ico ./res/tray-icon.ico
magick ./res/icon.png -resize 32x32 ./res/32x32.png
magick ./res/icon.png -resize 64x64 ./res/64x64.png
magick ./res/icon.png -resize 128x128 ./res/128x128.png
magick ./res/128x128.png -resize 200% ./res/128x128@2x.png
- name: ui.rs icon
if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true
shell: bash
run: |
cp ./src/ui.rs ./src/ui.rs.bak
b64=$(base64 -w0 < ./res/icon.png)
perl -0777 -pe "s|iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHL[A-Za-z0-9+/=]+|$b64|g" -i.bak ./src/ui.rs
b64=""
- name: fix connection delay
continue-on-error: true
if: ${{ env.delayFix == 'true' }}
shell: bash
run: |
# Precise fix: only extend the direct-connection condition with the
# key check, instead of globally replacing !key.is_empty() with
# false (which would also disable TCP encryption and UDP logic).
sed -i -e 's#if is_local || peer_nat_type == NatType::SYMMETRIC {#if is_local || peer_nat_type == NatType::SYMMETRIC || !key.is_empty() {#' ./src/client.rs
grep -n "key.is_empty()" ./src/client.rs || true
- name: use X for offline display instead of orange circle
if: env.xOffline == 'true'
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
continue_on_error: true
command: |
Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff -OutFile xoffline.diff
git apply xoffline.diff
- name: removeNewVersionNotif
continue-on-error: true
if: env.removeNewVersionNotif == 'true'
shell: bash
run: |
sed -i -e 's|updateUrl.isNotEmpty|false|' ./flutter/lib/desktop/pages/desktop_home_page.dart
sed -i '/let (request, url) =/,/Ok(())/{/Ok(())/!d}' ./src/common.rs
- name: replace flutter icons
if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true
run: |
cd ./flutter
#flutter pub upgrade win32
flutter pub get
flutter pub run flutter_launcher_icons
cd ..
- name: Checkout printer-adapter crate
uses: actions/checkout@v4
with:
repository: bryangerlach/rdgen
ref: master
path: ./temp-repo
- name: Move printer-adapter into place
shell: pwsh
run: |
if (Test-Path "./temp-repo/printer-adapter") {
Move-Item -Path "./temp-repo/printer-adapter" -Destination "./printer-adapter" -Force
Remove-Item -Recurse -Force "./temp-repo"
Write-Host "Successfully placed printer-adapter into workspace."
} else {
Write-Host "Error: printer-adapter folder not found in repository."
}
- name: Build rustdesk
run: |
# Windows: build RustDesk
python3 .\build.py --portable --hwcodec --flutter --vram --skip-portable-pack
mv ./flutter/build/windows/x64/runner/Release ./rustdesk
# Download usbmmidd_v2.zip and extract it to ./rustdesk
Invoke-WebRequest -Uri https://github.com/rustdesk-org/rdev/releases/download/usbmmidd_v2/usbmmidd_v2.zip -OutFile usbmmidd_v2.zip
Expand-Archive usbmmidd_v2.zip -DestinationPath . -Force
Remove-Item -Path usbmmidd_v2\Win32 -Recurse
Remove-Item -Path "usbmmidd_v2\deviceinstaller64.exe", "usbmmidd_v2\deviceinstaller.exe", "usbmmidd_v2\usbmmidd.bat"
mv -Force .\usbmmidd_v2 ./rustdesk
# Download and install driver package
try {
Invoke-WebRequest -Uri https://github.com/rustdesk/hbb_common/releases/download/driver/rustdesk_printer_driver_v4-1.4.zip -OutFile rustdesk_printer_driver_v4-1.4.zip
Expand-Archive rustdesk_printer_driver_v4-1.4.zip -DestinationPath .
mkdir ./rustdesk/drivers -ErrorAction SilentlyContinue
mv -Force .\rustdesk_printer_driver_v4-1.4 ./rustdesk/drivers/RustDeskPrinterDriver
} catch {
Write-Host "Warning: Printer driver download failed."
}
# Build and install open printer adapter
if (Test-Path "./printer-adapter/Cargo.toml") {
Write-Host "Building open printer-adapter..."
pushd ./printer-adapter
cargo build --release --locked
popd
if (Test-Path "./printer-adapter/target/release/printer_driver_adapter.dll") {
cp -Force "./printer-adapter/target/release/printer_driver_adapter.dll" "./rustdesk/printer_driver_adapter.dll"
Write-Host "Replaced printer_driver_adapter.dll with open implementation."
} else {
Write-Host "Error: printer-adapter build succeeded but DLL was not found."
}
} else {
Write-Host "Warning: ./printer-adapter/Cargo.toml not found, remote printing will remain disabled."
}
- name: icon stuff
if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true
run: |
mv ./rustdesk/data/flutter_assets/assets/icon.svg ./rustdesk/data/flutter_assets/assets/icon.svg.bak
magick ./res/icon.png ./rustdesk/data/flutter_assets/assets/icon.svg
- name: logo stuff
if: ${{ env.logolink_url != 'false' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
continue_on_error: true
command: |
Invoke-WebRequest -Uri ${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}"&"uuid=${{ env.logolink_uuid }} -OutFile ./rustdesk/data/flutter_assets/assets/logo.png
- name: find Runner.res
# Windows: find Runner.res (compiled from ./flutter/windows/runner/Runner.rc), copy to ./Runner.res
# Runner.rc does not contain actual version, but Runner.res does
continue-on-error: true
shell: bash
run: |
runner_res=$(find . -name "Runner.res");
if [ "$runner_res" == "" ]; then
echo "Runner.res: not found";
else
echo "Runner.res: $runner_res";
cp $runner_res ./libs/portable/Runner.res;
echo "list ./libs/portable/Runner.res";
ls -l ./libs/portable/Runner.res;
fi
- name: Download RustDeskTempTopMostWindow artifacts
uses: actions/download-artifact@master
if: env.UPLOAD_ARTIFACT == 'true'
with:
name: topmostwindow-artifacts
path: "./rustdesk"
- name: zip dlls
continue-on-error: true
shell: pwsh
run: |
Compress-Archive -Path ./rustdesk/*.dll, ./rustdesk/*.exe -DestinationPath ./rustdesk/unsigned_files.zip -CompressionLevel Fastest
- name: sign dlls
continue-on-error: true
shell: bash
run: |
if [ ! -z "${{ secrets.SIGN_BASE_URL }}" ] && [ ! -z "${{ secrets.SIGN_API_KEY }}" ]; then
curl -X POST -F "file=@./rustdesk/unsigned_files.zip" \
-H "X-API-KEY: ${{ secrets.SIGN_API_KEY }}" \
-m 900 \
"${{ secrets.SIGN_BASE_URL }}/sign/" -o ./rustdesk/signed_files.zip
else
echo "Signing skipped - signing URL or API key not configured"
cp ./rustdesk/unsigned_files.zip ./rustdesk/signed_files.zip
fi
- name: unzip dlls
continue-on-error: true
shell: pwsh
run: |
Expand-Archive -Path ./rustdesk/signed_files.zip -DestinationPath ./rustdesk/ -Force
Remove-Item ./rustdesk/unsigned_files.zip
Remove-Item ./rustdesk/signed_files.zip
- name: Create custom.txt file
shell: bash
run: |
echo -n "${{ env.custom }}" | cat > ./rustdesk/custom_.txt
- name: Build self-extracted executable
shell: bash
if: env.UPLOAD_ARTIFACT == 'true'
run: |
mv "./rustdesk/rustdesk.exe" "./rustdesk/${{ env.appname }}.exe" || echo "rustdesk.exe"
sed -i '/dpiAware/d' res/manifest.xml
pushd ./libs/portable
pip3 install -r requirements.txt
python3 ./generate.py -f ../../rustdesk/ -o . -e "../../rustdesk/${{ env.appname }}.exe"
popd
mkdir -p ./SignOutput
mv ./target/release/rustdesk-portable-packer.exe "./SignOutput/rustdesk.exe"
- name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v2
- name: Build msi
continue-on-error: true
if: env.UPLOAD_ARTIFACT == 'true'
run: |
$myappname = "${{ env.appname }}" -replace '\s','_'
cp "rustdesk/${{ env.appname }}.exe" "rustdesk/${myappname}.exe" -ErrorAction SilentlyContinue
pushd ./res/msi
python preprocess.py --app-name "$myappname" --arp -d ../../rustdesk
nuget restore msi.sln
msbuild msi.sln -p:Configuration=Release -p:Platform=x64 /p:TargetVersion=Windows10
cp ./Package/bin/x64/Release/en-us/Package.msi ../../SignOutput/rustdesk-latest.msi
mv ./Package/bin/x64/Release/en-us/Package.msi ../../SignOutput/rustdesk.msi
sha256sum ../../SignOutput/rustdesk.msi
- name: zip exe and msi
continue-on-error: true
shell: pwsh
run: |
Compress-Archive -Path ./SignOutput/*.exe, ./SignOutput/*.msi -DestinationPath ./SignOutput/unsigned_files.zip -CompressionLevel Fastest
- name: sign exe and msi
continue-on-error: true
shell: bash
run: |
if [ ! -z "${{ secrets.SIGN_BASE_URL }}" ] && [ ! -z "${{ secrets.SIGN_API_KEY }}" ]; then
curl -X POST -F "file=@./SignOutput/unsigned_files.zip" \
-H "X-API-KEY: ${{ secrets.SIGN_API_KEY }}" \
-m 900 \
"${{ secrets.SIGN_BASE_URL }}/sign/" -o ./SignOutput/signed_files.zip
else
echo "Signing skipped - signing URL or API key not configured"
cp ./SignOutput/unsigned_files.zip ./SignOutput/signed_files.zip
fi
- name: unzip exe and msi
continue-on-error: true
shell: pwsh
run: |
Expand-Archive -Path ./SignOutput/signed_files.zip -DestinationPath ./SignOutput/ -Force
Remove-Item ./SignOutput/unsigned_files.zip
Remove-Item ./SignOutput/signed_files.zip
- name: rename rustdesk.exe to filename.exe
run: |
mv ./SignOutput/rustdesk.exe "./SignOutput/${{ env.filename }}.exe" || echo "rustdesk"
- name: rename rustdesk.msi to filename.msi
continue-on-error: true
run: |
mv ./SignOutput/rustdesk.msi "./SignOutput/${{ env.filename }}.msi" || echo "rustdesk"
- name: send file to rdgen server
if: ${{ env.rdgen == 'true' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash
command: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.exe" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.msi" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client || true
- name: send file to api server
if: ${{ env.rdgen == 'false' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash
command: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.exe" ${{ env.apiServer }}/api/save_custom_client
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.msi" ${{ env.apiServer }}/api/save_custom_client || true
cleanup:
needs: [build-for-windows-flutter]
runs-on: ubuntu-latest
continue-on-error: true
if: always()
steps:
- name: Delete secrets artifact
uses: geekyeggo/delete-artifact@v5
with:
name: encrypted-secrets-zip