diff --git a/.github/actions/decrypt-secrets/action.yml b/.github/actions/decrypt-secrets/action.yml index 5ee3699ca..6846eae78 100644 --- a/.github/actions/decrypt-secrets/action.yml +++ b/.github/actions/decrypt-secrets/action.yml @@ -16,22 +16,32 @@ runs: shell: bash run: | pip install pyzipper + - name: Decrypt and Mask shell: python + env: + PYTHONUTF8: "1" + PYTHONIOENCODING: "utf-8" run: | + import sys + import io import pyzipper import json import os + # Полная замена стандартного вывода на UTF-8 с игнорированием ошибок кодировки + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + with pyzipper.AESZipFile('${{ inputs.zip_path }}') as zf: zf.setpassword('${{ inputs.zip_password }}'.encode()) with zf.open('secrets.json') as f: secrets = json.load(f) - with open(os.environ['GITHUB_ENV'], 'a') as env_file: + with open(os.environ['GITHUB_ENV'], 'a', encoding='utf-8') as env_file: for key, value in secrets.items(): if value: print(f"::add-mask::{value}") env_file.write(f"{key}={value}\n") - + print(f"Successfully masked {len(secrets)} secrets.") \ No newline at end of file diff --git a/.github/workflows/bridge.yml b/.github/workflows/bridge.yml index a51cb6abc..9dd05b01a 100644 --- a/.github/workflows/bridge.yml +++ b/.github/workflows/bridge.yml @@ -32,15 +32,15 @@ jobs: steps: - name: Checkout source code if: ${{ inputs.version != 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk ref: refs/tags/${{ inputs.version }} submodules: recursive - + - name: Checkout source code if: ${{ inputs.version == 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk submodules: recursive @@ -75,9 +75,17 @@ jobs: with: prefix-key: bridge-${{ matrix.job.os }} + - name: Cache cargo git + uses: actions/cache@v5 + with: + path: ~/.cargo/git + key: bridge-cargo-git-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + bridge-cargo-git- + - name: Cache Bridge id: cache-bridge - uses: actions/cache@v3 + uses: actions/cache@v5 with: path: /tmp/flutter_rust_bridge key: vcpkg-${{ matrix.job.arch }} @@ -90,19 +98,28 @@ jobs: cache: true - name: Install flutter rust bridge deps - shell: bash - run: | - cargo install cargo-expand --version ${{ env.CARGO_EXPAND_VERSION }} --locked - cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" --locked - pushd flutter && sed -i -e 's/extended_text: 14.0.0/extended_text: 13.0.0/g' pubspec.yaml && flutter pub get && popd + uses: nick-fields/retry@v4 + with: + timeout_minutes: 10 + max_attempts: 3 + shell: bash + command: | + cargo install cargo-expand --version ${{ env.CARGO_EXPAND_VERSION }} --locked + cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" --locked + pushd flutter && sed -i -e 's/extended_text: 14.0.0/extended_text: 13.0.0/g' pubspec.yaml && flutter pub get && popd - name: Run flutter rust bridge - run: | - ~/.cargo/bin/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 + uses: nick-fields/retry@v4 + with: + timeout_minutes: 10 + max_attempts: 3 + shell: bash + command: | + ~/.cargo/bin/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: Upload Artifact - uses: actions/upload-artifact@master + uses: actions/upload-artifact@v7 with: name: bridge-artifact path: | diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 0d68ffe30..88bdd5af5 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -6,7 +6,7 @@ env: TAG_NAME: "nightly" VERSION: "1.3.9" -jobs: +jobs: build-rustdesk-web: name: build-rustdesk-web runs-on: ubuntu-24.04 @@ -16,7 +16,7 @@ jobs: RELEASE_NAME: web-basic steps: - name: Checkout source code - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: recursive @@ -63,7 +63,7 @@ jobs: popd - name: Upload APK artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: web path: flutter/build/rustdesk-${{ env.VERSION }}-${{ env.RELEASE_NAME }}.tar.gz \ No newline at end of file diff --git a/.github/workflows/clear-cache.yml b/.github/workflows/clear-cache.yml index 384630438..00a778919 100644 --- a/.github/workflows/clear-cache.yml +++ b/.github/workflows/clear-cache.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Clear cache - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | console.log("About to clear") @@ -34,4 +34,4 @@ jobs: with: accessed: true # Purge caches by their last accessed time (default) created: false # Purge caches by their created time (default) - max-age: 1 # in seconds \ No newline at end of file + max-age: 1 # in seconds diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 57edcf694..bfd7997b2 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -28,18 +28,18 @@ jobs: fi - name: Login to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@v4 if: env.CREATE_IMAGE == 'true' with: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 if: env.CREATE_IMAGE == 'true' - name: Build and push - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 if: env.CREATE_IMAGE == 'true' with: push: true diff --git a/.github/workflows/fetch-encrypted-secrets.yml b/.github/workflows/fetch-encrypted-secrets.yml index 5461b4c77..f69944e27 100644 --- a/.github/workflows/fetch-encrypted-secrets.yml +++ b/.github/workflows/fetch-encrypted-secrets.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download with Retry - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -39,7 +39,7 @@ jobs: else: raise e - name: Upload Encrypted Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: encrypted-secrets-zip path: secrets.zip diff --git a/.github/workflows/generator-android.yml b/.github/workflows/generator-android.yml index 92658bcdd..045c39077 100644 --- a/.github/workflows/generator-android.yml +++ b/.github/workflows/generator-android.yml @@ -1,6 +1,6 @@ name: Custom Android Client Generator run-name: Custom Android Client Generator -on: +on: workflow_dispatch: inputs: version: @@ -23,7 +23,7 @@ env: SCITER_NASM_DEBVERSION: "2.14-1" LLVM_VERSION: "15.0.6" FLUTTER_VERSION: "3.24.5" - ANDROID_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 }}" @@ -82,9 +82,9 @@ jobs: } steps: - name: Checkout Repository - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 + uses: actions/checkout@v6 + + - uses: actions/download-artifact@v8 with: name: encrypted-secrets-zip @@ -96,7 +96,7 @@ jobs: - name: Finalize and Cleanup zip/json if: always() # Run even if previous steps fail continue-on-error: true - uses: fjogeleit/http-request-action@v1 + uses: fjogeleit/http-request-action@v2 with: url: "${{ secrets.GENURL }}/cleanzip" method: 'POST' @@ -104,7 +104,7 @@ jobs: data: '{"uuid": "${{ env.uuid }}"}' - name: Free Disk Space (Ubuntu) - uses: jlumbroso/free-disk-space@main + uses: jlumbroso/free-disk-space@v1.3.1 with: tool-cache: false android: false @@ -115,7 +115,7 @@ jobs: swap-storage: false - name: Export GitHub Actions cache environment variables - uses: actions/github-script@v6 + uses: actions/github-script@v8 with: script: | core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); @@ -175,15 +175,15 @@ jobs: - name: Checkout source code if: ${{ env.VERSION != 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk ref: refs/tags/${{ env.VERSION }} submodules: recursive - + - name: Checkout source code if: ${{ env.VERSION == 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk submodules: recursive @@ -199,7 +199,7 @@ jobs: run: | cd $(dirname $(dirname $(which flutter))) [[ "3.24.5" == ${{env.ANDROID_FLUTTER_VERSION}} ]] && git apply ${{ github.workspace }}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff - + - uses: nttld/setup-ndk@v1 id: setup-ndk @@ -243,7 +243,7 @@ jobs: shell: bash - name: Restore bridge files - uses: actions/download-artifact@master + uses: actions/download-artifact@v8 with: name: bridge-artifact path: ./ @@ -262,7 +262,7 @@ jobs: ###########################################################echo "${{ env.iconbase64 }}" | base64 -d > ./res/icon.png - name: icon stuff if: ${{ env.iconlink_url != 'false' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -292,55 +292,196 @@ jobs: sed -i '/adaptive_icon_foreground:/a \ \ adaptive_icon_foreground_inset: 32' ./flutter/pubspec.yaml sed -i '/ic_launcher_background/d' ./flutter/android/app/src/main/res/values/colors.xml - - name: set server, key, and apiserver + - 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: | - sed -i -e 's|rs-ny.rustdesk.com|${{ env.server }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.key }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs - # ./flutter/pubspec.yaml - #sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml - + 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 + + if ! [[ "$SERVER_PORT_INPUT" =~ ^[0-9]+$ ]]; then + echo "Error: serverPort must be a number, got: '$SERVER_PORT_INPUT'" + exit 1 + fi + + 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 + sed -i -e "s|pub const RENDEZVOUS_PORT: i32 = [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]\+;|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]\+;|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]\+;|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 -n "RENDEZVOUS_PORT\|RELAY_PORT\|WS_" ./libs/hbb_common/src/config.rs + grep -n "rustdesk\|admin\." ./src/common.rs || true + - name: change appname to custom if: env.appname != 'rustdesk' continue-on-error: true - shell: bash + shell: python + env: + PYTHONUTF8: "1" + PYTHONIOENCODING: "utf-8" + APP_NAME: ${{ env.appname }} + FILE_NAME: ${{ env.filename }} + COMP_NAME: ${{ env.compname }} + run: | + import sys + import io + import os + import glob + + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + + app_name = os.environ['APP_NAME'] + file_name = os.environ.get('FILE_NAME', '') + comp_name = os.environ.get('COMP_NAME', '') + + def replace_in_file(path, old, new, encoding='utf-8'): + try: + with open(path, 'r', encoding=encoding, errors='replace') as f: + content = f.read() + if old in content: + content = content.replace(old, new) + with open(path, 'w', encoding=encoding) as f: + f.write(content) + print(f"✅ Updated: {path} ('{old}' -> '{new}')") + else: + print(f"⚠️ Not found in: {path} ('{old}')") + except Exception as e: + print(f"❌ Error in {path}: {e}") + + # Cargo.toml + replace_in_file('./Cargo.toml', 'description = "RustDesk Remote Desktop"', f'description = "{app_name}"') + replace_in_file('./Cargo.toml', 'ProductName = "RustDesk"', f'ProductName = "{app_name}"') + replace_in_file('./Cargo.toml', 'FileDescription = "RustDesk Remote Desktop"', f'FileDescription = "{app_name}"') + replace_in_file('./Cargo.toml', 'OriginalFilename = "rustdesk.exe"', f'OriginalFilename = "{app_name}.exe"') + replace_in_file('./Cargo.toml', 'name = "RustDesk"', f'name = "{app_name}"') + + # libs/portable/Cargo.toml + replace_in_file('./libs/portable/Cargo.toml', 'description = "RustDesk Remote Desktop"', f'description = "{app_name}"') + replace_in_file('./libs/portable/Cargo.toml', 'ProductName = "RustDesk"', f'ProductName = "{app_name}"') + replace_in_file('./libs/portable/Cargo.toml', 'FileDescription = "RustDesk Remote Desktop"', f'FileDescription = "{app_name}"') + replace_in_file('./libs/portable/Cargo.toml', 'OriginalFilename = "rustdesk.exe"', f'OriginalFilename = "{app_name}.exe"') + + # libs/portable/src/main.rs + replace_in_file('./libs/portable/src/main.rs', 'const APP_PREFIX: &str = "rustdesk";', f'const APP_PREFIX: &str = "{app_name}";') + + # flutter/windows/runner/Runner.rc + replace_in_file('./flutter/windows/runner/Runner.rc', '"RustDesk Remote Desktop"', f'"{app_name}"') + replace_in_file('./flutter/windows/runner/Runner.rc', 'VALUE "InternalName", "rustdesk" "\0"', f'VALUE "InternalName", "{app_name}" "\0"') + replace_in_file('./flutter/windows/runner/Runner.rc', '"Copyright © 2025 Purslane Ltd. All rights reserved."', '"Copyright © 2025"') + if file_name: + replace_in_file('./flutter/windows/runner/Runner.rc', '"rustdesk.exe"', f'"{file_name}"') + replace_in_file('./flutter/windows/runner/Runner.rc', '"RustDesk"', f'"{app_name}"') + + # src/lang/*.rs + lang_files = glob.glob('./src/lang/*.rs') + for lang_file in lang_files: + try: + with open(lang_file, 'r', encoding='utf-8', errors='replace') as f: + content = f.read() + modified = False + if comp_name: + lines = content.split('\n') + new_lines = [] + for line in lines: + if 'powered_by_me' in line: + line = line.replace('RustDesk', comp_name) + modified = True + new_lines.append(line) + content = '\n'.join(new_lines) + if 'RustDesk' in content: + content = content.replace('RustDesk', app_name) + modified = True + if modified: + with open(lang_file, 'w', encoding='utf-8') as f: + f.write(content) + print(f"✅ Updated: {lang_file}") + except Exception as e: + print(f"❌ Error in {lang_file}: {e}") + + # Android специфичные файлы + replace_in_file('./flutter/android/app/src/main/res/values/strings.xml', 'RustDesk', app_name) + replace_in_file('./flutter/lib/main.dart', "title: 'RustDesk'", f"title: '{app_name}'") + replace_in_file('./flutter/lib/main.dart', 'RustDesk', app_name) + replace_in_file('./flutter/lib/web/bridge.dart', "return 'RustDesk';", f"return '{app_name}';") + replace_in_file('./flutter/android/app/src/main/AndroidManifest.xml', 'android:label="RustDesk"', f'android:label="{app_name}"') + replace_in_file('./flutter/android/app/src/main/AndroidManifest.xml', 'android:label="RustDesk Input"', f'android:label="{app_name} Input"') + replace_in_file('./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt', 'RustDesk is Open', f'{app_name} is Open') + replace_in_file('./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt', 'Show Rustdesk', f'Show {app_name}') + replace_in_file('./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt', '"RustDesk"', f'"{app_name}"') + replace_in_file('./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt', '"RustDesk Service', f'"{app_name} Service') + replace_in_file('./flutter/lib/desktop/widgets/tabbar_widget.dart', '"RustDesk"', f'"{app_name}"') + replace_in_file('./libs/hbb_common/src/config.rs', '"RustDesk"', f'"{app_name}"') + + print("Done!") + + - name: change company name + if: env.compname != 'Purslane Ltd' + continue-on-error: true + shell: python + env: + PYTHONUTF8: "1" + PYTHONIOENCODING: "utf-8" + COMP_NAME: ${{ env.compname }} run: | - 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 - sed -i 's|name = "RustDesk"|name = "${{ env.appname }}"|' ./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 - sed -i -e 's|const APP_PREFIX: \&str = "rustdesk";|const APP_PREFIX: \&str = "${{ env.appname }}";|' ./libs/portable/src/main.rs - 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|"Copyright © 2025 Purslane Ltd. All rights reserved."|"Copyright © 2025"|' ./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 - find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \; - sed -i -e 's|RustDesk|${{ env.appname }}|' ./flutter/android/app/src/main/res/values/strings.xml - sed -i -e "s|title: 'RustDesk'|title: '${{ env.appname }}'|" ./flutter/lib/main.dart - sed -i -e "s|return 'RustDesk';|return '${{ env.appname }}';|" ./flutter/lib/web/bridge.dart - sed -i 's|android:label="RustDesk"|android:label="${{ env.appname }}"|' ./flutter/android/app/src/main/AndroidManifest.xml - sed -i 's|android:label="RustDesk Input"|android:label="${{ env.appname }} Input"|' ./flutter/android/app/src/main/AndroidManifest.xml - sed -i 's|RustDesk is Open|${{ env.appname }} is Open|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt - sed -i 's|Show Rustdesk|Show ${{ env.appname }}|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt - sed -i 's|"RustDesk"|"${{ env.appname }}"|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt - sed -i 's|"RustDesk Service|"${{ env.appname }} Service|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt - sed -i 's|RustDesk|${{ env.appname }}|' ./flutter/lib/main.dart - sed -i 's|"RustDesk"|"${{ env.appname }}"|' ./flutter/lib/desktop/widgets/tabbar_widget.dart - sed -i 's|"RustDesk"|"${{ env.appname }}"|' ./libs/hbb_common/src/config.rs + import sys + import io + import os + + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + + comp_name = os.environ['COMP_NAME'] + old_name = 'Purslane Ltd' + + def replace_in_file(path, old, new, encoding='utf-8'): + try: + with open(path, 'r', encoding=encoding, errors='replace') as f: + content = f.read() + if old in content: + content = content.replace(old, new) + with open(path, 'w', encoding=encoding) as f: + f.write(content) + print(f"✅ Updated: {path}") + else: + print(f"⚠️ Not found in: {path}") + except Exception as e: + print(f"❌ Error in {path}: {e}") + + replace_in_file('./flutter/lib/desktop/pages/desktop_setting_page.dart', old_name, comp_name) + replace_in_file('./Cargo.toml', old_name, comp_name) + replace_in_file('./libs/portable/Cargo.toml', old_name, comp_name) + + print("Done!") - name: change url to custom if: env.urlLink != 'https://rustdesk.com' continue-on-error: true shell: bash - run: | + 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 @@ -384,11 +525,12 @@ jobs: if: ${{ env.delayFix == 'true' }} shell: bash run: | - sed -i -e 's|!key.is_empty()|false|' ./src/client.rs + 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: add cycle monitors to toolbar if: env.cycleMonitor == 'true' - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -400,7 +542,7 @@ jobs: - name: use X for offline display instead of orange circle if: env.xOffline == 'true' - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -412,7 +554,7 @@ jobs: - name: hide-cm if: env.hidecm == 'true' - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -425,7 +567,7 @@ jobs: - name: removeNewVersionNotif continue-on-error: true if: env.removeNewVersionNotif == 'true' - run: | + 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 @@ -470,7 +612,7 @@ jobs: esac - name: Upload Rustdesk library to Artifacts - uses: actions/upload-artifact@master + uses: actions/upload-artifact@v7 with: name: librustdesk.so.${{ matrix.job.target }} path: ./target/${{ matrix.job.target }}/release/liblibrustdesk.so @@ -544,7 +686,7 @@ jobs: mkdir -p signed-apk; pushd signed-apk mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk ./${{ env.filename }}-${{ matrix.job.arch }}.apk popd - + - uses: r0adkll/sign-android-release@v1 name: Sign app APK continue-on-error: true @@ -562,32 +704,43 @@ jobs: - name: send file to rdgen server if: ${{ env.rdgen == 'true' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: - timeout_minutes: 1 + timeout_minutes: 10 max_attempts: 3 shell: bash command: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./signed-apk/${{ env.filename }}-${{ matrix.job.arch }}.apk" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./signed-apk/${{ env.filename }}-${{ matrix.job.arch }}.apk" \ + -F "uuid=${{ env.uuid }}" \ + ${{ secrets.GENURL }}/save_custom_client - name: send file to api server if: ${{ env.rdgen == 'false' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: - timeout_minutes: 1 + timeout_minutes: 10 max_attempts: 3 shell: bash command: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./signed-apk/${{ env.filename }}-${{ matrix.job.arch }}.apk" ${{ env.apiServer }}/api/save_custom_client + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./signed-apk/${{ env.filename }}-${{ matrix.job.arch }}.apk" \ + ${{ env.apiServer }}/api/save_custom_client deploy: needs: [build-rustdesk-android] runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 + uses: actions/checkout@v6 + + - uses: actions/download-artifact@v8 with: name: encrypted-secrets-zip @@ -599,7 +752,7 @@ jobs: - name: Finalize and Cleanup zip/json if: always() # Run even if previous steps fail continue-on-error: true - uses: fjogeleit/http-request-action@v1 + uses: fjogeleit/http-request-action@v2 with: url: "${{ secrets.GENURL }}/cleanzip" method: 'POST' @@ -616,7 +769,7 @@ jobs: run: | echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $GITHUB_ENV - - uses: geekyeggo/delete-artifact@v5 + - uses: geekyeggo/delete-artifact@v6 continue-on-error: true with: name: ${{ env.filename }}-*.deb @@ -628,6 +781,6 @@ jobs: if: always() steps: - name: Delete secrets artifact - uses: geekyeggo/delete-artifact@v5 + uses: geekyeggo/delete-artifact@v6 with: name: encrypted-secrets-zip \ No newline at end of file diff --git a/.github/workflows/generator-linux.yml b/.github/workflows/generator-linux.yml index 88865fd62..80cddcdba 100644 --- a/.github/workflows/generator-linux.yml +++ b/.github/workflows/generator-linux.yml @@ -1,6 +1,6 @@ name: Custom Linux Client Generator run-name: Custom Linux Client Generator -on: +on: workflow_dispatch: inputs: version: @@ -22,7 +22,7 @@ env: SCITER_NASM_DEBVERSION: "2.14-1" LLVM_VERSION: "15.0.6" FLUTTER_VERSION: "3.24.5" - ANDROID_FLUTTER_VERSION: "3.24.5" + ANDROID_FLUTTER_VERSION: "3.24.5" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" @@ -78,9 +78,9 @@ jobs: } steps: - name: Checkout Repository - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 + uses: actions/checkout@v6 + + - uses: actions/download-artifact@v8 with: name: encrypted-secrets-zip @@ -90,7 +90,7 @@ jobs: zip_password: ${{ secrets.ZIP_PASSWORD }} - name: Export GitHub Actions cache environment variables - uses: actions/github-script@v6 + uses: actions/github-script@v8 with: script: | core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); @@ -116,7 +116,7 @@ jobs: if [[ "${{ matrix.job.arch }}" == "x86_64" ]]; then sudo apt-get install -y qemu-user-static fi - + - name: Install dependencies run: | sudo apt-get update @@ -124,7 +124,7 @@ jobs: - name: Checkout source code if: ${{ env.VERSION != 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk ref: refs/tags/${{ env.VERSION }} @@ -132,14 +132,14 @@ jobs: - name: Checkout source code if: ${{ env.VERSION == 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk submodules: recursive - name: Set Swap Space if: ${{ matrix.job.arch == 'x86_64' }} - uses: pierotofy/set-swap-space@master + uses: pierotofy/set-swap-space@v1.0 with: swap-size-gb: 12 @@ -168,7 +168,7 @@ jobs: - name: Restore bridge files if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' - uses: actions/download-artifact@master + uses: actions/download-artifact@v8 with: name: bridge-artifact path: ./ @@ -203,7 +203,7 @@ jobs: - name: icon stuff if: ${{ env.iconlink_url != 'false' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -232,51 +232,201 @@ jobs: - name: change appname to custom if: env.appname != 'rustdesk' continue-on-error: true - shell: bash + shell: python + env: + PYTHONUTF8: "1" + PYTHONIOENCODING: "utf-8" + APP_NAME: ${{ env.appname }} + COMP_NAME: ${{ env.compname }} run: | - 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 - 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 - sed -i -e 's|const APP_PREFIX: \&str = "rustdesk";|const APP_PREFIX: \&str = "${{ env.appname }}";|' ./libs/portable/src/main.rs - find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \; - sed -i -e '/-p tmpdeb\/usr\/lib\/rustdesk/d' ./build.py + import sys + import io + import os + import glob + import re + + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + + app_name = os.environ['APP_NAME'] + comp_name = os.environ.get('COMP_NAME', '') + + def replace_in_file(path, old, new, encoding='utf-8'): + try: + with open(path, 'r', encoding=encoding, errors='replace') as f: + content = f.read() + if old in content: + content = content.replace(old, new) + with open(path, 'w', encoding=encoding) as f: + f.write(content) + print(f"✅ Updated: {path} ('{old}' -> '{new}')") + else: + print(f"⚠️ Not found in: {path} ('{old}')") + except Exception as e: + print(f"❌ Error in {path}: {e}") + + def delete_line_in_file(path, pattern, encoding='utf-8'): + try: + with open(path, 'r', encoding=encoding, errors='replace') as f: + lines = f.readlines() + new_lines = [l for l in lines if pattern not in l] + if len(new_lines) != len(lines): + with open(path, 'w', encoding=encoding) as f: + f.writelines(new_lines) + print(f"✅ Deleted line with '{pattern}' in: {path}") + else: + print(f"⚠️ Pattern not found in: {path} ('{pattern}')") + except Exception as e: + print(f"❌ Error in {path}: {e}") + + # Cargo.toml + replace_in_file('./Cargo.toml', 'description = "RustDesk Remote Desktop"', f'description = "{app_name}"') + replace_in_file('./Cargo.toml', 'ProductName = "RustDesk"', f'ProductName = "{app_name}"') + replace_in_file('./Cargo.toml', 'FileDescription = "RustDesk Remote Desktop"', f'FileDescription = "{app_name}"') + replace_in_file('./Cargo.toml', 'OriginalFilename = "rustdesk.exe"', f'OriginalFilename = "{app_name}.exe"') + + # libs/portable/Cargo.toml + replace_in_file('./libs/portable/Cargo.toml', 'description = "RustDesk Remote Desktop"', f'description = "{app_name}"') + replace_in_file('./libs/portable/Cargo.toml', 'ProductName = "RustDesk"', f'ProductName = "{app_name}"') + replace_in_file('./libs/portable/Cargo.toml', 'FileDescription = "RustDesk Remote Desktop"', f'FileDescription = "{app_name}"') + replace_in_file('./libs/portable/Cargo.toml', 'OriginalFilename = "rustdesk.exe"', f'OriginalFilename = "{app_name}.exe"') + + # libs/portable/src/main.rs + replace_in_file('./libs/portable/src/main.rs', 'const APP_PREFIX: &str = "rustdesk";', f'const APP_PREFIX: &str = "{app_name}";') + + # src/lang/*.rs + lang_files = glob.glob('./src/lang/*.rs') + for lang_file in lang_files: + try: + with open(lang_file, 'r', encoding='utf-8', errors='replace') as f: + content = f.read() + modified = False + if comp_name: + lines = content.split('\n') + new_lines = [] + for line in lines: + if 'powered_by_me' in line: + line = line.replace('RustDesk', comp_name) + modified = True + new_lines.append(line) + content = '\n'.join(new_lines) + if 'RustDesk' in content: + content = content.replace('RustDesk', app_name) + modified = True + if modified: + with open(lang_file, 'w', encoding='utf-8') as f: + f.write(content) + print(f"✅ Updated: {lang_file}") + except Exception as e: + print(f"❌ Error in {lang_file}: {e}") + + # Linux специфично — удалить строку из build.py + delete_line_in_file('./build.py', '-p tmpdeb/usr/lib/rustdesk') + + print("Done!") - name: change company name if: env.compname != 'Purslane Ltd' + continue-on-error: true + shell: python + env: + PYTHONUTF8: "1" + PYTHONIOENCODING: "utf-8" + COMP_NAME: ${{ env.compname }} + run: | + import sys + import io + import os + + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + + comp_name = os.environ['COMP_NAME'] + old_name = 'Purslane Ltd' + + def replace_in_file(path, old, new, encoding='utf-8'): + try: + with open(path, 'r', encoding=encoding, errors='replace') as f: + content = f.read() + if old in content: + content = content.replace(old, new) + with open(path, 'w', encoding=encoding) as f: + f.write(content) + print(f"✅ Updated: {path}") + else: + print(f"⚠️ Not found in: {path}") + except Exception as e: + print(f"❌ Error in {path}: {e}") + + # Linux специфичные файлы + replace_in_file('./flutter/lib/desktop/pages/desktop_setting_page.dart', old_name, comp_name) + replace_in_file('./Cargo.toml', old_name, comp_name) + replace_in_file('./libs/portable/Cargo.toml', old_name, comp_name) + + print("Done!") + + - 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: | - sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart - sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./Cargo.toml - sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./libs/portable/Cargo.toml - + 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 + + if ! [[ "$SERVER_PORT_INPUT" =~ ^[0-9]+$ ]]; then + echo "Error: serverPort must be a number, got: '$SERVER_PORT_INPUT'" + exit 1 + fi + + 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 + sed -i -e "s|pub const RENDEZVOUS_PORT: i32 = [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]\+;|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]\+;|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]\+;|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 -n "RENDEZVOUS_PORT\|RELAY_PORT\|WS_" ./libs/hbb_common/src/config.rs + grep -n "rustdesk\|admin\." ./src/common.rs || true + - name: allow custom.txt - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 shell: bash command: | - sed -i -e 's|rs-ny.rustdesk.com|${{ env.server }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.key }}|' ./libs/hbb_common/src/config.rs wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.py python allowCustom.py wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff git apply removeSetupServerTip.diff echo -n "${{ env.custom }}" | cat > ./custom_.txt # sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml - sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs - + - name: change url to custom if: env.urlLink != 'https://rustdesk.com' continue-on-error: true shell: bash - run: | + 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 @@ -299,11 +449,12 @@ jobs: if: ${{ env.delayFix == 'true' }} shell: bash run: | - sed -i -e 's|!key.is_empty()|false|' ./src/client.rs + 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: add cycle monitors to toolbar if: env.cycleMonitor == 'true' - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -314,7 +465,7 @@ jobs: - name: use X for offline display instead of orange circle if: env.xOffline == 'true' - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -325,7 +476,7 @@ jobs: - name: hide-cm if: env.hidecm == 'true' - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -333,17 +484,17 @@ jobs: command: | wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/hidecm.diff git apply hidecm.diff - + - name: removeNewVersionNotif continue-on-error: true if: env.removeNewVersionNotif == 'true' - run: | + 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: Restore bridge files if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' - uses: actions/download-artifact@master + uses: actions/download-artifact@v8 with: name: bridge-artifact path: ./ @@ -602,32 +753,76 @@ jobs: - name: send file to rdgen server if: ${{ env.rdgen == 'true' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: - timeout_minutes: 1 + timeout_minutes: 10 max_attempts: 3 shell: bash command: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.deb" -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=@./output/${{ env.filename }}-${{ matrix.job.arch }}.rpm" -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=@./output/${{ env.filename }}-suse-${{ matrix.job.arch }}.rpm" -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=@./output/${{ env.filename }}-${{ matrix.job.arch }}.pkg.tar.zst" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client || true + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.deb" \ + -F "uuid=${{ env.uuid }}" \ + ${{ secrets.GENURL }}/save_custom_client + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.rpm" \ + -F "uuid=${{ env.uuid }}" \ + ${{ secrets.GENURL }}/save_custom_client + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./output/${{ env.filename }}-suse-${{ matrix.job.arch }}.rpm" \ + -F "uuid=${{ env.uuid }}" \ + ${{ secrets.GENURL }}/save_custom_client + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.pkg.tar.zst" \ + -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 + uses: nick-fields/retry@v4 with: - timeout_minutes: 1 + timeout_minutes: 10 max_attempts: 3 shell: bash command: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.deb" ${{ env.apiServer }}/api/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.rpm" ${{ env.apiServer }}/api/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-suse-${{ matrix.job.arch }}.rpm" ${{ env.apiServer }}/api/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.pkg.tar.zst" ${{ env.apiServer }}/api/save_custom_client || true + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.deb" \ + ${{ env.apiServer }}/api/save_custom_client + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.rpm" \ + ${{ env.apiServer }}/api/save_custom_client + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./output/${{ env.filename }}-suse-${{ matrix.job.arch }}.rpm" \ + ${{ env.apiServer }}/api/save_custom_client + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.pkg.tar.zst" \ + ${{ env.apiServer }}/api/save_custom_client || true - name: Upload deb - uses: actions/upload-artifact@master + uses: actions/upload-artifact@v7 if: env.UPLOAD_ARTIFACT == 'true' with: name: ${{ env.filename }}-${{ matrix.job.arch }}.deb @@ -645,9 +840,9 @@ jobs: - { target: aarch64-unknown-linux-gnu, arch: aarch64 } steps: - name: Checkout Repository - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 + uses: actions/checkout@v6 + + - uses: actions/download-artifact@v8 with: name: encrypted-secrets-zip @@ -658,7 +853,7 @@ jobs: - name: Checkout source code if: ${{ env.VERSION != 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk ref: refs/tags/${{ env.VERSION }} @@ -666,13 +861,13 @@ jobs: - name: Checkout source code if: ${{ env.VERSION == 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk submodules: recursive - name: Download Binary - uses: actions/download-artifact@master + uses: actions/download-artifact@v8 with: name: ${{ env.filename }}-${{ matrix.job.arch }}.deb path: . @@ -734,23 +929,34 @@ jobs: - name: send file to rdgen server if: ${{ env.rdgen == 'true' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: - timeout_minutes: 1 + timeout_minutes: 10 max_attempts: 3 shell: bash command: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./appimage/${{ env.filename }}-${{ matrix.job.arch }}.AppImage" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client - + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./appimage/${{ env.filename }}-${{ matrix.job.arch }}.AppImage" \ + -F "uuid=${{ env.uuid }}" \ + ${{ secrets.GENURL }}/save_custom_client + - name: send file to api server if: ${{ env.rdgen == 'false' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: - timeout_minutes: 1 + timeout_minutes: 10 max_attempts: 3 shell: bash command: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./appimage/${{ env.filename }}-${{ matrix.job.arch }}.AppImage" ${{ env.apiServer }}/api/save_custom_client + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./appimage/${{ env.filename }}-${{ matrix.job.arch }}.AppImage" \ + ${{ env.apiServer }}/api/save_custom_client build-flatpak: name: Build flatpak ${{ matrix.job.target }}${{ matrix.job.suffix }} @@ -778,9 +984,9 @@ jobs: } steps: - name: Checkout Repository - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 + uses: actions/checkout@v6 + + - uses: actions/download-artifact@v8 with: name: encrypted-secrets-zip @@ -791,7 +997,7 @@ jobs: - name: Checkout source code if: ${{ env.VERSION != 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk ref: refs/tags/${{ env.VERSION }} @@ -799,13 +1005,13 @@ jobs: - name: Checkout source code if: ${{ env.VERSION == 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk submodules: recursive - name: Download Binary - uses: actions/download-artifact@master + uses: actions/download-artifact@v8 with: name: ${{ env.filename }}-${{ matrix.job.arch }}.deb path: . @@ -845,23 +1051,34 @@ jobs: - name: send file to rdgen server if: ${{ env.rdgen == 'true' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: - timeout_minutes: 1 + timeout_minutes: 10 max_attempts: 3 shell: bash command: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./flatpak/${{ env.filename }}-${{ matrix.job.arch }}.flatpak" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client - + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./flatpak/${{ env.filename }}-${{ matrix.job.arch }}.flatpak" \ + -F "uuid=${{ env.uuid }}" \ + ${{ secrets.GENURL }}/save_custom_client + - name: send file to api server if: ${{ env.rdgen == 'false' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: - timeout_minutes: 1 + timeout_minutes: 10 max_attempts: 3 shell: bash command: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./flatpak/${{ env.filename }}-${{ matrix.job.arch }}.flatpak" ${{ env.apiServer }}/api/save_custom_client + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./flatpak/${{ env.filename }}-${{ matrix.job.arch }}.flatpak" \ + ${{ env.apiServer }}/api/save_custom_client deploy: needs: [build-rustdesk-linux,build-flatpak,build-appimage] @@ -869,9 +1086,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 + uses: actions/checkout@v6 + + - uses: actions/download-artifact@v8 with: name: encrypted-secrets-zip @@ -883,7 +1100,7 @@ jobs: - name: Finalize and Cleanup zip/json if: always() # Run even if previous steps fail continue-on-error: true - uses: fjogeleit/http-request-action@v1 + uses: fjogeleit/http-request-action@v2 with: url: "${{ secrets.GENURL }}/cleanzip" method: 'POST' @@ -900,7 +1117,7 @@ jobs: run: | echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $GITHUB_ENV - - uses: geekyeggo/delete-artifact@v5 + - uses: geekyeggo/delete-artifact@v6 continue-on-error: true with: name: ${{ env.filename }}-*.deb @@ -912,6 +1129,6 @@ jobs: if: always() steps: - name: Delete secrets artifact - uses: geekyeggo/delete-artifact@v5 + uses: geekyeggo/delete-artifact@v6 with: name: encrypted-secrets-zip diff --git a/.github/workflows/generator-macos.yml b/.github/workflows/generator-macos.yml index dc84a04fa..c2d2ec5f3 100644 --- a/.github/workflows/generator-macos.yml +++ b/.github/workflows/generator-macos.yml @@ -1,6 +1,6 @@ name: Custom macOS Client Generator run-name: Custom macOS Client Generator -on: +on: workflow_dispatch: inputs: version: @@ -17,7 +17,7 @@ on: 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 - MAC_RUST_VERSION: "1.81" + MAC_RUST_VERSION: "1.81" CARGO_NDK_VERSION: "3.1.2" SCITER_ARMV7_CMAKE_VERSION: "3.29.7" SCITER_NASM_DEBVERSION: "2.14-1" @@ -78,9 +78,9 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 + uses: actions/checkout@v6 + + - uses: actions/download-artifact@v8 with: name: encrypted-secrets-zip @@ -92,7 +92,7 @@ jobs: - name: Finalize and Cleanup zip/json if: always() # Run even if previous steps fail continue-on-error: true - uses: fjogeleit/http-request-action@v1 + uses: fjogeleit/http-request-action@v2 with: url: "${{ secrets.GENURL }}/cleanzip" method: 'POST' @@ -100,7 +100,7 @@ jobs: data: '{"uuid": "${{ env.uuid }}"}' - name: Export GitHub Actions cache environment variables - uses: actions/github-script@v6 + uses: actions/github-script@v8 with: script: | core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); @@ -108,15 +108,15 @@ jobs: - name: Checkout source code if: ${{ env.VERSION != 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk ref: refs/tags/${{ env.VERSION }} submodules: recursive - + - name: Checkout source code if: ${{ env.VERSION == 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk submodules: recursive @@ -135,13 +135,13 @@ jobs: # MACSTUFF Backup the original files cp ./flutter/macos/Runner/Info.plist ./flutter/macos/Runner/Info.plist.bak cp ./flutter/macos/Runner/Configs/AppInfo.xcconfig ./flutter/macos/Runner/Configs/AppInfo.xcconfig.bak - + # MACSTUFF Update Info.plist sed -i '' -e 's|CFBundleName.*.*|CFBundleName\n\t${{ env.appname }}|' ./flutter/macos/Runner/Info.plist sed -i '' -e 's|CFBundleDisplayName.*.*|CFBundleDisplayName\n\t${{ env.appname }}|' ./flutter/macos/Runner/Info.plist sed -i '' -e 's|CFBundleIdentifier.*.*|CFBundleIdentifier\n\tcom.${{ env.appname }}.app|' ./flutter/macos/Runner/Info.plist # sed -i '' '/NSHumanReadableCopyright<\/key>/{n;s/.*<\/string>/Copyright 2025 ${{ env.appname }}. All rights reserved.<\/string>/;}' ./flutter/macos/Runner/Info.plist - + # MACSTUFF Update AppInfo.xcconfig sed -i '' -e 's|PRODUCT_NAME = .*|PRODUCT_NAME = ${{ env.appname }}|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig sed -i '' -e 's|PRODUCT_BUNDLE_IDENTIFIER = .*|PRODUCT_BUNDLE_IDENTIFIER = com.${{ env.appname }}.app|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig @@ -150,7 +150,7 @@ jobs: sed -i -e 's|Purslane Ltd.|${{ env.appname }}|' ./Cargo.toml sed -i -e 's|Purslane Ltd|${{ env.appname }}|' ./libs/portable/Cargo.toml - + # Update Xcode project settings sed -i '' -e 's/PRODUCT_NAME = "RustDesk"/PRODUCT_NAME = "${{ env.appname }}"/' ./flutter/macos/Runner.xcodeproj/project.pbxproj sed -i '' -e 's/PRODUCT_BUNDLE_IDENTIFIER = ".*"/PRODUCT_BUNDLE_IDENTIFIER = "com.${{ env.appname }}.app"/' ./flutter/macos/Runner.xcodeproj/project.pbxproj @@ -169,7 +169,7 @@ jobs: cp ./src/lang/nl.rs ./src/lang/nl.rs.bak find ./src/lang -name "*.rs" -exec sed -i '' -e 's|RustDesk|${{ env.appname }}|' {} \; sed -i '' -e 's|RustDesk|${{ env.appname }}|' ./src/lang/nl.rs - + sed -i '' -e 's|https://rustdesk.com|${{ 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 @@ -189,11 +189,11 @@ jobs: # Update slogan #sed -i '' '/NSHumanReadableCopyright<\/key>/{n;s/.*<\/string>/Copyright 2025 ${{ env.appname }}. All rights reserved.<\/string>/;}' ./flutter/macos/Runner/Info.plist - - # Update slogan - About in en.rs + + # Update slogan - About in en.rs sed -i '' -e 's/("Slogan_tip", "Made with heart in this chaotic world!")/("Slogan_tip", "Powered by ${{ env.appname }}")/' ./src/lang/en.rs sed -i '' -e 's/("About RustDesk", "")/("About RustDesk", "About ${{ env.appname }}")/' ./src/lang/en.rs - + # Update slogan - About in nl.rs sed -i '' -e 's/("Slogan_tip", "Ontwikkeld met het hart voor deze chaotische wereld!")/("Slogan_tip", "Powered by ${{ env.appname }}")/' ./src/lang/nl.rs @@ -204,19 +204,19 @@ jobs: sed -i -e 's|rs-ny.rustdesk.com|${{ env.server }}|' ./libs/hbb_common/src/config.rs sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.key }}|' ./libs/hbb_common/src/config.rs sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs - + wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.py python allowCustom.py wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff git apply removeSetupServerTip.diff - + # Update pubspec.yaml with proper YAML formatting cp ./flutter/pubspec.yaml ./flutter/pubspec.yaml.bak echo " archive: ^3.6.1" > ./flutter/temp_dependency.txt awk '/intl:/{print;system("cat ./flutter/temp_dependency.txt");next}1' ./flutter/pubspec.yaml > ./flutter/pubspec.yaml.tmp mv ./flutter/pubspec.yaml.tmp ./flutter/pubspec.yaml - rm ./flutter/temp_dependency.txt - + rm ./flutter/temp_dependency.txt + - name: Install build runtime run: | brew install llvm create-dmg @@ -249,7 +249,7 @@ jobs: run: | cd $(dirname $(dirname $(which flutter))) [[ "3.24.5" == ${{env.FLUTTER_VERSION}} ]] && git apply ${{ github.workspace }}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff - + - name: Workaround for flutter issue shell: bash run: | @@ -257,14 +257,14 @@ jobs: # https://github.com/flutter/flutter/issues/133533 sed -i -e 's/_setFramesEnabledState(false);/\/\/_setFramesEnabledState(false);/g' ../packages/flutter/lib/src/scheduler/binding.dart grep -n '_setFramesEnabledState(false);' ../packages/flutter/lib/src/scheduler/binding.dart - + - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 with: toolchain: ${{ env.MAC_RUST_VERSION }} targets: ${{ matrix.job.target }} components: "rustfmt" - + - uses: Swatinem/rust-cache@v2 with: prefix-key: ${{ matrix.job.os }} @@ -286,7 +286,7 @@ jobs: -H "User-Agent: Mozilla/5.0" \ "${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}&uuid=${{ env.iconlink_uuid }}" \ -o ./res/icon.png || wget --no-check-certificate -O ./res/icon.png "${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}&uuid=${{ env.iconlink_uuid }}" - + # Backup existing files (if they exist) [ -f "./res/32x32.png" ] && mv ./res/32x32.png ./res/32x32.png.bak [ -f "./res/64x64.png" ] && mv ./res/64x64.png ./res/64x64.png.bak @@ -295,22 +295,22 @@ jobs: [ -f "./flutter/assets/icon.png" ] && mv ./flutter/assets/icon.png ./flutter/assets/icon.png.bak [ -f "./flutter/assets/icon.svg" ] && mv ./flutter/assets/icon.svg ./flutter/assets/icon.svg.bak [ -f "./rustdesk/data/flutter_assets/assets/icon.svg" ] && mv ./rustdesk/data/flutter_assets/assets/icon.svg ./rustdesk/data/flutter_assets/assets/icon.svg.bak - + # Create standard app icons 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 - + # Copy icon to Flutter assets cp ./res/icon.png ./flutter/assets/icon.png cp ./res/icon.png ./rustdesk/data/flutter_assets/assets/icon.png - + # Convert PNG to SVG using potrace magick ./res/icon.png -flatten ./temp_icon.pbm potrace --svg -o ./flutter/assets/icon.svg ./temp_icon.pbm cp ./flutter/assets/icon.svg ./rustdesk/data/flutter_assets/assets/icon.svg rm ./temp_icon.pbm - + # Create macOS app icons magick ./res/icon.png -resize 16x16 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png" magick ./res/icon.png -resize 32x32 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png" @@ -322,10 +322,10 @@ jobs: # Create macOS specific icons magick ./res/icon.png -resize 128x128 ./res/mac-icon.png - + # Create dark mode tray icon (optimized for macOS menu bar) magick ./res/icon.png -resize 22x22 -colorspace gray -alpha set -background none -channel A -evaluate set 100% ./res/mac-tray-dark-x2.png - + # Create light mode tray icon (optimized for macOS menu bar) magick ./res/icon.png -resize 22x22 -negate -colorspace gray -alpha set -background none -channel A -evaluate set 100% ./res/mac-tray-light-x2.png @@ -343,7 +343,7 @@ jobs: cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png" "./iconset.iconset/icon_512x512@2x.png" iconutil -c icns ./iconset.iconset -o ./flutter/macos/Runner/AppIcon.icns rm -rf ./iconset.iconset - + # Create Contents.json for macOS app icon echo '{ "images": [ @@ -363,10 +363,10 @@ jobs: "author": "xcode" } }' > "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json" - + # Copy icons and Contents.json to both locations cp -r flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/* macos/Runner/Assets.xcassets/AppIcon.appiconset/ - + # Verify files exist and show their sizes echo "Verifying generated files:" ls -lh ./res/mac-tray-dark-x2.png @@ -390,19 +390,19 @@ jobs: mkdir -p assets echo '{"name":"${{ env.appname }}","short_name":"${{ env.appname }}","start_url":"/","display":"standalone","background_color":"#ffffff","theme_color":"#ffffff","description":"A remote desktop software."}' > web/manifest.json echo '${{ env.appname }}' > web/index.html - + # Ensure the AppIcon.appiconset directory exists mkdir -p macos/Runner/Assets.xcassets/AppIcon.appiconset - + # Copy the processed icons to Flutter locations cp ../res/mac-icon.png ./assets/icon.png cp ../flutter/assets/icon.svg ./assets/icon.svg || true - + flutter pub upgrade win32 flutter pub get flutter pub run flutter_launcher_icons cd .. - + - name: ui.rs if: ${{ env.iconlink_url != 'false' }} continue-on-error: true @@ -428,7 +428,7 @@ jobs: run: | wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/cycle_monitor.diff git apply cycle_monitor.diff - + - name: use X for offline display instead of orange circle continue-on-error: true if: env.xOffline == 'true' @@ -446,12 +446,12 @@ jobs: - name: removeNewVersionNotif continue-on-error: true if: env.removeNewVersionNotif == 'true' - run: | + 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: Restore bridge files - uses: actions/download-artifact@master + uses: actions/download-artifact@v8 with: name: bridge-artifact path: ./ @@ -497,7 +497,7 @@ jobs: # - name: Copy service file # run: | # cp -rf ../target/release/service ./build/macos/Build/Products/Release/RustDesk.app/Contents/MacOS/ - + - name: Install rcodesign tool if: env.MACOS_P12_BASE64 != null shell: bash @@ -517,7 +517,7 @@ jobs: else brew install pkg-config fi - + - name: Show version information (Rust, cargo, Clang) shell: bash run: | @@ -527,7 +527,7 @@ jobs: rustup default cargo -V rustc -V - + - name: icon svg handling if: ${{ env.iconlink_url != 'false' }} continue-on-error: false @@ -566,21 +566,21 @@ jobs: # Debug info echo "Current directory contents:" ls -la - + # Rename RustDesk.app to the custom app name first if [ -d "RustDesk.app" ]; then # First rename the app if it's still called RustDesk.app mv "RustDesk.app" "${{ env.appname }}.app" echo "Renamed RustDesk.app to ${{ env.appname }}.app" fi - + echo "App bundle contents after rename:" ls -la "${{ env.appname }}.app" || echo "App not found" ls -la "${{ env.appname }}.app/Contents" || echo "Contents not found" - + # Decode the certificate echo "${{ secrets.MACOS_P12_BASE64 }}" | base64 --decode > certificate.p12 - + # Sign the app bundle and its contents if [ -d "${{ env.appname }}.app/Contents/MacOS" ]; then echo "Signing main executable..." @@ -600,12 +600,12 @@ jobs: --code-signature-flags runtime "${{ env.appname }}.app/Contents/MacOS/$ACTUAL_EXECUTABLE" fi fi - + echo "Signing frameworks..." find "${{ env.appname }}.app/Contents/Frameworks" -type f -not -name ".*" -exec \ rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \ --code-signature-flags runtime {} \; - + echo "Signing main bundle..." rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \ --code-signature-flags runtime "${{ env.appname }}.app" @@ -613,9 +613,9 @@ jobs: echo "Error: Invalid app bundle structure" exit 1 fi - + # Clean up - rm certificate.p12 + rm certificate.p12 - name: Create DMG run: | @@ -644,17 +644,17 @@ jobs: "${{ env.appname }}-${{ matrix.job.arch }}.dmg" \ "${{ env.appname }}.app" mv "${{ env.appname }}-${{ matrix.job.arch }}.dmg" $GITHUB_WORKSPACE/ - + - name: Rename rustdesk if: env.UPLOAD_ARTIFACT == 'true' run: | cd $GITHUB_WORKSPACE echo "Directory contents:" ls -la - + # Find the DMG file dynamically DMG_FILE=$(find . -name "${{ env.appname }}-${{ matrix.job.arch }}.dmg") - + if [ -n "$DMG_FILE" ]; then echo "Found DMG file: $DMG_FILE" mv "$DMG_FILE" "${{ env.filename }}-${{ matrix.job.arch }}.dmg" @@ -674,7 +674,7 @@ jobs: -F "file=@$GITHUB_WORKSPACE/${{ env.filename }}-${{ matrix.job.arch }}.dmg" \ -F "uuid=${{ env.uuid }}" \ "${{ secrets.GENURL }}/save_custom_client" - + - name: send file to api server if: ${{ env.rdgen == 'false' }} @@ -693,6 +693,6 @@ jobs: if: always() steps: - name: Delete secrets artifact - uses: geekyeggo/delete-artifact@v5 + uses: geekyeggo/delete-artifact@v6 with: name: encrypted-secrets-zip diff --git a/.github/workflows/generator-windows-x86.yml b/.github/workflows/generator-windows-x86.yml index 57eed606b..44887848c 100644 --- a/.github/workflows/generator-windows-x86.yml +++ b/.github/workflows/generator-windows-x86.yml @@ -1,6 +1,6 @@ name: Custom Windows x86 Client Generator run-name: Custom Windows x86 Client Generator -on: +on: workflow_dispatch: inputs: version: @@ -31,7 +31,7 @@ env: # vcpkg version: 2025.08.27 # If we change the `VCPKG COMMIT_ID`, please remember: # 1. Call `$VCPKG_ROOT/vcpkg x-update-baseline` to update the baseline in `vcpkg.json`. - # Or we may face build issue like + # Or we may face build issue like # https://github.com/rustdesk/rustdesk/actions/runs/14414119794/job/40427970174 # 2. Update the `VCPKG_COMMIT_ID` in `ci.yml` and `playground.yml`. VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" @@ -72,9 +72,9 @@ jobs: # - { target: aarch64-pc-windows-msvc, os: windows-2022 } steps: - name: Checkout Repository - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 + uses: actions/checkout@v6 + + - uses: actions/download-artifact@v8 with: name: encrypted-secrets-zip @@ -86,7 +86,7 @@ jobs: - name: Finalize and Cleanup zip/json if: always() # Run even if previous steps fail continue-on-error: true - uses: fjogeleit/http-request-action@v1 + uses: fjogeleit/http-request-action@v2 with: url: "${{ secrets.GENURL }}/cleanzip" method: 'POST' @@ -94,7 +94,7 @@ jobs: data: '{"uuid": "${{ env.uuid }}"}' - name: Export GitHub Actions cache environment variables - uses: actions/github-script@v6 + uses: actions/github-script@v8 with: script: | core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); @@ -112,7 +112,7 @@ jobs: - name: Checkout source code if: ${{ env.VERSION != 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk ref: refs/tags/${{ env.VERSION }} @@ -120,7 +120,7 @@ jobs: - name: Checkout source code if: ${{ env.VERSION == 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk submodules: recursive @@ -133,22 +133,82 @@ jobs: - name: change appname to custom if: env.appname != 'rustdesk' continue-on-error: true - shell: bash + shell: python + env: + PYTHONUTF8: "1" + PYTHONIOENCODING: "utf-8" + APP_NAME: ${{ env.appname }} + FILE_NAME: ${{ env.filename }} + COMP_NAME: ${{ env.compname }} 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 - # ./src/lang/en.rs - find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \; + import sys + import io + import os + import glob + + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + + app_name = os.environ['APP_NAME'] + file_name = os.environ.get('FILE_NAME', '') + comp_name = os.environ.get('COMP_NAME', '') + + def replace_in_file(path, old, new, encoding='utf-8'): + try: + with open(path, 'r', encoding=encoding, errors='replace') as f: + content = f.read() + if old in content: + content = content.replace(old, new) + with open(path, 'w', encoding=encoding) as f: + f.write(content) + print(f"✅ Updated: {path} ('{old}' -> '{new}')") + else: + print(f"⚠️ Not found in: {path} ('{old}')") + except Exception as e: + print(f"❌ Error in {path}: {e}") + + # Cargo.toml + replace_in_file('./Cargo.toml', 'description = "RustDesk Remote Desktop"', f'description = "{app_name}"') + replace_in_file('./Cargo.toml', 'ProductName = "RustDesk"', f'ProductName = "{app_name}"') + replace_in_file('./Cargo.toml', 'FileDescription = "RustDesk Remote Desktop"', f'FileDescription = "{app_name}"') + replace_in_file('./Cargo.toml', 'OriginalFilename = "rustdesk.exe"', f'OriginalFilename = "{app_name}.exe"') + + # libs/portable/Cargo.toml + replace_in_file('./libs/portable/Cargo.toml', 'description = "RustDesk Remote Desktop"', f'description = "{app_name}"') + replace_in_file('./libs/portable/Cargo.toml', 'ProductName = "RustDesk"', f'ProductName = "{app_name}"') + replace_in_file('./libs/portable/Cargo.toml', 'FileDescription = "RustDesk Remote Desktop"', f'FileDescription = "{app_name}"') + replace_in_file('./libs/portable/Cargo.toml', 'OriginalFilename = "rustdesk.exe"', f'OriginalFilename = "{app_name}.exe"') + + # libs/portable/src/main.rs + replace_in_file('./libs/portable/src/main.rs', 'const APP_PREFIX: &str = "rustdesk";', f'const APP_PREFIX: &str = "{app_name}";') + + # src/lang/*.rs + lang_files = glob.glob('./src/lang/*.rs') + for lang_file in lang_files: + try: + with open(lang_file, 'r', encoding='utf-8', errors='replace') as f: + content = f.read() + modified = False + if comp_name: + lines = content.split('\n') + new_lines = [] + for line in lines: + if 'powered_by_me' in line: + line = line.replace('RustDesk', comp_name) + modified = True + new_lines.append(line) + content = '\n'.join(new_lines) + if 'RustDesk' in content: + content = content.replace('RustDesk', app_name) + modified = True + if modified: + with open(lang_file, 'w', encoding='utf-8') as f: + f.write(content) + print(f"✅ Updated: {lang_file}") + except Exception as e: + print(f"❌ Error in {lang_file}: {e}") + + print("Done!") - name: fix registry if appname has a space if: contains(env.appname, ' ') @@ -169,19 +229,50 @@ jobs: 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 + shell: python + env: + PYTHONUTF8: "1" + PYTHONIOENCODING: "utf-8" + COMP_NAME: ${{ env.compname }} run: | - 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|Copyright © 2025 Purslane Ltd.|Copyright \© 2025 ${{ env.compname }}|' ./src/ui/index.tis - 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/setup.nsi - + import sys + import io + import os + + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + + comp_name = os.environ['COMP_NAME'] + old_name = 'Purslane Ltd' + + def replace_in_file(path, old, new, encoding='utf-8'): + try: + with open(path, 'r', encoding=encoding, errors='replace') as f: + content = f.read() + if old in content: + content = content.replace(old, new) + with open(path, 'w', encoding=encoding) as f: + f.write(content) + print(f"✅ Updated: {path}") + else: + print(f"⚠️ Not found in: {path}") + except Exception as e: + print(f"❌ Error in {path}: {e}") + + replace_in_file('./res/msi/preprocess.py', 'PURSLANE', comp_name) + replace_in_file('./res/msi/preprocess.py', old_name, comp_name) + # Sciter UI — специфично для x86 + replace_in_file('./src/ui/index.tis', f'Copyright © 2025 {old_name}.', f'Copyright © 2025 {comp_name}') + replace_in_file('./Cargo.toml', old_name, comp_name) + replace_in_file('./libs/portable/Cargo.toml', old_name, comp_name) + replace_in_file('./res/setup.nsi', old_name, comp_name) + + print("Done!") + - name: change url to custom if: env.urlLink != 'https://rustdesk.com' continue-on-error: true @@ -199,17 +290,53 @@ jobs: run: | sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis - - name: set server, key, and apiserver + - 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: | - sed -i -e 's|rs-ny.rustdesk.com|${{ env.server }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.key }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs + 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 + + if ! [[ "$SERVER_PORT_INPUT" =~ ^[0-9]+$ ]]; then + echo "Error: serverPort must be a number, got: '$SERVER_PORT_INPUT'" + exit 1 + fi + + 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 + sed -i -e "s|pub const RENDEZVOUS_PORT: i32 = [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]\+;|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]\+;|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]\+;|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 + # Sciter UI sed -i -e 's|{translate("Ready")}, {translate("setup_server_tip")}|translate("Ready")|' ./src/ui/index.tis + echo "=== Verification ===" + grep -n "RENDEZVOUS_PORT\|RELAY_PORT\|WS_" ./libs/hbb_common/src/config.rs + grep -n "rustdesk\|admin\." ./src/common.rs || true + - name: allow custom.txt - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -273,13 +400,13 @@ jobs: - name: magick stuff if: ${{ env.iconlink_url != 'false' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 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/icon.png + Invoke-WebRequest -Uri ${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}"&"uuid=${{ env.iconlink_uuid }} -OutFile ./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 @@ -307,13 +434,14 @@ jobs: if: ${{ env.delayFix == 'true' }} shell: bash run: | - sed -i -e 's|!key.is_empty()|false|' ./src/client.rs + 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: removeNewVersionNotif continue-on-error: true if: env.removeNewVersionNotif == 'true' shell: bash - run: | + run: | sed -i -e 's|{software_update_url ? : ""}||' ./src/ui/index.tis sed -i '/let (request, url) =/,/Ok(())/{/Ok(())/!d}' ./src/common.rs @@ -352,7 +480,7 @@ jobs: echo "list ./libs/portable/Runner.res"; ls -l ./libs/portable/Runner.res; fi - + - name: zip dlls continue-on-error: true shell: pwsh @@ -432,15 +560,34 @@ jobs: - name: send file to rdgen server if: ${{ env.rdgen == 'true' }} - shell: bash - run: | - 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 + uses: nick-fields/retry@v4 + with: + timeout_minutes: 10 + max_attempts: 3 + shell: bash + command: | + curl -i -X POST \ + --max-time 540 \ + -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 - name: send file to api server if: ${{ env.rdgen == 'false' }} - shell: bash - run: | - 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 + uses: nick-fields/retry@v4 + with: + timeout_minutes: 10 + max_attempts: 3 + shell: bash + command: | + curl -i -X POST \ + --max-time 540 \ + -H "Content-Type: multipart/form-data" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@./SignOutput/${{ env.filename }}.exe" \ + ${{ env.apiServer }}/api/save_custom_client cleanup: needs: [build-for-windows-sciter] @@ -449,6 +596,6 @@ jobs: if: always() steps: - name: Delete secrets artifact - uses: geekyeggo/delete-artifact@v5 + uses: geekyeggo/delete-artifact@v6 with: name: encrypted-secrets-zip diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index 52de8fd41..133ca9482 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -1,6 +1,6 @@ name: Custom Windows Client Generator run-name: Custom Windows Client Generator -on: +on: workflow_dispatch: inputs: version: @@ -23,7 +23,7 @@ env: SCITER_NASM_DEBVERSION: "2.15.05-1" LLVM_VERSION: "15.0.6" FLUTTER_VERSION: "3.24.5" - ANDROID_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 }}" @@ -84,9 +84,9 @@ jobs: # - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 } steps: - name: Checkout Repository - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 + uses: actions/checkout@v6 + + - uses: actions/download-artifact@v8 with: name: encrypted-secrets-zip @@ -98,7 +98,7 @@ jobs: - name: Finalize and Cleanup zip/json if: always() # Run even if previous steps fail continue-on-error: true - uses: fjogeleit/http-request-action@v1 + uses: fjogeleit/http-request-action@v2 with: url: "${{ secrets.GENURL }}/cleanzip" method: 'POST' @@ -106,12 +106,12 @@ jobs: data: '{"uuid": "${{ env.uuid }}"}' - name: Export GitHub Actions cache environment variables - uses: actions/github-script@v6 + uses: actions/github-script@v8 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: | @@ -124,7 +124,7 @@ jobs: - name: Checkout source code if: ${{ env.VERSION != 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk ref: refs/tags/${{ env.VERSION }} @@ -132,13 +132,13 @@ jobs: - name: Checkout source code if: ${{ env.VERSION == 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk submodules: recursive - name: Restore bridge files - uses: actions/download-artifact@master + uses: actions/download-artifact@v8 with: name: bridge-artifact path: ./ @@ -153,36 +153,106 @@ jobs: run: | sed -i -e 's|2ded7f146437a761ffe6981e2f742038f85ca68d|08a471bb8ceccdd50483c81cdfa8b81b07b14b87|' ./flutter/pubspec.lock sed -i -e 's|2ded7f146437a761ffe6981e2f742038f85ca68d|08a471bb8ceccdd50483c81cdfa8b81b07b14b87|' ./flutter/pubspec.yaml - + - name: change appname to custom if: env.appname != 'rustdesk' continue-on-error: true - shell: bash + shell: python + env: + PYTHONUTF8: "1" + PYTHONIOENCODING: "utf-8" + APP_NAME: ${{ env.appname }} + FILE_NAME: ${{ env.filename }} + COMP_NAME: ${{ env.compname }} 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 + import sys + import io + import os + import glob + + # UTF-8 обёртка + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + + app_name = os.environ['APP_NAME'] + file_name = os.environ.get('FILE_NAME', '') + comp_name = os.environ.get('COMP_NAME', '') + + print(f"App name: '{app_name}'") + print(f"File name: '{file_name}'") + print(f"Comp name: '{comp_name}'") + + def replace_in_file(path, old, new, encoding='utf-8'): + try: + with open(path, 'r', encoding=encoding, errors='replace') as f: + content = f.read() + if old in content: + content = content.replace(old, new) + with open(path, 'w', encoding=encoding) as f: + f.write(content) + print(f"✅ Updated: {path} ('{old}' -> '{new}')") + else: + print(f"⚠️ Not found in: {path} ('{old}')") + except Exception as e: + print(f"❌ Error in {path}: {e}") + + # 1. Cargo.toml + replace_in_file('./Cargo.toml', 'description = "RustDesk Remote Desktop"', f'description = "{app_name}"') + replace_in_file('./Cargo.toml', 'ProductName = "RustDesk"', f'ProductName = "{app_name}"') + replace_in_file('./Cargo.toml', 'FileDescription = "RustDesk Remote Desktop"', f'FileDescription = "{app_name}"') + replace_in_file('./Cargo.toml', 'OriginalFilename = "rustdesk.exe"', f'OriginalFilename = "{app_name}.exe"') + + # 2. libs/portable/Cargo.toml + replace_in_file('./libs/portable/Cargo.toml', 'description = "RustDesk Remote Desktop"', f'description = "{app_name}"') + replace_in_file('./libs/portable/Cargo.toml', 'ProductName = "RustDesk"', f'ProductName = "{app_name}"') + replace_in_file('./libs/portable/Cargo.toml', 'FileDescription = "RustDesk Remote Desktop"', f'FileDescription = "{app_name}"') + replace_in_file('./libs/portable/Cargo.toml', 'OriginalFilename = "rustdesk.exe"', f'OriginalFilename = "{app_name}.exe"') + + # 3. libs/portable/src/main.rs + replace_in_file('./libs/portable/src/main.rs', 'const APP_PREFIX: &str = "rustdesk";', f'const APP_PREFIX: &str = "{app_name}";') + + # 4. flutter/windows/runner/Runner.rc + replace_in_file('./flutter/windows/runner/Runner.rc', '"RustDesk Remote Desktop"', f'"{app_name}"') + replace_in_file('./flutter/windows/runner/Runner.rc', 'VALUE "InternalName", "rustdesk" "\0"', f'VALUE "InternalName", "{app_name}" "\0"') + if file_name: + replace_in_file('./flutter/windows/runner/Runner.rc', '"rustdesk.exe"', f'"{file_name}"') + replace_in_file('./flutter/windows/runner/Runner.rc', '"RustDesk"', f'"{app_name}"') + + # 5. src/lang/*.rs + # сначала powered_by_me — меняем RustDesk на compname если задан + lang_files = glob.glob('./src/lang/*.rs') + for lang_file in lang_files: + try: + with open(lang_file, 'r', encoding='utf-8', errors='replace') as f: + content = f.read() + modified = False + # powered_by_me строки — меняем на compname если задан + if comp_name: + lines = content.split('\n') + new_lines = [] + for line in lines: + if 'powered_by_me' in line: + line = line.replace('RustDesk', comp_name) + modified = True + new_lines.append(line) + content = '\n'.join(new_lines) + # остальные строки — меняем на appname + if 'RustDesk' in content: + content = content.replace('RustDesk', app_name) + modified = True + if modified: + with open(lang_file, 'w', encoding='utf-8') as f: + f.write(content) + print(f"✅ Updated: {lang_file}") + else: + print(f"⚠️ Not found in: {lang_file}") + except Exception as e: + print(f"❌ Error in {lang_file}: {e}") + + # 6. res/msi/Package/License.rtf + replace_in_file('./res/msi/Package/License.rtf', 'RustDesk', app_name) + + print("Done!") - name: fix registry if appname has a space if: contains(env.appname, ' ') @@ -203,21 +273,69 @@ jobs: 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 + shell: python + env: + PYTHONUTF8: "1" + PYTHONIOENCODING: "utf-8" + COMP_NAME: ${{ env.compname }} run: | - 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|"Copyright © 2025 Purslane Ltd. All rights reserved."|"Copyright © 2025 ${{ env.compname }}. All rights reserved."|' ./flutter/windows/runner/Runner.rc - 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 - + import sys + import io + import os + + # UTF-8 обёртка + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + + comp_name = os.environ['COMP_NAME'] + old_name = 'Purslane Ltd' + + print(f"Replacing '{old_name}' with '{comp_name}'") + + def replace_in_file(path, old, new, encoding='utf-8'): + try: + with open(path, 'r', encoding=encoding, errors='replace') as f: + content = f.read() + if old in content: + content = content.replace(old, new) + with open(path, 'w', encoding=encoding) as f: + f.write(content) + print(f"✅ Updated: {path}") + else: + print(f"⚠️ Not found in: {path}") + except Exception as e: + print(f"❌ Error in {path}: {e}") + + # 1. desktop_setting_page.dart + replace_in_file('./flutter/lib/desktop/pages/desktop_setting_page.dart', old_name, comp_name) + + # 2. preprocess.py — два замены + replace_in_file('./res/msi/preprocess.py', 'PURSLANE', comp_name) + replace_in_file('./res/msi/preprocess.py', old_name, comp_name) + + # 3. Runner.rc — сначала Copyright строку, потом просто название + replace_in_file( + './flutter/windows/runner/Runner.rc', + f'"Copyright © 2025 {old_name}. All rights reserved."', + f'"Copyright © 2025 {comp_name}. All rights reserved."' + ) + replace_in_file('./flutter/windows/runner/Runner.rc', old_name, comp_name) + + # 4. Cargo.toml + replace_in_file('./Cargo.toml', old_name, comp_name) + + # 5. libs/portable/Cargo.toml + replace_in_file('./libs/portable/Cargo.toml', old_name, comp_name) + + # 6. License.rtf — правильный путь с Package + replace_in_file('./res/msi/Package/License.rtf', old_name, comp_name) + + print("Done!") + - name: change url to custom if: env.urlLink != 'https://rustdesk.com' continue-on-error: true @@ -241,18 +359,65 @@ jobs: 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 - - name: set server, key, and apiserver + - 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: | - sed -i -e 's|rs-ny.rustdesk.com|${{ env.server }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.key }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs + # Проверка наличия файлов + 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 + + # Проверка что порт — число + if ! [[ "$SERVER_PORT_INPUT" =~ ^[0-9]+$ ]]; then + echo "Error: serverPort must be a number, got: '$SERVER_PORT_INPUT'" + exit 1 + fi + + # Вычисляем порты + 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 + + # Замена портов + sed -i -e "s|pub const RENDEZVOUS_PORT: i32 = [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]\+;|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]\+;|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]\+;|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 + + # Замена API сервера + sed -i -e "s|https://admin.rustdesk.com|$API_SERVER|" ./src/common.rs + # ./flutter/pubspec.yaml #sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml + # Проверка результата + echo "=== Verification ===" + grep -n "RENDEZVOUS_PORT\|RELAY_PORT\|WS_" ./libs/hbb_common/src/config.rs + grep -n "rustdesk\|admin\." ./src/common.rs || true + - name: allow custom.txt - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -265,7 +430,7 @@ jobs: git apply removeSetupServerTip.diff - name: Install LLVM and Clang - uses: KyleMayes/install-llvm-action@v1 + uses: KyleMayes/install-llvm-action@v2 with: version: ${{ env.LLVM_VERSION }} @@ -328,16 +493,16 @@ jobs: fi head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true shell: bash - + - name: magick stuff if: ${{ env.iconlink_url != 'false' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 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 + 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 @@ -369,11 +534,12 @@ jobs: if: ${{ env.delayFix == 'true' }} shell: bash run: | - sed -i -e 's|!key.is_empty()|false|' ./src/client.rs + 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: add cycle monitors to toolbar if: env.cycleMonitor == 'true' - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -385,7 +551,7 @@ jobs: - name: use X for offline display instead of orange circle if: env.xOffline == 'true' - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -399,7 +565,7 @@ jobs: continue-on-error: true if: env.removeNewVersionNotif == 'true' shell: bash - run: | + 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 @@ -428,13 +594,13 @@ jobs: # 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 + mv -Force .\usbmmidd_v2 ./rustdesk # Download printer driver files and extract them to ./rustdesk try { @@ -472,7 +638,7 @@ jobs: - name: logo stuff if: ${{ env.logolink_url != 'false' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -498,12 +664,12 @@ jobs: fi - name: Download RustDeskTempTopMostWindow artifacts - uses: actions/download-artifact@master + uses: actions/download-artifact@v8 if: env.UPLOAD_ARTIFACT == 'true' with: name: topmostwindow-artifacts path: "./rustdesk" - + - name: zip dlls continue-on-error: true shell: pwsh @@ -551,7 +717,7 @@ jobs: mv ./target/release/rustdesk-portable-packer.exe "./SignOutput/rustdesk.exe" - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v2 + uses: microsoft/setup-msbuild@v3 - name: Build msi continue-on-error: true @@ -606,25 +772,47 @@ jobs: - name: send file to rdgen server if: ${{ env.rdgen == 'true' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: - timeout_minutes: 1 + timeout_minutes: 10 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 + curl -i -X POST \ + --max-time 540 \ + -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 \ + --max-time 540 \ + -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 + uses: nick-fields/retry@v4 with: - timeout_minutes: 1 + timeout_minutes: 10 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 + curl -i -X POST \ + --max-time 540 \ + -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 \ + --max-time 540 \ + -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] @@ -633,6 +821,6 @@ jobs: if: always() steps: - name: Delete secrets artifact - uses: geekyeggo/delete-artifact@v5 + uses: geekyeggo/delete-artifact@v6 with: name: encrypted-secrets-zip diff --git a/.github/workflows/sh-generator-windows.yml b/.github/workflows/sh-generator-windows.yml index 0672d0c31..52991cce8 100644 --- a/.github/workflows/sh-generator-windows.yml +++ b/.github/workflows/sh-generator-windows.yml @@ -84,9 +84,9 @@ jobs: # - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 } steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: encrypted-secrets-zip @@ -98,7 +98,7 @@ jobs: - name: Finalize and Cleanup zip/json if: always() # Run even if previous steps fail continue-on-error: true - uses: fjogeleit/http-request-action@v1 + uses: fjogeleit/http-request-action@v2 with: url: "${{ secrets.GENURL }}/cleanzip" method: 'POST' @@ -106,7 +106,7 @@ jobs: data: '{"uuid": "${{ env.uuid }}"}' - name: Export GitHub Actions cache environment variables - uses: actions/github-script@v6 + uses: actions/github-script@v8 with: script: | core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); @@ -124,7 +124,7 @@ jobs: - name: Checkout source code if: ${{ env.VERSION != 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk ref: refs/tags/${{ env.VERSION }} @@ -132,24 +132,24 @@ jobs: - name: Checkout source code if: ${{ env.VERSION == 'master' }} - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: rustdesk/rustdesk submodules: recursive - name: Restore bridge files - uses: actions/download-artifact@master + uses: actions/download-artifact@v8 with: name: bridge-artifact path: ./ - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.12' - name: Setup NuGet - uses: NuGet/setup-nuget@v2 + uses: NuGet/setup-nuget@v3 with: nuget-version: 'latest' @@ -251,7 +251,7 @@ jobs: #sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml - name: allow custom.txt - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -265,7 +265,7 @@ jobs: - name: magick stuff if: ${{ env.iconlink_url != 'false' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -307,7 +307,7 @@ jobs: - name: add cycle monitors to toolbar if: env.cycleMonitor == 'true' - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -319,7 +319,7 @@ jobs: - name: use X for offline display instead of orange circle if: env.xOffline == 'true' - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -396,7 +396,7 @@ jobs: - name: logo stuff if: ${{ env.logolink_url != 'false' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -422,7 +422,7 @@ jobs: fi - name: Download RustDeskTempTopMostWindow artifacts - uses: actions/download-artifact@master + uses: actions/download-artifact@v8 if: env.UPLOAD_ARTIFACT == 'true' with: name: topmostwindow-artifacts @@ -475,7 +475,7 @@ jobs: mv ./target/release/rustdesk-portable-packer.exe "./SignOutput/rustdesk.exe" - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v2 + uses: microsoft/setup-msbuild@v3 - name: Build msi continue-on-error: true @@ -530,7 +530,7 @@ jobs: - name: send file to rdgen server if: ${{ env.rdgen == 'true' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -541,7 +541,7 @@ jobs: - name: send file to api server if: ${{ env.rdgen == 'false' }} - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -557,6 +557,6 @@ jobs: if: always() steps: - name: Delete secrets artifact - uses: geekyeggo/delete-artifact@v5 + uses: geekyeggo/delete-artifact@v6 with: name: encrypted-secrets-zip diff --git a/.github/workflows/third-party-RustDeskTempTopMostWindow.yml b/.github/workflows/third-party-RustDeskTempTopMostWindow.yml index 73db6e966..c03de7b2a 100644 --- a/.github/workflows/third-party-RustDeskTempTopMostWindow.yml +++ b/.github/workflows/third-party-RustDeskTempTopMostWindow.yml @@ -39,12 +39,12 @@ jobs: build_output_dir: RustDeskTempTopMostWindow/WindowInjection/${{ inputs.platform }}/${{ inputs.configuration }} steps: - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v2 + uses: microsoft/setup-msbuild@v3 - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: encrypted-secrets-zip @@ -56,7 +56,7 @@ jobs: - name: Finalize and Cleanup zip/json if: always() # Run even if previous steps fail continue-on-error: true - uses: fjogeleit/http-request-action@v1 + uses: fjogeleit/http-request-action@v2 with: url: "${{ secrets.GENURL }}/cleanzip" method: 'POST' @@ -69,7 +69,7 @@ jobs: # Build. commit 53b548a5398624f7149a382000397993542ad796 is tag v0.3 - name: Build the project - uses: nick-fields/retry@v3 + uses: nick-fields/retry@v4 with: timeout_minutes: 1 max_attempts: 3 @@ -86,7 +86,7 @@ jobs: msbuild ${{ env.project_path }} -p:Configuration=${{ inputs.configuration }} -p:Platform=${{ inputs.platform }} /p:TargetVersion=${{ inputs.target_version }} - name: Archive build artifacts - uses: actions/upload-artifact@master + uses: actions/upload-artifact@v7 if: ${{ inputs.upload-artifact }} with: name: topmostwindow-artifacts diff --git a/.github/workflows/vcpkg.yml b/.github/workflows/vcpkg.yml index 001a918a0..56a12a885 100644 --- a/.github/workflows/vcpkg.yml +++ b/.github/workflows/vcpkg.yml @@ -29,7 +29,7 @@ jobs: echo "C:\Program Files\NASM" >> $GITHUB_PATH - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Clone RustDesk overlays run: | @@ -60,7 +60,7 @@ jobs: shell: bash - name: Upload Installed Dependencies - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: vcpkg-export-${{ matrix.job.vcpkg-triplet }} path: ${{ github.workspace }}/installed/ diff --git a/.gitignore b/.gitignore index 402d3e943..78c102921 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,8 @@ db.sqlite3 gunicorn.conf .venv +.env +/png +/temp_zips +/exe +workspace.code-workspace \ No newline at end of file diff --git a/rdgenerator/forms.py b/rdgenerator/forms.py index 32140199f..5ea486cd0 100644 --- a/rdgenerator/forms.py +++ b/rdgenerator/forms.py @@ -29,6 +29,7 @@ class GenerateForm(forms.Form): #Custom Server serverIP = forms.CharField(label="Host", required=False) + serverPort = forms.CharField(label="Port", required=False) apiServer = forms.CharField(label="API Server", required=False) key = forms.CharField(label="Key", required=False) urlLink = forms.CharField(label="Custom URL for links", required=False) @@ -104,7 +105,7 @@ def clean_iconfile(self): # Check for square dimensions if width != height: raise forms.ValidationError("Custom App Icon dimensions must be square.") - + return image except OSError: # Handle cases where the uploaded file is not a valid image raise forms.ValidationError("Invalid icon file.") diff --git a/rdgenerator/templates/generator.html b/rdgenerator/templates/generator.html index 55544d862..6cda45fdd 100644 --- a/rdgenerator/templates/generator.html +++ b/rdgenerator/templates/generator.html @@ -93,11 +93,11 @@ color: #2e52f7; } .text-64 { - font-size: 0.5em; + font-size: 0.5em; font-weight: bold; - bottom: -0.2em; + bottom: -0.2em; right: -0.2em; - position: absolute; + position: absolute; color: white; transform: translate(-50%, -50%); } @@ -228,8 +228,8 @@

RustDesk Custom Client Builder

-

Save/Load Configuration

-
+

Save/Load Configuration

+
@@ -274,7 +274,7 @@

Select Platform

-
+

General

@@ -295,6 +295,8 @@

General

Custom Server

{{ form.serverIP }}

+ + {{ form.serverPort }}

{{ form.key }}

@@ -315,17 +317,17 @@

Security

To use the hide connection window feature, please set a permanent password.
{{ form.permanentPassword }} *The password is used as default, but can be changed by the client

- - + +
- +
- +

- +

Visual

{{ form.iconbase64.as_hidden }} @@ -390,8 +392,8 @@

Other