# Gitea Actions 适配副本:由 .github/workflows 同名文件适配(本地路径/cache/artifact 差异),修改时请同步两边。差异说明见 setup.md「Gitea 部署附录」。 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;files,D:\vcpkg-cache,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: ./.gitea/workflows/fetch-encrypted-secrets.yml with: zip_url_json: ${{ inputs.zip_url }} generate-bridge: uses: ./.gitea/workflows/bridge.yml with: version: ${{ inputs.version }} build-RustDeskTempTopMostWindow: needs: setup uses: ./.gitea/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 with: path: .rdgen-src - uses: actions/download-artifact@v4 with: name: encrypted-secrets-zip - name: Load Secrets uses: ./.gitea/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: 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@v4 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: | Copy-Item .rdgen-src/.github/patches/allowCustom.py allowCustom.py python allowCustom.py # Remove Setup Server Tip Copy-Item .rdgen-src/.github/patches/removeSetupServerTip.diff 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 {\n' ' format!("{} Printer", app_name)') new = ('fn get_port_name(app_name: &str) -> Vec {\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: | Copy-Item .rdgen-src/.github/patches/xoffline.diff 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: ${{ github.repository }} ref: ${{ github.ref_name }} 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@v4 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@v4 with: name: encrypted-secrets-zip