From 3c312dad7b6778b9d97cdbee4a34b9405d53debe Mon Sep 17 00:00:00 2001 From: Bryan Date: Mon, 12 Jan 2026 17:31:05 -0600 Subject: [PATCH] Hide inputs from action logs --- .github/workflows/generator-android.yml | 240 +++++++------- .github/workflows/generator-linux.yml | 396 ++++++++++++++++-------- .github/workflows/generator-macos.yml | 307 +++++++++--------- .github/workflows/generator-windows.yml | 250 +++++++-------- docker-compose.yml | 3 + rdgen/settings.py | 2 + rdgen/urls.py | 1 + rdgenerator/views.py | 154 ++++++--- requirements.txt | 2 +- setup.md | 4 + 10 files changed, 802 insertions(+), 557 deletions(-) diff --git a/.github/workflows/generator-android.yml b/.github/workflows/generator-android.yml index 1b611a9..343870c 100644 --- a/.github/workflows/generator-android.yml +++ b/.github/workflows/generator-android.yml @@ -3,56 +3,16 @@ run-name: Custom Android Client Generator on: workflow_dispatch: inputs: - server: - description: 'Rendezvous Server' + version: + description: 'version to buld' required: true default: '' type: string - key: - description: 'Public Key' + zip_url: + description: 'url to zip of json' required: true default: '' type: string - apiServer: - description: 'API Server' - required: true - default: '' - type: string - custom: - description: "Custom JSON" - required: true - default: '' - type: string - uuid: - description: "uuid of request" - required: true - default: '' - type: string - iconlink: - description: "icon link" - required: false - default: 'false' - type: string - logolink: - description: "logo link" - required: false - default: 'false' - type: string - appname: - description: "app name" - required: true - default: 'rustdesk' - type: string - filename: - description: "Filename" - required: true - default: 'rustdesk' - type: string - extras: - description: "extra inputs in json" - required: true - default: '{}' - type: string env: @@ -71,7 +31,7 @@ env: # vcpkg version: 2024.07.12 VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" - VERSION: "${{ fromJson(inputs.extras).version }}" + VERSION: "${{ inputs.version }}" NDK_VERSION: "r27c" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" @@ -84,7 +44,7 @@ jobs: generate-bridge-linux: uses: ./.github/workflows/bridge.yml with: - version: ${{ fromJson(inputs.extras).version }} + version: ${{ inputs.version }} build-rustdesk-android: needs: [generate-bridge-linux] @@ -116,6 +76,46 @@ jobs: suffix: "", } steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}') + r.raise_for_status() + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + + - name: Finalize and Cleanup zip/json + if: always() # Run even if previous steps fail + uses: fjogeleit/http-request-action@v1 + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}"}' + - name: Free Disk Space (Ubuntu) uses: jlumbroso/free-disk-space@main with: @@ -135,14 +135,14 @@ jobs: core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} run: | echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} run: | - echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $GITHUB_ENV + echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $GITHUB_ENV - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -151,7 +151,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "5% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "5% complete"}' - name: Install dependencies run: | @@ -281,16 +281,16 @@ jobs: prefix-key: rustdesk-lib-cache-android # TODO: drop '-android' part after caches are invalidated key: ${{ matrix.job.target }} - ###########################################################echo "${{ inputs.iconbase64 }}" | base64 -d > ./res/icon.png + ###########################################################echo "${{ env.iconbase64 }}" | base64 -d > ./res/icon.png - name: icon stuff - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true shell: bash run: | 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 - wget -O ./res/icon.png ${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}"&"uuid=${{ fromJson(inputs.iconlink).uuid }} + wget -O ./res/icon.png ${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}"&"uuid=${{ env.iconlink_uuid }} mv ./res/32x32.png ./res/32x32.png.bak mv ./res/64x64.png ./res/64x64.png.bak mv ./res/128x128.png ./res/128x128.png.bak @@ -315,74 +315,74 @@ jobs: continue-on-error: true shell: bash run: | - sed -i -e 's|rs-ny.rustdesk.com|${{ inputs.server }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ inputs.key }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|https://admin.rustdesk.com|${{ inputs.apiServer }}|' ./src/common.rs + 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 - name: change appname to custom - if: inputs.appname != 'rustdesk' + if: env.appname != 'rustdesk' continue-on-error: true shell: bash run: | - sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./Cargo.toml - sed -i 's|name = "RustDesk"|name = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./libs/portable/Cargo.toml - sed -i -e 's|"RustDesk Remote Desktop"|"${{ inputs.appname }}"|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|VALUE "InternalName", "rustdesk" "\0"|VALUE "InternalName", "${{ inputs.appname }}" "\0"|' ./flutter/windows/runner/Runner.rc + 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|"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"|"${{ inputs.filename }}"|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|"RustDesk"|"${{ inputs.appname }}"|' ./flutter/windows/runner/Runner.rc - find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ inputs.appname }}|' {} \; - sed -i -e 's|RustDesk|${{ inputs.appname }}|' ./flutter/android/app/src/main/res/values/strings.xml - sed -i -e "s|title: 'RustDesk'|title: '${{ inputs.appname }}'|" ./flutter/lib/main.dart - sed -i -e "s|return 'RustDesk';|return '${{ inputs.appname }}';|" ./flutter/lib/web/bridge.dart - sed -i 's|android:label="RustDesk"|android:label="${{ inputs.appname }}"|' ./flutter/android/app/src/main/AndroidManifest.xml - sed -i 's|android:label="RustDesk Input"|android:label="${{ inputs.appname }} Input"|' ./flutter/android/app/src/main/AndroidManifest.xml - sed -i 's|RustDesk is Open|${{ inputs.appname }} is Open|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt - sed -i 's|Show Rustdesk|Show ${{ inputs.appname }}|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt - sed -i 's|"RustDesk"|"${{ inputs.appname }}"|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt - sed -i 's|"RustDesk Service|"${{ inputs.appname }} Service|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt - sed -i 's|RustDesk|${{ inputs.appname }}|' ./flutter/lib/main.dart - sed -i 's|"RustDesk"|"${{ inputs.appname }}"|' ./flutter/lib/desktop/widgets/tabbar_widget.dart - sed -i 's|"RustDesk"|"${{ inputs.appname }}"|' ./libs/hbb_common/src/config.rs + 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 - name: change url to custom - if: fromJson(inputs.extras).urlLink != 'https://rustdesk.com' + if: env.urlLink != 'https://rustdesk.com' continue-on-error: true shell: bash run: | - sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ fromJson(inputs.extras).urlLink }}|' ./build.py - sed -i -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ fromJson(inputs.extras).urlLink }}'));|" ./flutter/lib/common.dart - sed -i -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ fromJson(inputs.extras).urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart - sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart - sed -i -e "s|const url = 'https://rustdesk.com/';|const url = '${{ fromJson(inputs.extras).urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart - sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart - sed -i -e "s|child: Text('rustdesk.com',|child: Text('${{ fromJson(inputs.extras).urlLink }}',|" ./flutter/lib/mobile/pages/settings_page.dart - sed -i -e "s|https://rustdesk.com/privacy.html|${{ fromJson(inputs.extras).urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart + sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ env.urlLink }}|' ./build.py + sed -i -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ env.urlLink }}'));|" ./flutter/lib/common.dart + sed -i -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ env.urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i -e "s|const url = 'https://rustdesk.com/';|const url = '${{ env.urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i -e "s|child: Text('rustdesk.com',|child: Text('${{ env.urlLink }}',|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i -e "s|https://rustdesk.com/privacy.html|${{ env.urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart - name: change app id to custom - if: fromJson(inputs.extras).androidappid != 'com.carriez.flutter_hbb' + if: env.androidappid != 'com.carriez.flutter_hbb' continue-on-error: true shell: bash run: | - sed -i -e 's|com.carriez.flutter_hbb|${{ fromJson(inputs.extras).androidappid }}|' ./flutter/android/app/build.gradle + sed -i -e 's|com.carriez.flutter_hbb|${{ env.androidappid }}|' ./flutter/android/app/build.gradle - name: change download link to custom - if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' + if: env.downloadLink != 'https://rustdesk.com/download' continue-on-error: true shell: bash run: | - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis - name: allow custom.txt continue-on-error: true @@ -394,35 +394,35 @@ jobs: - name: fix connection delay continue-on-error: true - if: ${{ fromJson(inputs.extras).delayFix == 'true' }} + if: ${{ env.delayFix == 'true' }} shell: bash run: | sed -i -e 's|!key.is_empty()|false|' ./src/client.rs - name: add cycle monitors to toolbar continue-on-error: true - if: fromJson(inputs.extras).cycleMonitor == 'true' + if: env.cycleMonitor == 'true' 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: fromJson(inputs.extras).xOffline == 'true' + if: env.xOffline == 'true' run: | wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff git apply xoffline.diff - name: hide-cm continue-on-error: true - if: fromJson(inputs.extras).hidecm == 'true' + if: env.hidecm == 'true' run: | 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: fromJson(inputs.extras).removeNewVersionNotif == 'true' + if: env.removeNewVersionNotif == 'true' 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 @@ -434,10 +434,10 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "35% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "35% complete"}' - name: replace flutter icons - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} run: | pushd ./flutter flutter pub get @@ -489,10 +489,10 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "45% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "45% complete"}' - name: icons - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true run: | mv ./flutter/assets/icon.svg ./flutter/assets/icon.svg.bak @@ -515,7 +515,7 @@ jobs: mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/ cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so - echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt + echo -n "${{ env.custom }}" | cat > ./flutter/assets/custom_.txt #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml # build flutter pushd flutter @@ -526,7 +526,7 @@ jobs: mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/arm-linux-androideabi/libc++_shared.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/ cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so - echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt + echo -n "${{ env.custom }}" | cat > ./flutter/assets/custom_.txt #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml # build flutter pushd flutter @@ -537,7 +537,7 @@ jobs: mkdir -p ./flutter/android/app/src/main/jniLibs/x86_64 cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86_64/ cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86_64/librustdesk.so - echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt + echo -n "${{ env.custom }}" | cat > ./flutter/assets/custom_.txt #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml # build flutter pushd flutter @@ -548,7 +548,7 @@ jobs: mkdir -p ./flutter/android/app/src/main/jniLibs/x86 cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/i686-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86/ cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86/librustdesk.so - echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt + echo -n "${{ env.custom }}" | cat > ./flutter/assets/custom_.txt #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml # build flutter pushd flutter @@ -558,7 +558,7 @@ jobs: esac popd mkdir -p signed-apk; pushd signed-apk - mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk ./${{ inputs.filename }}-${{ matrix.job.arch }}.apk + mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk ./${{ env.filename }}-${{ matrix.job.arch }}.apk popd - name: Report Status @@ -568,7 +568,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "75% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "75% complete"}' - uses: r0adkll/sign-android-release@v1 name: Sign app APK @@ -586,16 +586,16 @@ jobs: BUILD_TOOLS_VERSION: "30.0.2" - name: send file to rdgen server - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} shell: bash run: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./signed-apk/${{ inputs.filename }}-${{ matrix.job.arch }}.apk" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client + 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 - name: send file to api server - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} shell: bash run: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./signed-apk/${{ inputs.filename }}-${{ matrix.job.arch }}.apk" ${{ inputs.apiServer }}/api/save_custom_client + 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 - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -604,7 +604,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Success"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Success"}' - name: failed if: failure() @@ -613,7 +613,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation failed, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation failed, try again"}' - name: failed if: cancelled() @@ -622,4 +622,4 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation cancelled, try again"}' diff --git a/.github/workflows/generator-linux.yml b/.github/workflows/generator-linux.yml index 7422be0..b94ba72 100644 --- a/.github/workflows/generator-linux.yml +++ b/.github/workflows/generator-linux.yml @@ -3,56 +3,16 @@ run-name: Custom Linux Client Generator on: workflow_dispatch: inputs: - server: - description: 'Rendezvous Server' + version: + description: 'version to buld' required: true default: '' type: string - key: - description: 'Public Key' + zip_url: + description: 'url to zip of json' required: true default: '' type: string - apiServer: - description: 'API Server' - required: true - default: '' - type: string - custom: - description: "Custom JSON" - required: true - default: '' - type: string - uuid: - description: "uuid of request" - required: true - default: '' - type: string - iconlink: - description: "icon link" - required: false - default: 'false' - type: string - logolink: - description: "logo link" - required: false - default: 'false' - type: string - appname: - description: "app name" - required: true - default: 'rustdesk' - type: string - filename: - description: "Filename" - required: true - default: 'rustdesk' - type: string - extras: - description: "extra inputs in json" - required: true - default: '{}' - type: string env: SCITER_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503, also 1.78 has ABI change which causes our sciter version not working, https://blog.rust-lang.org/2024/03/30/i128-layout-update.html @@ -71,7 +31,7 @@ env: # vcpkg version: 2024.07.12 VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" - VERSION: "${{ fromJson(inputs.extras).version }}" + VERSION: "${{ inputs.version }}" NDK_VERSION: "r27c" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" @@ -84,7 +44,7 @@ jobs: generate-bridge-linux: uses: ./.github/workflows/bridge.yml with: - version: ${{ fromJson(inputs.extras).version }} + version: ${{ inputs.version }} build-rustdesk-linux: needs: [generate-bridge-linux] @@ -112,6 +72,48 @@ jobs: vcpkg-triplet: arm64-linux, } steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + for attempt in range(5): + try: + print(f"Downloading secrets (Attempt {attempt + 1})...") + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}', timeout=60) + r.raise_for_status() + break + except (requests.exceptions.RequestException, requests.exceptions.Timeout) as e: + if attempt < 4: + print(f"Timeout/Error occurred: {e}. Retrying in 5 seconds...") + time.sleep(5) + else: + print("Max retries reached. Failing.") + raise e + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + - name: Export GitHub Actions cache environment variables uses: actions/github-script@v6 with: @@ -120,14 +122,14 @@ jobs: core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} run: | echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} run: | - echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $GITHUB_ENV + echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $GITHUB_ENV - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -136,7 +138,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "5% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "5% complete"}' - name: Maximize build space run: | @@ -205,7 +207,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "15% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "15% complete"}' - name: Restore bridge files if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' @@ -243,14 +245,14 @@ jobs: shell: bash - name: icon stuff - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true shell: bash run: | 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 - wget -O ./res/icon.png ${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}"&"uuid=${{ fromJson(inputs.iconlink).uuid }} + wget -O ./res/icon.png ${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}"&"uuid=${{ env.iconlink_uuid }} mv ./res/32x32.png ./res/32x32.png.bak mv ./res/64x64.png ./res/64x64.png.bak mv ./res/128x128.png ./res/128x128.png.bak @@ -268,97 +270,97 @@ jobs: b64="" - name: change appname to custom - if: inputs.appname != 'rustdesk' + if: env.appname != 'rustdesk' continue-on-error: true shell: bash run: | - sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./Cargo.toml - sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./libs/portable/Cargo.toml - find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ inputs.appname }}|' {} \; + 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 + find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \; sed -i -e '/-p tmpdeb\/usr\/lib\/rustdesk/d' ./build.py - name: change company name - if: fromJson(inputs.extras).compname != 'Purslane Ltd' + if: env.compname != 'Purslane Ltd' continue-on-error: true shell: bash run: | - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./Cargo.toml - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./libs/portable/Cargo.toml + 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 - name: allow custom.txt continue-on-error: true shell: bash run: | - sed -i -e 's|rs-ny.rustdesk.com|${{ inputs.server }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ inputs.key }}|' ./libs/hbb_common/src/config.rs + 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.diff git apply allowCustom.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff git apply removeSetupServerTip.diff - echo -n "${{ inputs.custom }}" | cat > ./custom_.txt + 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|${{ inputs.apiServer }}|' ./src/common.rs + sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs - name: change url to custom - if: fromJson(inputs.extras).urlLink != 'https://rustdesk.com' + if: env.urlLink != 'https://rustdesk.com' continue-on-error: true shell: bash run: | - sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ fromJson(inputs.extras).urlLink }}|' ./build.py - sed -i -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ fromJson(inputs.extras).urlLink }}'));|" ./flutter/lib/common.dart - sed -i -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ fromJson(inputs.extras).urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart - sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart - sed -i -e "s|const url = 'https://rustdesk.com/';|const url = '${{ fromJson(inputs.extras).urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart - sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart - sed -i -e "s|https://rustdesk.com/privacy.html|${{ fromJson(inputs.extras).urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart + sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ env.urlLink }}|' ./build.py + sed -i -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ env.urlLink }}'));|" ./flutter/lib/common.dart + sed -i -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ env.urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i -e "s|const url = 'https://rustdesk.com/';|const url = '${{ env.urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i -e "s|https://rustdesk.com/privacy.html|${{ env.urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart - name: change download link to custom - if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' + if: env.downloadLink != 'https://rustdesk.com/download' continue-on-error: true shell: bash run: | - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis - name: fix connection delay continue-on-error: true - if: ${{ fromJson(inputs.extras).delayFix == 'true' }} + if: ${{ env.delayFix == 'true' }} shell: bash run: | sed -i -e 's|!key.is_empty()|false|' ./src/client.rs - name: add cycle monitors to toolbar continue-on-error: true - if: fromJson(inputs.extras).cycleMonitor == 'true' + if: env.cycleMonitor == 'true' 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: fromJson(inputs.extras).xOffline == 'true' + if: env.xOffline == 'true' run: | wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff git apply xoffline.diff - name: hide-cm continue-on-error: true - if: fromJson(inputs.extras).hidecm == 'true' + if: env.hidecm == 'true' run: | 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: fromJson(inputs.extras).removeNewVersionNotif == 'true' + if: env.removeNewVersionNotif == 'true' 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 @@ -377,7 +379,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "65% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "65% complete"}' - uses: rustdesk-org/run-on-arch-action@amd64-support name: Build rustdesk @@ -523,10 +525,10 @@ jobs: export DEB_ARCH=${{ matrix.job.deb_arch }} mkdir -p flutter/tmpdeb/usr/share/rustdesk cp ./custom_.txt ./flutter/tmpdeb/usr/share/rustdesk/custom_.txt - if [[ "${{ inputs.logolink }}" != "false" ]]; then - wget -O ./flutter/assets/logo.png ${{ fromJson(inputs.logolink).url }}/get_png?filename=${{ fromJson(inputs.logolink).file }}"&"uuid=${{ fromJson(inputs.logolink).uuid }} + if [[ "${{ env.logolink_url }}" != "false" ]]; then + wget -O ./flutter/assets/logo.png ${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}"&"uuid=${{ env.logolink_uuid }} fi - if [[ "${{ inputs.iconlink }}" != "false" ]]; then + if [[ "${{ env.iconlink_url }}" != "false" ]]; then mv ./flutter/assets/icon.svg ./flutter/assets/icon.svg.bak convert ./res/icon.png ./flutter/assets/icon.svg convert ./res/128x128.png -resize 200% ./flutter/assets/128x128@2x.png || true @@ -541,7 +543,7 @@ jobs: fi python3 ./build.py --flutter --skip-cargo for name in rustdesk*??.deb; do - mv "$name" /workspace/output/"${{ inputs.filename }}-${{ matrix.job.arch }}.deb" + mv "$name" /workspace/output/"${{ env.filename }}-${{ matrix.job.arch }}.deb" done # rpm package @@ -555,7 +557,7 @@ jobs: HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} for name in rustdesk*??.rpm; do - mv "$name" /workspace/output/"${{ inputs.filename }}-${{ matrix.job.arch }}.rpm" + mv "$name" /workspace/output/"${{ env.filename }}-${{ matrix.job.arch }}.rpm" done # rpm suse package @@ -569,7 +571,7 @@ jobs: HBB=`pwd` rpmbuild ./res/rpm-flutter-suse.spec -bb pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} for name in rustdesk*??.rpm; do - mv "$name" /workspace/output/"${{ inputs.filename }}-suse-${{ matrix.job.arch }}.rpm" + mv "$name" /workspace/output/"${{ env.filename }}-suse-${{ matrix.job.arch }}.rpm" done # only x86_64 for arch since we can not find newest arm64 docker image to build @@ -597,32 +599,32 @@ jobs: continue-on-error: true if: matrix.job.arch == 'x86_64' && env.UPLOAD_ARTIFACT == 'true' && env.VERSION != 'master' run: | - cp ./res/rustdesk-${{ env.VERSION }}-0-x86_64.pkg.tar.zst ./output/${{ inputs.filename }}-${{ matrix.job.arch }}.pkg.tar.zst + cp ./res/rustdesk-${{ env.VERSION }}-0-x86_64.pkg.tar.zst ./output/${{ env.filename }}-${{ matrix.job.arch }}.pkg.tar.zst - name: send file to rdgen server - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} shell: bash run: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.deb" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.rpm" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-suse-${{ matrix.job.arch }}.rpm" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.pkg.tar.zst" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client || true + 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 - name: send file to api server - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} shell: bash run: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.deb" ${{ inputs.apiServer }}/api/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.rpm" ${{ inputs.apiServer }}/api/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-suse-${{ matrix.job.arch }}.rpm" ${{ inputs.apiServer }}/api/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.pkg.tar.zst" ${{ inputs.apiServer }}/api/save_custom_client || true + 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 - name: Upload deb uses: actions/upload-artifact@master if: env.UPLOAD_ARTIFACT == 'true' with: - name: ${{ inputs.filename }}-${{ matrix.job.arch }}.deb - path: ./output/${{ inputs.filename }}-${{ matrix.job.arch }}.deb + name: ${{ env.filename }}-${{ matrix.job.arch }}.deb + path: ./output/${{ env.filename }}-${{ matrix.job.arch }}.deb - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -631,7 +633,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Finished ${{ matrix.job.arch }}"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Finished ${{ matrix.job.arch }}"}' - name: failed if: failure() @@ -640,7 +642,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation failed, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation failed, try again"}' - name: failed if: cancelled() @@ -649,7 +651,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation cancelled, try again"}' build-appimage: name: Build appimage ${{ matrix.job.target }} @@ -662,6 +664,48 @@ jobs: - { target: x86_64-unknown-linux-gnu, arch: x86_64 } - { target: aarch64-unknown-linux-gnu, arch: aarch64 } steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + for attempt in range(5): + try: + print(f"Downloading secrets (Attempt {attempt + 1})...") + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}', timeout=60) + r.raise_for_status() + break + except (requests.exceptions.RequestException, requests.exceptions.Timeout) as e: + if attempt < 4: + print(f"Timeout/Error occurred: {e}. Retrying in 5 seconds...") + time.sleep(5) + else: + print("Max retries reached. Failing.") + raise e + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + - name: Checkout source code if: ${{ env.VERSION != 'master' }} uses: actions/checkout@v4 @@ -680,12 +724,12 @@ jobs: - name: Download Binary uses: actions/download-artifact@master with: - name: ${{ inputs.filename }}-${{ matrix.job.arch }}.deb + name: ${{ env.filename }}-${{ matrix.job.arch }}.deb path: . - name: Rename Binary run: | - mv ${{ inputs.filename }}-${{ matrix.job.arch }}.deb appimage/rustdesk.deb + mv ${{ env.filename }}-${{ matrix.job.arch }}.deb appimage/rustdesk.deb - name: Build appimage package shell: bash @@ -698,19 +742,19 @@ jobs: # run appimage-builder pushd appimage sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml - sudo mv ./rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.AppImage ./${{ inputs.filename }}-${{ matrix.job.arch }}.AppImage + sudo mv ./rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.AppImage ./${{ env.filename }}-${{ matrix.job.arch }}.AppImage - name: send file to rdgen server - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} shell: bash run: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./appimage/${{ inputs.filename }}-${{ matrix.job.arch }}.AppImage" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client + 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 - name: send file to api server - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} shell: bash run: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./appimage/${{ inputs.filename }}-${{ matrix.job.arch }}.AppImage" ${{ inputs.apiServer }}/api/save_custom_client + 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 build-flatpak: name: Build flatpak ${{ matrix.job.target }}${{ matrix.job.suffix }} @@ -737,6 +781,48 @@ jobs: suffix: "", } steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + for attempt in range(5): + try: + print(f"Downloading secrets (Attempt {attempt + 1})...") + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}', timeout=60) + r.raise_for_status() + break + except (requests.exceptions.RequestException, requests.exceptions.Timeout) as e: + if attempt < 4: + print(f"Timeout/Error occurred: {e}. Retrying in 5 seconds...") + time.sleep(5) + else: + print("Max retries reached. Failing.") + raise e + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + - name: Checkout source code if: ${{ env.VERSION != 'master' }} uses: actions/checkout@v4 @@ -755,12 +841,12 @@ jobs: - name: Download Binary uses: actions/download-artifact@master with: - name: ${{ inputs.filename }}-${{ matrix.job.arch }}.deb + name: ${{ env.filename }}-${{ matrix.job.arch }}.deb path: . - name: Rename Binary run: | - mv ${{ inputs.filename }}-${{ matrix.job.arch }}.deb flatpak/rustdesk.deb + mv ${{ env.filename }}-${{ matrix.job.arch }}.deb flatpak/rustdesk.deb - uses: rustdesk-org/run-on-arch-action@amd64-support name: Build rustdesk flatpak package for ${{ matrix.job.arch }} @@ -789,21 +875,21 @@ jobs: pushd flatpak git clone https://github.com/flathub/shared-modules.git --depth=1 flatpak-builder --user --install-deps-from=flathub -y --force-clean --repo=repo ./build ./rustdesk.json - flatpak build-bundle ./repo ${{ inputs.filename }}-${{ matrix.job.arch }}.flatpak com.rustdesk.RustDesk + flatpak build-bundle ./repo ${{ env.filename }}-${{ matrix.job.arch }}.flatpak com.rustdesk.RustDesk - name: send file to rdgen server - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} continue-on-error: true shell: bash run: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./flatpak/${{ inputs.filename }}-${{ matrix.job.arch }}.flatpak" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client + 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 - name: send file to api server - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} continue-on-error: true shell: bash run: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./flatpak/${{ inputs.filename }}-${{ matrix.job.arch }}.flatpak" ${{ inputs.apiServer }}/api/save_custom_client + 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 @@ -811,16 +897,66 @@ jobs: needs: [build-rustdesk-linux,build-flatpak,build-appimage] runs-on: ubuntu-latest steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + for attempt in range(5): + try: + print(f"Downloading secrets (Attempt {attempt + 1})...") + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}', timeout=60) + r.raise_for_status() + break + except (requests.exceptions.RequestException, requests.exceptions.Timeout) as e: + if attempt < 4: + print(f"Timeout/Error occurred: {e}. Retrying in 5 seconds...") + time.sleep(5) + else: + print("Max retries reached. Failing.") + raise e + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + + - name: Finalize and Cleanup zip/json + if: always() # Run even if previous steps fail + uses: fjogeleit/http-request-action@v1 + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}"}' - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} run: | echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} run: | - echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $GITHUB_ENV + echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $GITHUB_ENV - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -828,9 +964,9 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Success"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Success"}' - uses: geekyeggo/delete-artifact@v5 continue-on-error: true with: - name: ${{ inputs.filename }}-*.deb + name: ${{ env.filename }}-*.deb diff --git a/.github/workflows/generator-macos.yml b/.github/workflows/generator-macos.yml index 3d06d41..5610c44 100644 --- a/.github/workflows/generator-macos.yml +++ b/.github/workflows/generator-macos.yml @@ -3,56 +3,16 @@ run-name: Custom macOS Client Generator on: workflow_dispatch: inputs: - server: - description: 'Rendezvous Server' + version: + description: 'version to buld' required: true default: '' type: string - key: - description: 'Public Key' + zip_url: + description: 'url to zip of json' required: true default: '' type: string - apiServer: - description: 'API Server' - required: true - default: '' - type: string - custom: - description: "Custom JSON" - required: true - default: '' - type: string - uuid: - description: "uuid of request" - required: true - default: '' - type: string - iconlink: - description: "icon link" - required: false - default: 'false' - type: string - logolink: - description: "logo link" - required: false - default: 'false' - type: string - appname: - description: "app name" - required: true - default: 'rustdesk' - type: string - filename: - description: "Filename" - required: true - default: 'rustdesk' - type: string - extras: - description: "extra inputs in json" - required: true - default: '{}' - type: string env: SCITER_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503, also 1.78 has ABI change which causes our sciter version not working, https://blog.rust-lang.org/2024/03/30/i128-layout-update.html @@ -71,7 +31,7 @@ env: # vcpkg version: 2024.07.12 VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" - VERSION: "${{ fromJson(inputs.extras).version }}" + VERSION: "${{ inputs.version }}" NDK_VERSION: "r27c" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" @@ -83,7 +43,7 @@ jobs: generate-bridge: uses: ./.github/workflows/bridge.yml with: - version: ${{ fromJson(inputs.extras).version }} + version: ${{ inputs.version }} build-for-macos: name: ${{ matrix.job.target }} @@ -95,7 +55,7 @@ jobs: job: - { target: x86_64-apple-darwin, - os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel + os: macos-15-intel, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel extra-build-args: "", arch: x86_64, vcpkg-triplet: x64-osx, @@ -109,9 +69,59 @@ jobs: vcpkg-triplet: arm64-osx, } env: - STATUS_URL: ${{ fromJson(inputs.extras).rdgen == 'true' && format('{0}/updategh', secrets.GENURL) || format('{0}/api/updategh', inputs.apiServer) }} + STATUS_URL: "${{ secrets.GENURL }}/updategh" steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}') + r.raise_for_status() + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + api_server = secrets.get('apiServer', '').strip() + api_server = api_server.rstrip('/') + + rdgen_value = str(secrets.get('rdgen', 'false')).lower() + if rdgen_value == "true": + status_url = "${{ secrets.GENURL }}/updategh" + else: + status_url = f"{api_server}/api/updategh" + env_file.write(f"STATUS_URL={status_url}\n") + + print("Secrets loaded into environment.") + + - name: Finalize and Cleanup zip/json + if: always() # Run even if previous steps fail + uses: fjogeleit/http-request-action@v1 + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}"}' + - name: Export GitHub Actions cache environment variables uses: actions/github-script@v6 with: @@ -125,7 +135,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "5% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "5% complete"}' - name: Checkout source code if: ${{ env.VERSION != 'master' }} @@ -158,73 +168,73 @@ jobs: 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${{ inputs.appname }}|' ./flutter/macos/Runner/Info.plist - sed -i '' -e 's|CFBundleDisplayName.*.*|CFBundleDisplayName\n\t${{ inputs.appname }}|' ./flutter/macos/Runner/Info.plist - sed -i '' -e 's|CFBundleIdentifier.*.*|CFBundleIdentifier\n\tcom.${{ inputs.appname }}.app|' ./flutter/macos/Runner/Info.plist - # sed -i '' '/NSHumanReadableCopyright<\/key>/{n;s/.*<\/string>/Copyright 2025 ${{ inputs.appname }}. All rights reserved.<\/string>/;}' ./flutter/macos/Runner/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 = ${{ inputs.appname }}|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig - sed -i '' -e 's|PRODUCT_BUNDLE_IDENTIFIER = .*|PRODUCT_BUNDLE_IDENTIFIER = com.${{ inputs.appname }}.app|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig - sed -i '' -e 's|Purslane Ltd.|${{ inputs.appname }}|' ./flutter/macos/Runner/Configs/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 + sed -i '' -e 's|Purslane Ltd.|${{ env.appname }}|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig # Keep DEVELOPMENT_TEAM if it exists, don't blank it out - sed -i -e 's|Purslane Ltd.|${{ inputs.appname }}|' ./Cargo.toml - sed -i -e 's|Purslane Ltd|${{ inputs.appname }}|' ./libs/portable/Cargo.toml + 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 = "${{ inputs.appname }}"/' ./flutter/macos/Runner.xcodeproj/project.pbxproj - sed -i '' -e 's/PRODUCT_BUNDLE_IDENTIFIER = ".*"/PRODUCT_BUNDLE_IDENTIFIER = "com.${{ inputs.appname }}.app"/' ./flutter/macos/Runner.xcodeproj/project.pbxproj + 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 # Don't modify DEVELOPMENT_TEAM in project.pbxproj # Update CMake settings if [ -f "./flutter/macos/CMakeLists.txt" ]; then - sed -i '' -e 's/set(BINARY_NAME ".*")/set(BINARY_NAME "${{ inputs.appname }}")/' ./flutter/macos/CMakeLists.txt + sed -i '' -e 's/set(BINARY_NAME ".*")/set(BINARY_NAME "${{ env.appname }}")/' ./flutter/macos/CMakeLists.txt fi # Update Podfile - keep the target as 'Runner' - # sed -i '' -e 's/target '"'"'Runner'"'"' do/target '"'"'${{ inputs.appname }}'"'"' do/' ./flutter/macos/Podfile + # sed -i '' -e 's/target '"'"'Runner'"'"' do/target '"'"'${{ env.appname }}'"'"' do/' ./flutter/macos/Podfile sed -i '' -e 's/target '"'"'Runner'"'"' do/target '"'"'Runner'"'"' do/' ./flutter/macos/Podfile cp ./src/lang/en.rs ./src/lang/en.rs.bak cp ./src/lang/nl.rs ./src/lang/nl.rs.bak - find ./src/lang -name "*.rs" -exec sed -i '' -e 's|RustDesk|${{ inputs.appname }}|' {} \; - sed -i '' -e 's|RustDesk|${{ inputs.appname }}|' ./src/lang/nl.rs + 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|${{ fromJson(inputs.extras).urlLink }}|' ./build.py - sed -i '' -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ fromJson(inputs.extras).urlLink }}'));|" ./flutter/lib/common.dart - sed -i '' -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ fromJson(inputs.extras).urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart - sed -i '' -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart - sed -i '' -e "s|const url = 'https://rustdesk.com/';|const url = '${{ fromJson(inputs.extras).urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart - sed -i '' -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart - sed -i '' -e "s|https://rustdesk.com/privacy.html|${{ fromJson(inputs.extras).urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart + 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 + sed -i '' -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i '' -e "s|const url = 'https://rustdesk.com/';|const url = '${{ env.urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i '' -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i '' -e "s|https://rustdesk.com/privacy.html|${{ env.urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart - name: change download link to custom - if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' + if: env.downloadLink != 'https://rustdesk.com/download' continue-on-error: true shell: bash run: | - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis # Update slogan - #sed -i '' '/NSHumanReadableCopyright<\/key>/{n;s/.*<\/string>/Copyright 2025 ${{ inputs.appname }}. All rights reserved.<\/string>/;}' ./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 # Update slogan - About in en.rs - sed -i '' -e 's/("Slogan_tip", "Made with heart in this chaotic world!")/("Slogan_tip", "Powered by ${{ inputs.appname }}")/' ./src/lang/en.rs - sed -i '' -e 's/("About RustDesk", "")/("About RustDesk", "About ${{ inputs.appname }}")/' ./src/lang/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 ${{ inputs.appname }}")/' ./src/lang/nl.rs - sed -i '' -e 's/("Your Desktop", "Uw Bureaublad")/("Your Desktop", "Uw ${{ inputs.appname }}")/' ./src/lang/nl.rs - sed -i '' -e 's/("About RustDesk", "Over RustDesk")/("About RustDesk", "Over ${{ inputs.appname }}")/' ./src/lang/nl.rs - sed -i '' -e 's/("About", "Over")/("About", "Over ${{ inputs.appname }}")/' ./src/lang/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 + sed -i '' -e 's/("Your Desktop", "Uw Bureaublad")/("Your Desktop", "Uw ${{ env.appname }}")/' ./src/lang/nl.rs + sed -i '' -e 's/("About RustDesk", "Over RustDesk")/("About RustDesk", "Over ${{ env.appname }}")/' ./src/lang/nl.rs + sed -i '' -e 's/("About", "Over")/("About", "Over ${{ env.appname }}")/' ./src/lang/nl.rs - sed -i -e 's|rs-ny.rustdesk.com|${{ inputs.server }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ inputs.key }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|https://admin.rustdesk.com|${{ inputs.apiServer }}|' ./src/common.rs + 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.diff git apply allowCustom.diff @@ -244,11 +254,11 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "10% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "10% complete"}' - name: Install build runtime run: | - brew install llvm create-dmg nasm + brew install llvm create-dmg # pkg-config is handled in a separate step, because it may be already installed by `macos-latest`(14.7.1) runner if command -v pkg-config &>/dev/null; then echo "pkg-config is already installed" @@ -256,6 +266,17 @@ jobs: brew install pkg-config fi + - name: Install NASM + run: | + # Install NASM 2.16.x from official release. + # Do NOT use `brew install nasm` which installs NASM 3.x. + # NASM 3.x is a complete rewrite with incompatible CLI options and removed features. + # aom and other multimedia libraries require NASM 2.x for x86/x86_64 assembly. + wget https://www.nasm.us/pub/nasm/releasebuilds/2.16.03/macosx/nasm-2.16.03-macosx.zip + unzip nasm-2.16.03-macosx.zip + sudo cp nasm-2.16.03/nasm /usr/local/bin/nasm + nasm --version + - name: Install flutter uses: subosito/flutter-action@v2 with: @@ -288,7 +309,7 @@ jobs: prefix-key: ${{ matrix.job.os }} - name: Magick stuff for macOS - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: false shell: bash run: | @@ -302,8 +323,8 @@ jobs: # Download icon using curl with additional SSL options curl -k -L --tlsv1.2 --proto =https --ssl-reqd \ -H "User-Agent: Mozilla/5.0" \ - "${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}&uuid=${{ fromJson(inputs.iconlink).uuid }}" \ - -o ./res/icon.png || wget --no-check-certificate -O ./res/icon.png "${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}&uuid=${{ fromJson(inputs.iconlink).uuid }}" + "${{ 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 @@ -398,7 +419,7 @@ jobs: ls -lh rustdesk/data/flutter_assets/assets/ - name: replace flutter icons - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: false shell: bash run: | @@ -406,8 +427,8 @@ jobs: # Create required directories and files mkdir -p web mkdir -p assets - echo '{"name":"${{ inputs.appname }}","short_name":"${{ inputs.appname }}","start_url":"/","display":"standalone","background_color":"#ffffff","theme_color":"#ffffff","description":"A remote desktop software."}' > web/manifest.json - echo '${{ inputs.appname }}' > web/index.html + 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 @@ -422,7 +443,7 @@ jobs: cd .. - name: ui.rs - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true shell: bash run: | @@ -435,35 +456,35 @@ jobs: - name: fix connection delay continue-on-error: false - if: ${{ fromJson(inputs.extras).delayFix == 'true' }} + if: ${{ env.delayFix == 'true' }} shell: bash run: | sed -i -e 's|!key.is_empty()|false|' ./src/client.rs - name: add cycle monitors to toolbar continue-on-error: true - if: fromJson(inputs.extras).cycleMonitor == 'true' + if: env.cycleMonitor == 'true' 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: fromJson(inputs.extras).xOffline == 'true' + if: env.xOffline == 'true' run: | wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff git apply xoffline.diff - name: hide-cm continue-on-error: true - if: fromJson(inputs.extras).hidecm == 'true' + if: env.hidecm == 'true' run: | 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: fromJson(inputs.extras).removeNewVersionNotif == 'true' + if: env.removeNewVersionNotif == 'true' 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 @@ -474,7 +495,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "20% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "20% complete"}' - name: Restore bridge files uses: actions/download-artifact@master @@ -510,7 +531,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "25% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "25% complete"}' - name: Create MacOS directory structure run: | @@ -525,7 +546,7 @@ jobs: sed -i -e "s/osx_minimum_system_version = \"[0-9]*.[0-9]*\"/osx_minimum_system_version = \"${MIN_MACOS_VERSION}\"/" Cargo.toml sed -i -e "s/MACOSX_DEPLOYMENT_TARGET = [0-9]*.[0-9]*;/MACOSX_DEPLOYMENT_TARGET = ${MIN_MACOS_VERSION};/" flutter/macos/Runner.xcodeproj/project.pbxproj fi - sed -i -e "s/RustDesk.app/\"${{ inputs.appname }}.app\"/" build.py + sed -i -e "s/RustDesk.app/\"${{ env.appname }}.app\"/" build.py ./build.py --flutter --hwcodec --unix-file-copy-paste ${{ matrix.job.extra-build-args }} # - name: Copy service file @@ -538,7 +559,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "50% complete, this step takes about 5 minutes, be patient."}' + data: '{"uuid": "${{ env.uuid }}", "status": "50% complete, this step takes about 5 minutes, be patient."}' - name: Install rcodesign tool if: env.MACOS_P12_BASE64 != null @@ -566,7 +587,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "70% complete, this step takes about 5 minutes, be patient."}' + data: '{"uuid": "${{ env.uuid }}", "status": "70% complete, this step takes about 5 minutes, be patient."}' - name: Show version information (Rust, cargo, Clang) shell: bash @@ -579,7 +600,7 @@ jobs: rustc -V - name: icon svg handling - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: false shell: bash run: | @@ -595,7 +616,7 @@ jobs: rm ./temp_icon.pbm - name: logo handling - if: ${{ inputs.logolink != 'false' }} + if: ${{ env.logolink_url != 'false' }} continue-on-error: false shell: bash run: | @@ -603,11 +624,11 @@ jobs: mkdir -p "$ASSETS_DIR" curl -k -L --tlsv1.2 --proto =https --ssl-reqd \ -H "User-Agent: Mozilla/5.0" \ - "${{ fromJson(inputs.logolink).url }}/get_png?filename=${{ fromJson(inputs.logolink).file }}&uuid=${{ fromJson(inputs.logolink).uuid }}" \ + "${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}&uuid=${{ env.logolink_uuid }}" \ -o "$ASSETS_DIR/logo.png" || \ wget --no-check-certificate \ -O "$ASSETS_DIR/logo.png" \ - "${{ fromJson(inputs.logolink).url }}/get_png?filename=${{ fromJson(inputs.logolink).file }}&uuid=${{ fromJson(inputs.logolink).uuid }}" + "${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}&uuid=${{ env.logolink_uuid }}" - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -615,7 +636,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "85% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "85% complete"}' - name: Sign macOS app bundle if: env.MACOS_P12_BASE64 != '' @@ -628,21 +649,21 @@ jobs: # 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" "${{ inputs.appname }}.app" - echo "Renamed RustDesk.app to ${{ inputs.appname }}.app" + mv "RustDesk.app" "${{ env.appname }}.app" + echo "Renamed RustDesk.app to ${{ env.appname }}.app" fi echo "App bundle contents after rename:" - ls -la "${{ inputs.appname }}.app" || echo "App not found" - ls -la "${{ inputs.appname }}.app/Contents" || echo "Contents not found" + 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 "${{ inputs.appname }}.app/Contents/MacOS" ]; then + if [ -d "${{ env.appname }}.app/Contents/MacOS" ]; then echo "Signing main executable..." - MAIN_EXECUTABLE="${{ inputs.appname }}.app/Contents/MacOS/${{ inputs.appname }}" + MAIN_EXECUTABLE="${{ env.appname }}.app/Contents/MacOS/${{ env.appname }}" if [ -f "$MAIN_EXECUTABLE" ]; then rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \ --code-signature-flags runtime "$MAIN_EXECUTABLE" @@ -650,23 +671,23 @@ jobs: echo "Main executable not found at expected path: $MAIN_EXECUTABLE" # Try to find the actual executable echo "Available executables in MacOS directory:" - ls -la "${{ inputs.appname }}.app/Contents/MacOS/" - ACTUAL_EXECUTABLE=$(ls "${{ inputs.appname }}.app/Contents/MacOS/" | head -n 1) + ls -la "${{ env.appname }}.app/Contents/MacOS/" + ACTUAL_EXECUTABLE=$(ls "${{ env.appname }}.app/Contents/MacOS/" | head -n 1) if [ -n "$ACTUAL_EXECUTABLE" ]; then echo "Found executable: $ACTUAL_EXECUTABLE" rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \ - --code-signature-flags runtime "${{ inputs.appname }}.app/Contents/MacOS/$ACTUAL_EXECUTABLE" + --code-signature-flags runtime "${{ env.appname }}.app/Contents/MacOS/$ACTUAL_EXECUTABLE" fi fi echo "Signing frameworks..." - find "${{ inputs.appname }}.app/Contents/Frameworks" -type f -not -name ".*" -exec \ + 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 "${{ inputs.appname }}.app" + --code-signature-flags runtime "${{ env.appname }}.app" else echo "Error: Invalid app bundle structure" exit 1 @@ -684,24 +705,24 @@ jobs: # Find the actual .app bundle if [ -d "RustDesk.app" ]; then # First rename the app if it's still called RustDesk.app - mv "RustDesk.app" "${{ inputs.appname }}.app" + mv "RustDesk.app" "${{ env.appname }}.app" fi - if [ ! -d "${{ inputs.appname }}.app" ]; then + if [ ! -d "${{ env.appname }}.app" ]; then echo "Could not find .app bundle!" exit 1 fi - echo "Creating DMG for ${{ inputs.appname }}.app" + echo "Creating DMG for ${{ env.appname }}.app" create-dmg \ - --volname "${{ inputs.appname }}" \ + --volname "${{ env.appname }}" \ --window-pos 200 120 \ --window-size 800 400 \ --icon-size 100 \ - --icon "${{ inputs.appname }}.app" 200 190 \ - --hide-extension "${{ inputs.appname }}.app" \ + --icon "${{ env.appname }}.app" 200 190 \ + --hide-extension "${{ env.appname }}.app" \ --app-drop-link 600 185 \ - "${{ inputs.appname }}-${{ matrix.job.arch }}.dmg" \ - "${{ inputs.appname }}.app" - mv "${{ inputs.appname }}-${{ matrix.job.arch }}.dmg" $GITHUB_WORKSPACE/ + "${{ 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' @@ -711,38 +732,38 @@ jobs: ls -la # Find the DMG file dynamically - DMG_FILE=$(find . -name "${{ inputs.appname }}-${{ matrix.job.arch }}.dmg") + DMG_FILE=$(find . -name "${{ env.appname }}-${{ matrix.job.arch }}.dmg") if [ -n "$DMG_FILE" ]; then echo "Found DMG file: $DMG_FILE" - mv "$DMG_FILE" "${{ inputs.filename }}-${{ matrix.job.arch }}.dmg" - echo "Renamed to ${{ inputs.filename }}-${{ matrix.job.arch }}.dmg" + mv "$DMG_FILE" "${{ env.filename }}-${{ matrix.job.arch }}.dmg" + echo "Renamed to ${{ env.filename }}-${{ matrix.job.arch }}.dmg" else echo "No DMG file found matching the pattern" exit 1 fi - name: send file to rdgen server - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} shell: bash run: | curl -i -X POST \ -H "Content-Type: multipart/form-data" \ - -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" \ - -F "file=@$GITHUB_WORKSPACE/${{ inputs.filename }}-${{ matrix.job.arch }}.dmg" \ - -F "uuid=${{ inputs.uuid }}" \ + -H "Authorization: Bearer ${{ env.token }}" \ + -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: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} shell: bash run: | curl -i -X POST \ -H "Content-Type: multipart/form-data" \ - -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" \ - -F "file=@$GITHUB_WORKSPACE/${{ inputs.filename }}-${{ matrix.job.arch }}.dmg" \ - "${{ inputs.apiServer }}/api/save_custom_client" + -H "Authorization: Bearer ${{ env.token }}" \ + -F "file=@$GITHUB_WORKSPACE/${{ env.filename }}-${{ matrix.job.arch }}.dmg" \ + "${{ env.apiServer }}/api/save_custom_client" - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -750,7 +771,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Success"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Success"}' - name: failed if: failure() @@ -759,7 +780,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation failed, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation failed, try again"}' - name: failed if: cancelled() @@ -768,4 +789,4 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation cancelled, try again"}' diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index 205f65d..a8c8f44 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -3,56 +3,16 @@ run-name: Custom Windows Client Generator on: workflow_dispatch: inputs: - server: - description: 'Rendezvous Server' + version: + description: 'version to buld' required: true default: '' type: string - key: - description: 'Public Key' + zip_url: + description: 'url to zip of json' required: true default: '' type: string - apiServer: - description: 'API Server' - required: true - default: '' - type: string - custom: - description: "Custom JSON" - required: true - default: '' - type: string - uuid: - description: "uuid of request" - required: true - default: '' - type: string - iconlink: - description: "icon link" - required: false - default: 'false' - type: string - logolink: - description: "logo link" - required: false - default: 'false' - type: string - appname: - description: "app name" - required: true - default: 'rustdesk' - type: string - filename: - description: "Filename" - required: true - default: 'rustdesk' - type: string - extras: - description: "extra inputs in json" - required: true - default: '{}' - type: string env: @@ -71,7 +31,7 @@ env: # vcpkg version: 2024.07.12 VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" - VERSION: "${{ fromJson(inputs.extras).version }}" + VERSION: "${{ inputs.version }}" NDK_VERSION: "r27c" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" @@ -85,7 +45,7 @@ jobs: generate-bridge: uses: ./.github/workflows/bridge.yml with: - version: ${{ fromJson(inputs.extras).version }} + version: ${{ inputs.version }} build-RustDeskTempTopMostWindow: uses: ./.github/workflows/third-party-RustDeskTempTopMostWindow.yml @@ -116,6 +76,46 @@ jobs: } # - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 } steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}') + r.raise_for_status() + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + + - name: Finalize and Cleanup zip/json + if: always() # Run even if previous steps fail + uses: fjogeleit/http-request-action@v1 + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}"}' + - name: Export GitHub Actions cache environment variables uses: actions/github-script@v6 with: @@ -124,14 +124,14 @@ jobs: core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} run: | echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $env:GITHUB_ENV - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} run: | - echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $env:GITHUB_ENV + echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $env:GITHUB_ENV - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -140,7 +140,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "5% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "5% complete"}' - name: Checkout source code if: ${{ env.VERSION != 'master' }} @@ -175,34 +175,34 @@ jobs: sed -i -e 's|2ded7f146437a761ffe6981e2f742038f85ca68d|08a471bb8ceccdd50483c81cdfa8b81b07b14b87|' ./flutter/pubspec.yaml - name: change appname to custom - if: inputs.appname != 'rustdesk' + if: env.appname != 'rustdesk' continue-on-error: true shell: bash run: | # ./Cargo.toml - sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./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 = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./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 # ./flutter/windows/runner/Runner.rc - sed -i -e 's|"RustDesk Remote Desktop"|"${{ inputs.appname }}"|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|VALUE "InternalName", "rustdesk" "\0"|VALUE "InternalName", "${{ inputs.appname }}" "\0"|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|"rustdesk.exe"|"${{ inputs.filename }}"|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|"RustDesk"|"${{ inputs.appname }}"|' ./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 "${{ fromJson(inputs.extras).compname }}" ]; then - find ./src/lang -name "*.rs" -exec sed -i '/powered_by_me/s|RustDesk|${{ fromJson(inputs.extras).compname }}|g' {} \; + 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|${{ inputs.appname }}|' {} \; + find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \; - name: fix registry if appname has a space - if: contains(inputs.appname, ' ') + if: contains(env.appname, ' ') continue-on-error: true shell: bash run: | @@ -222,47 +222,47 @@ jobs: 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: fromJson(inputs.extras).compname != 'Purslane Ltd' + if: env.compname != 'Purslane Ltd' continue-on-error: true shell: bash run: | - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart - sed -i -e 's|PURSLANE|${{ fromJson(inputs.extras).compname }}|' ./res/msi/preprocess.py - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./res/msi/preprocess.py - sed -i -e 's|"Copyright © 2025 Purslane Ltd. All rights reserved."|"Copyright © 2025 ${{ fromJson(inputs.extras).compname }}. All rights reserved."|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./Cargo.toml - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./libs/portable/Cargo.toml + 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 - name: change url to custom - if: fromJson(inputs.extras).urlLink != 'https://rustdesk.com' + if: env.urlLink != 'https://rustdesk.com' continue-on-error: true shell: bash run: | - sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ fromJson(inputs.extras).urlLink }}|' ./build.py - sed -i -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ fromJson(inputs.extras).urlLink }}'));|" ./flutter/lib/common.dart - sed -i -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ fromJson(inputs.extras).urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart - sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart - sed -i -e "s|const url = 'https://rustdesk.com/';|const url = '${{ fromJson(inputs.extras).urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart - sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart - sed -i -e "s|https://rustdesk.com/privacy.html|${{ fromJson(inputs.extras).urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart + sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ env.urlLink }}|' ./build.py + sed -i -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ env.urlLink }}'));|" ./flutter/lib/common.dart + sed -i -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ env.urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i -e "s|const url = 'https://rustdesk.com/';|const url = '${{ env.urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i -e "s|https://rustdesk.com/privacy.html|${{ env.urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart - name: change download link to custom - if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' + if: env.downloadLink != 'https://rustdesk.com/download' continue-on-error: true shell: bash run: | - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis - name: set server, key, and apiserver continue-on-error: true shell: bash run: | - sed -i -e 's|rs-ny.rustdesk.com|${{ inputs.server }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ inputs.key }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|https://admin.rustdesk.com|${{ inputs.apiServer }}|' ./src/common.rs + 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 @@ -287,7 +287,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "10% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "10% complete"}' - name: Install flutter uses: subosito/flutter-action@v2.12.0 #https://github.com/subosito/flutter-action/issues/277 @@ -325,7 +325,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "15% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "15% complete"}' - uses: Swatinem/rust-cache@v2 with: @@ -338,7 +338,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "20% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "20% complete"}' - name: Setup vcpkg with Github Actions binary cache uses: lukka/run-vcpkg@v11 @@ -368,10 +368,10 @@ jobs: shell: bash - name: magick stuff - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true run: | - Invoke-WebRequest -Uri ${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}"&"uuid=${{ fromJson(inputs.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 @@ -389,7 +389,7 @@ jobs: - name: ui.rs icon - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true shell: bash run: | @@ -400,28 +400,28 @@ jobs: - name: fix connection delay continue-on-error: true - if: ${{ fromJson(inputs.extras).delayFix == 'true' }} + if: ${{ env.delayFix == 'true' }} shell: bash run: | sed -i -e 's|!key.is_empty()|false|' ./src/client.rs - name: add cycle monitors to toolbar continue-on-error: true - if: fromJson(inputs.extras).cycleMonitor == 'true' + if: env.cycleMonitor == 'true' run: | Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/cycle_monitor.diff -OutFile cycle_monitor.diff git apply cycle_monitor.diff - name: use X for offline display instead of orange circle continue-on-error: true - if: fromJson(inputs.extras).xOffline == 'true' + if: env.xOffline == 'true' run: | Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff -OutFile xoffline.diff git apply xoffline.diff - name: removeNewVersionNotif continue-on-error: true - if: fromJson(inputs.extras).removeNewVersionNotif == 'true' + if: env.removeNewVersionNotif == 'true' shell: bash run: | sed -i -e 's|updateUrl.isNotEmpty|false|' ./flutter/lib/desktop/pages/desktop_home_page.dart @@ -429,7 +429,7 @@ jobs: # - name: run as admin # continue-on-error: true - # if: ${{ fromJson(inputs.extras).runasadmin == 'true' }} + # if: ${{ env.runasadmin == 'true' }} # shell: bash # run: | # sed -i '/<\/compatibility>/a \ @@ -444,10 +444,10 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "25% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "25% complete"}' - name: replace flutter icons - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true run: | cd ./flutter @@ -463,7 +463,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "50% complete, this step takes about 5 minutes, be patient."}' + data: '{"uuid": "${{ env.uuid }}", "status": "50% complete, this step takes about 5 minutes, be patient."}' - name: Build rustdesk run: | @@ -506,17 +506,17 @@ jobs: } - name: icon stuff - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true run: | mv ./rustdesk/data/flutter_assets/assets/icon.svg ./rustdesk/data/flutter_assets/assets/icon.svg.bak magick ./res/icon.png ./rustdesk/data/flutter_assets/assets/icon.svg - name: logo stuff - if: ${{ inputs.logolink != 'false' }} + if: ${{ env.logolink_url != 'false' }} continue-on-error: true run: | - Invoke-WebRequest -Uri ${{ fromJson(inputs.logolink).url }}/get_png?filename=${{ fromJson(inputs.logolink).file }}"&"uuid=${{ fromJson(inputs.logolink).uuid }} -OutFile ./rustdesk/data/flutter_assets/assets/logo.png + Invoke-WebRequest -Uri ${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}"&"uuid=${{ env.logolink_uuid }} -OutFile ./rustdesk/data/flutter_assets/assets/logo.png - name: find Runner.res # Windows: find Runner.res (compiled from ./flutter/windows/runner/Runner.rc), copy to ./Runner.res @@ -548,7 +548,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "70% complete, this step takes about 5 minutes, be patient."}' + data: '{"uuid": "${{ env.uuid }}", "status": "70% complete, this step takes about 5 minutes, be patient."}' - name: zip dlls continue-on-error: true @@ -581,17 +581,17 @@ jobs: - name: Create custom.txt file shell: bash run: | - echo -n "${{ inputs.custom }}" | cat > ./rustdesk/custom_.txt + echo -n "${{ env.custom }}" | cat > ./rustdesk/custom_.txt - name: Build self-extracted executable shell: bash if: env.UPLOAD_ARTIFACT == 'true' run: | - mv "./rustdesk/rustdesk.exe" "./rustdesk/${{ inputs.appname }}.exe" || echo "rustdesk.exe" + mv "./rustdesk/rustdesk.exe" "./rustdesk/${{ env.appname }}.exe" || echo "rustdesk.exe" sed -i '/dpiAware/d' res/manifest.xml pushd ./libs/portable pip3 install -r requirements.txt - python3 ./generate.py -f ../../rustdesk/ -o . -e "../../rustdesk/${{ inputs.appname }}.exe" + python3 ./generate.py -f ../../rustdesk/ -o . -e "../../rustdesk/${{ env.appname }}.exe" popd mkdir -p ./SignOutput mv ./target/release/rustdesk-portable-packer.exe "./SignOutput/rustdesk.exe" @@ -603,8 +603,8 @@ jobs: continue-on-error: true if: env.UPLOAD_ARTIFACT == 'true' run: | - $myappname = "${{ inputs.appname }}" -replace '\s','_' - cp "rustdesk/${{ inputs.appname }}.exe" "rustdesk/${myappname}.exe" -ErrorAction SilentlyContinue + $myappname = "${{ env.appname }}" -replace '\s','_' + cp "rustdesk/${{ env.appname }}.exe" "rustdesk/${myappname}.exe" -ErrorAction SilentlyContinue pushd ./res/msi python preprocess.py --app-name "$myappname" --arp -d ../../rustdesk nuget restore msi.sln @@ -620,7 +620,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "85% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "85% complete"}' - name: zip exe and msi continue-on-error: true @@ -652,26 +652,26 @@ jobs: - name: rename rustdesk.exe to filename.exe run: | - mv ./SignOutput/rustdesk.exe "./SignOutput/${{ inputs.filename }}.exe" || echo "rustdesk" + mv ./SignOutput/rustdesk.exe "./SignOutput/${{ env.filename }}.exe" || echo "rustdesk" - name: rename rustdesk.msi to filename.msi continue-on-error: true run: | - mv ./SignOutput/rustdesk.msi "./SignOutput/${{ inputs.filename }}.msi" || echo "rustdesk" + mv ./SignOutput/rustdesk.msi "./SignOutput/${{ env.filename }}.msi" || echo "rustdesk" - name: send file to rdgen server - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} shell: bash run: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./SignOutput/${{ inputs.filename }}.exe" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./SignOutput/${{ inputs.filename }}.msi" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client || true + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.exe" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.msi" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client || true - name: send file to api server - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} shell: bash run: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./SignOutput/${{ inputs.filename }}.exe" ${{ inputs.apiServer }}/api/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./SignOutput/${{ inputs.filename }}.msi" ${{ inputs.apiServer }}/api/save_custom_client || true + 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 - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -679,7 +679,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Success"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Success"}' - name: failed if: failure() @@ -688,7 +688,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation failed, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation failed, try again"}' - name: failed if: cancelled() @@ -697,4 +697,4 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation cancelled, try again"}' diff --git a/docker-compose.yml b/docker-compose.yml index c55db3f..68f22f4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,7 @@ services: rdgen: # use bryangerlach/rdgen:latest for the latest build + #build: . image: bryangerlach/rdgen:latest restart: unless-stopped environment: @@ -8,6 +9,8 @@ services: GHUSER: "github_username" GHBEARER: "github_access_token" GENURL: "accessible_url_of_server" + ZIP_PASSWORD: "" + GHBRANCH: "master" PROTOCOL: "https" REPONAME: "rdgen" ports: diff --git a/rdgen/settings.py b/rdgen/settings.py index 9e64ac4..9931e2f 100644 --- a/rdgen/settings.py +++ b/rdgen/settings.py @@ -24,6 +24,8 @@ SECRET_KEY = os.environ.get('SECRET_KEY','django-insecure-!(t-!f#6g#sr%yfded9(xh GHUSER = os.environ.get("GHUSER", '') GHBEARER = os.environ.get("GHBEARER", '') GENURL = os.environ.get("GENURL", '') +GHBRANCH = os.environ.get("GHBRANCH",'master') +ZIP_PASSWORD = os.environ.get("ZIP_PASSWORD",'insecure') PROTOCOL = os.environ.get("PROTOCOL", 'https') REPONAME = os.environ.get("REPONAME", 'rdgen') diff --git a/rdgen/urls.py b/rdgen/urls.py index c182e9d..9159056 100644 --- a/rdgen/urls.py +++ b/rdgen/urls.py @@ -32,4 +32,5 @@ urlpatterns = [ url(r'^startgh',views.startgh), url(r'^get_png',views.get_png), url(r'^save_custom_client',views.save_custom_client), + url(r'^get_zip',views.get_zip), ] diff --git a/rdgenerator/views.py b/rdgenerator/views.py index 75850dc..4c4639b 100644 --- a/rdgenerator/views.py +++ b/rdgenerator/views.py @@ -9,14 +9,13 @@ import requests import base64 import json import uuid +import pyzipper from django.conf import settings as _settings from django.db.models import Q from .forms import GenerateForm from .models import GithubRun from PIL import Image from urllib.parse import quote -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import padding def generator_view(request): if request.method == 'POST': @@ -48,6 +47,8 @@ def generator_view(request): installation = form.cleaned_data['installation'] settings = form.cleaned_data['settings'] appname = form.cleaned_data['appname'] + if not appname: + appname = "rustdesk" filename = form.cleaned_data['exename'] compname = form.cleaned_data['compname'] if not compname: @@ -98,18 +99,22 @@ def generator_view(request): iconfile = form.cleaned_data.get('iconfile') if not iconfile: iconfile = form.cleaned_data.get('iconbase64') - iconlink = save_png(iconfile,myuuid,full_url,"icon.png") + iconlink_url, iconlink_uuid, iconlink_file = save_png(iconfile,myuuid,full_url,"icon.png") except: print("failed to get icon, using default") - iconlink = "false" + iconlink_url = "false" + iconlink_uuid = "false" + iconlink_file = "false" try: logofile = form.cleaned_data.get('logofile') if not logofile: logofile = form.cleaned_data.get('logobase64') - logolink = save_png(logofile,myuuid,full_url,"logo.png") + logolink_url, logolink_uuid, logolink_file = save_png(logofile,myuuid,full_url,"logo.png") except: print("failed to get logo") - logolink = "false" + logolink_url = "false" + logolink_uuid = "false" + logolink_file = "false" ###create the custom.txt json here and send in as inputs below decodedCustom = {} @@ -192,21 +197,20 @@ def generator_view(request): base64_bytes = base64.b64encode(string_bytes) encodedCustom = base64_bytes.decode("ascii") - #github limits inputs to 10, so lump extras into one with json - extras = {} - extras['genurl'] = _settings.GENURL - #extras['runasadmin'] = runasadmin - extras['urlLink'] = urlLink - extras['downloadLink'] = downloadLink - extras['delayFix'] = 'true' if delayFix else 'false' - extras['version'] = version - extras['rdgen'] = 'true' - extras['cycleMonitor'] = 'true' if cycleMonitor else 'false' - extras['xOffline'] = 'true' if xOffline else 'false' - extras['removeNewVersionNotif'] = 'true' if removeNewVersionNotif else 'false' - extras['compname'] = compname - extras['androidappid'] = androidappid - extra_input = json.dumps(extras) + # #github limits inputs to 10, so lump extras into one with json + # extras = {} + # extras['genurl'] = _settings.GENURL + # #extras['runasadmin'] = runasadmin + # extras['urlLink'] = urlLink + # extras['downloadLink'] = downloadLink + # extras['delayFix'] = 'true' if delayFix else 'false' + # extras['rdgen'] = 'true' + # extras['cycleMonitor'] = 'true' if cycleMonitor else 'false' + # extras['xOffline'] = 'true' if xOffline else 'false' + # extras['removeNewVersionNotif'] = 'true' if removeNewVersionNotif else 'false' + # extras['compname'] = compname + # extras['androidappid'] = androidappid + # extra_input = json.dumps(extras) ####from here run the github action, we need user, repo, access token. if platform == 'windows': @@ -223,19 +227,59 @@ def generator_view(request): url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-windows.yml/dispatches' #url = 'https://api.github.com/repos/'+_settings.GHUSER+'/rustdesk/actions/workflows/test.yml/dispatches' + inputs_raw = { + "server":server, + "key":key, + "apiServer":apiServer, + "custom":encodedCustom, + "uuid":myuuid, + "iconlink_url":iconlink_url, + "iconlink_uuid":iconlink_uuid, + "iconlink_file":iconlink_file, + "logolink_url":logolink_url, + "logolink_uuid":logolink_uuid, + "logolink_file":logolink_file, + "appname":appname, + "genurl":_settings.GENURL, + "urlLink":urlLink, + "downloadLink":downloadLink, + "delayFix": 'true' if delayFix else 'false', + "rdgen":'true', + "cycleMonitor": 'true' if cycleMonitor else 'false', + "xOffline": 'true' if xOffline else 'false', + "removeNewVersionNotif": 'true' if removeNewVersionNotif else 'false', + "compname": compname, + "androidappid":androidappid, + "filename":filename + } + + temp_json_path = f"data_{uuid.uuid4()}.json" + zip_filename = f"secrets_{uuid.uuid4()}.zip" + zip_path = "temp_zips/%s" % (zip_filename) + Path("temp_zips").mkdir(parents=True, exist_ok=True) + + with open(temp_json_path, "w") as f: + json.dump(inputs_raw, f) + + with pyzipper.AESZipFile(zip_path, 'w', compression=pyzipper.ZIP_LZMA, encryption=pyzipper.WZ_AES) as zf: + zf.setpassword(_settings.ZIP_PASSWORD.encode()) + zf.write(temp_json_path, arcname="secrets.json") + + # 4. Cleanup the plain JSON file immediately + if os.path.exists(temp_json_path): + os.remove(temp_json_path) + + zipJson = {} + zipJson['url'] = full_url + zipJson['file'] = zip_filename + + zip_url = json.dumps(zipJson) + data = { - "ref":"master", + "ref":_settings.GHBRANCH, "inputs":{ - "server":server, - "key":key, - "apiServer":apiServer, - "custom":encodedCustom, - "uuid":myuuid, - "iconlink":iconlink, - "logolink":logolink, - "appname":appname, - "extras":extra_input, - "filename":filename + "version":version, + "zip_url":zip_url } } #print(data) @@ -357,7 +401,7 @@ def startgh(request): ####from here run the github action, we need user, repo, access token. url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-'+data_.get('platform')+'.yml/dispatches' data = { - "ref":"master", + "ref": _settings.GHBRANCH, "inputs":{ "server":data_.get('server'), "key":data_.get('key'), @@ -400,12 +444,12 @@ def save_png(file, uuid, domain, name): with open(file_save_path, "wb+") as f: for chunk in file.chunks(): f.write(chunk) - imageJson = {} - imageJson['url'] = domain - imageJson['uuid'] = uuid - imageJson['file'] = name + # imageJson = {} + # imageJson['url'] = domain + # imageJson['uuid'] = uuid + # imageJson['file'] = name #return "%s/%s" % (domain, file_save_path) - return json.dumps(imageJson) + return domain, uuid, name def save_custom_client(request): file = request.FILES['file'] @@ -417,3 +461,37 @@ def save_custom_client(request): f.write(chunk) return HttpResponse("File saved successfully!") + +def cleanup_secrets(request): + # Pass the UUID as a query param or in JSON body + my_uuid = request.GET.get('uuid') + + if not my_uuid: + return HttpResponse("Missing UUID", status=400) + + # 1. Find the files in your temp directory matching the UUID + temp_dir = os.path.join('temp_zips') + + # We look for any file starting with 'secrets_' and containing the uuid + for filename in os.listdir(temp_dir): + if my_uuid in filename and filename.endswith('.zip'): + file_path = os.path.join(temp_dir, filename) + try: + os.remove(file_path) + print(f"Successfully deleted {file_path}") + except OSError as e: + print(f"Error deleting file: {e}") + + return HttpResponse("Cleanup successful", status=200) + +def get_zip(request): + filename = request.GET['filename'] + #filename = filename+".exe" + file_path = os.path.join('temp_zips',filename) + with open(file_path, 'rb') as file: + response = HttpResponse(file, headers={ + 'Content-Type': 'application/vnd.microsoft.portable-executable', + 'Content-Disposition': f'attachment; filename="{filename}"' + }) + + return response diff --git a/requirements.txt b/requirements.txt index 6a09b31..a59cd13 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ django requests pillow gunicorn -cryptography>=42.0.0 \ No newline at end of file +pyzipper \ No newline at end of file diff --git a/setup.md b/setup.md index 781ee18..bc7adb7 100644 --- a/setup.md +++ b/setup.md @@ -20,10 +20,14 @@ * Now click New repository secret * Set the Name to GENURL * Set the Secret to https://rdgen.hostname.com (or whatever your server will be accessed from) + * Now click New repository secret again + * Set the Name to ZIP_PASSWORD + * Set the Secret to any password you want (use this in the next step as well) - generate a password by running: ```python3 -c 'import secrets; print(secrets.token_hex(100))'``` 4. Now download the docker-compose.yml file and fill in the environment variables: * SECRET_KEY="your secret key" - generate a secret key by running: ```python3 -c 'import secrets; print(secrets.token_hex(100))'``` * GHUSER="your github username" * GHBEARER="your fine-grained access token" + * ZIP_PASSWORD="the same password that you entered as a github secret" * PROTOCOL="https" *optional - defaults to "https", change to "http" if you need to * REPONAME="rdgen" *optional - defaults to "rdgen", change this if you renamed the repo when you forked it 5. Now just run ```docker compose up -d```