diff --git a/.github/workflows/sh-generator-windows.yml b/.github/workflows/sh-generator-windows.yml new file mode 100644 index 0000000..db16ecd --- /dev/null +++ b/.github/workflows/sh-generator-windows.yml @@ -0,0 +1,641 @@ +name: Custom Windows Client Generator +run-name: Custom Windows Client Generator +on: + workflow_dispatch: + inputs: + version: + description: 'version to buld' + required: true + default: '' + type: string + zip_url: + description: 'url to zip of json' + required: true + default: '' + type: string + + +env: + SCITER_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503, also 1.78 has ABI change which causes our sciter version not working, https://blog.rust-lang.org/2024/03/30/i128-layout-update.html + RUST_VERSION: "1.75" # sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html + CARGO_NDK_VERSION: "3.1.2" + SCITER_ARMV7_CMAKE_VERSION: "3.29.7" + SCITER_NASM_DEBVERSION: "2.15.05-1" + LLVM_VERSION: "15.0.6" + FLUTTER_VERSION: "3.24.5" + ANDROID_FLUTTER_VERSION: "3.24.5" + # for arm64 linux because official Dart SDK does not work + FLUTTER_ELINUX_VERSION: "3.16.9" + TAG_NAME: "${{ inputs.upload-tag }}" + VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" + # vcpkg version: 2024.07.12 + VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" + ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" + VERSION: "${{ inputs.version }}" + NDK_VERSION: "r27c" + #signing keys env variable checks + ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" + MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}" + UPLOAD_ARTIFACT: 'true' + SIGN_BASE_URL: "${{ secrets.SIGN_BASE_URL }}" + STATUS_URL: "${{ secrets.GENURL }}/updategh" + + +jobs: + setup: + uses: ./.github/workflows/fetch-encrypted-secrets.yml + with: + zip_url_json: ${{ inputs.zip_url }} + + generate-bridge: + uses: ./.github/workflows/bridge.yml + with: + version: ${{ inputs.version }} + + build-RustDeskTempTopMostWindow: + needs: setup + uses: ./.github/workflows/third-party-RustDeskTempTopMostWindow.yml + with: + upload-artifact: true + target: windows-2022 + configuration: Release + platform: x64 + target_version: Windows10 + secrets: inherit + strategy: + fail-fast: false + + build-for-windows-flutter: + name: Build Windows + needs: [build-RustDeskTempTopMostWindow, generate-bridge, setup] + runs-on: self-hosted + strategy: + fail-fast: false + matrix: + job: + # - { target: i686-pc-windows-msvc , os: windows-2022 } + # - { target: x86_64-pc-windows-gnu , os: windows-2022 } + - { + target: x86_64-pc-windows-msvc, + os: windows-2022, + arch: x86_64, + vcpkg-triplet: x64-windows-static, + } + # - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 } + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4 + with: + name: encrypted-secrets-zip + + - name: Load Secrets + uses: ./.github/actions/decrypt-secrets + with: + zip_password: ${{ secrets.ZIP_PASSWORD }} + + - name: Finalize and Cleanup zip/json + if: always() # Run even if previous steps fail + continue-on-error: true + uses: fjogeleit/http-request-action@v1 + with: + url: "${{ secrets.GENURL }}/cleanzip" + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}"}' + + - name: Export GitHub Actions cache environment variables + uses: actions/github-script@v6 + with: + script: | + core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + + - name: Set rdgen value + if: ${{ env.rdgen == 'true' }} + run: | + echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $env:GITHUB_ENV + + - name: Set rdgen value + if: ${{ env.rdgen == 'false' }} + run: | + echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $env:GITHUB_ENV + + - name: Report Status + uses: fjogeleit/http-request-action@v1 + continue-on-error: true + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}", "status": "5% complete"}' + + - name: Checkout source code + if: ${{ env.VERSION != 'master' }} + uses: actions/checkout@v4 + with: + repository: rustdesk/rustdesk + ref: refs/tags/${{ env.VERSION }} + submodules: recursive + + - name: Checkout source code + if: ${{ env.VERSION == 'master' }} + uses: actions/checkout@v4 + with: + repository: rustdesk/rustdesk + submodules: recursive + + - name: Restore bridge files + uses: actions/download-artifact@master + with: + name: bridge-artifact + path: ./ + + - name: change appname to custom + if: env.appname != 'rustdesk' + continue-on-error: true + shell: bash + run: | + # ./Cargo.toml + sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./Cargo.toml + sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./Cargo.toml + sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./Cargo.toml + sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./Cargo.toml + # ./libs/portable/Cargo.toml + sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./libs/portable/Cargo.toml + sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./libs/portable/Cargo.toml + sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./libs/portable/Cargo.toml + sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./libs/portable/Cargo.toml + # ./libs/portable/src/main.rs + sed -i -e 's|const APP_PREFIX: \&str = "rustdesk";|const APP_PREFIX: \&str = "${{ env.appname }}";|' ./libs/portable/src/main.rs + # ./flutter/windows/runner/Runner.rc + sed -i -e 's|"RustDesk Remote Desktop"|"${{ env.appname }}"|' ./flutter/windows/runner/Runner.rc + sed -i -e 's|VALUE "InternalName", "rustdesk" "\0"|VALUE "InternalName", "${{ env.appname }}" "\0"|' ./flutter/windows/runner/Runner.rc + sed -i -e 's|"rustdesk.exe"|"${{ env.filename }}"|' ./flutter/windows/runner/Runner.rc + sed -i -e 's|"RustDesk"|"${{ env.appname }}"|' ./flutter/windows/runner/Runner.rc + # ./src/lang/en.rs + # change powered by rustdek to powered by compname + if [ ! -z "${{ env.compname }}" ]; then + find ./src/lang -name "*.rs" -exec sed -i '/powered_by_me/s|RustDesk|${{ env.compname }}|g' {} \; + fi + find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \; + sed -i -e 's|RustDesk|${{ env.appname }}|' ./res/msi/Package/License.rtf + + - name: fix registry if appname has a space + if: contains(env.appname, ' ') + continue-on-error: true + shell: bash + run: | + #./src/platform/windows.rs + sed -i -e 's|reg add {}|reg add \\\"{}\\\"|' ./src/platform/windows.rs + sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext} /f|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\" /f|' ./src/platform/windows.rs + sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext}\\\\DefaultIcon /f|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\\DefaultIcon\\\" /f|' ./src/platform/windows.rs + sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell /f|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\" /f|' ./src/platform/windows.rs + sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\\open /f|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\\open\\\" /f|' ./src/platform/windows.rs + sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\\open\\\\command|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\\open\\\\command\\\"|' ./src/platform/windows.rs + sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\{ext} /f|reg add \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\" /f|' ./src/platform/windows.rs + sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\{ext}\\\\shell /f|reg add \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\" /f|' ./src/platform/windows.rs + sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\\open /f|reg add \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\\open\\\" /f|' ./src/platform/windows.rs + sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\\open\\\\command /f|reg add \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\\open\\\\command\\\" /f|' ./src/platform/windows.rs + sed -i -e 's|{subkey}|\\\"{subkey}\\\"|' ./src/platform/windows.rs + sed -i -e 's|reg delete HKEY_CLASSES_ROOT\\\\.{ext} /f|reg delete \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\" /f|' ./src/platform/windows.rs + sed -i -e 's|reg delete HKEY_CLASSES_ROOT\\\\{ext} /f|reg delete \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\" /f|' ./src/platform/windows.rs + + - name: change company name + if: env.compname != 'Purslane Ltd' + continue-on-error: true + shell: bash + run: | + sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i -e 's|PURSLANE|${{ env.compname }}|' ./res/msi/preprocess.py + sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./res/msi/preprocess.py + sed -i -e 's|"Copyright © 2025 Purslane Ltd. All rights reserved."|"Copyright © 2025 ${{ env.compname }}. All rights reserved."|' ./flutter/windows/runner/Runner.rc + sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./flutter/windows/runner/Runner.rc + sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./Cargo.toml + sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./libs/portable/Cargo.toml + sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./res/msi/Package/License.rtf + + - name: change url to custom + if: env.urlLink != 'https://rustdesk.com' + continue-on-error: true + shell: bash + run: | + sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ env.urlLink }}|' ./build.py + sed -i -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ env.urlLink }}'));|" ./flutter/lib/common.dart + sed -i -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ env.urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i -e "s|const url = 'https://rustdesk.com/';|const url = '${{ env.urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i -e "s|https://rustdesk.com/privacy.html|${{ env.urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart + sed -i -e "s|rustdesk.com|${{ env.urlLink }}|" ./res/msi/Package/License.rtf + + - name: change download link to custom + if: env.downloadLink != 'https://rustdesk.com/download' + continue-on-error: true + shell: bash + run: | + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis + + - name: set server, key, and apiserver + continue-on-error: true + shell: bash + run: | + sed -i -e 's|rs-ny.rustdesk.com|${{ env.server }}|' ./libs/hbb_common/src/config.rs + sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.key }}|' ./libs/hbb_common/src/config.rs + sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs + # ./flutter/pubspec.yaml + #sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml + + - name: allow custom.txt + uses: nick-fields/retry@v3 + with: + timeout_minutes: 1 + max_attempts: 3 + shell: pwsh + command: | + Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.py -OutFile allowCustom.py + python allowCustom.py + # Remove Setup Server Tip + Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff -OutFile removeSetupServerTip.diff + git apply removeSetupServerTip.diff + + - name: Report Status + uses: fjogeleit/http-request-action@v1 + continue-on-error: true + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}", "status": "10% complete"}' + + - name: Report Status + uses: fjogeleit/http-request-action@v1 + continue-on-error: true + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}", "status": "20% complete"}' + + - name: magick stuff + if: ${{ env.iconlink_url != 'false' }} + uses: nick-fields/retry@v3 + with: + timeout_minutes: 1 + max_attempts: 3 + shell: pwsh + command: | + Invoke-WebRequest -Uri ${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}"&"uuid=${{ env.iconlink_uuid }} -OutFile ./res/iconx.png + mv ./res/icon.ico ./res/icon.ico.bak + mv ./res/icon.png ./res/icon.png.bak + mv ./res/tray-icon.ico ./res/tray-icon.ico.bak + mv ./res/iconx.png ./res/icon.png + mv ./res/32x32.png ./res/32x32.png.bak + mv ./res/64x64.png ./res/64x64.png.bak + mv ./res/128x128.png ./res/128x128.png.bak + mv ./res/128x128@2x.png ./res/128x128@2x.png.bak + magick ./res/icon.png -define icon:auto-resize=256,64,48,32,16 ./res/icon.ico + cp ./res/icon.ico ./res/tray-icon.ico + magick ./res/icon.png -resize 32x32 ./res/32x32.png + magick ./res/icon.png -resize 64x64 ./res/64x64.png + magick ./res/icon.png -resize 128x128 ./res/128x128.png + magick ./res/128x128.png -resize 200% ./res/128x128@2x.png + + + - name: ui.rs icon + if: ${{ env.iconlink_url != 'false' }} + continue-on-error: true + shell: bash + run: | + cp ./src/ui.rs ./src/ui.rs.bak + b64=$(base64 -w0 < ./res/icon.png) + perl -0777 -pe "s|iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHL[A-Za-z0-9+/=]+|$b64|g" -i.bak ./src/ui.rs + b64="" + + - name: fix connection delay + continue-on-error: true + if: ${{ env.delayFix == 'true' }} + shell: bash + run: | + sed -i -e 's|!key.is_empty()|false|' ./src/client.rs + + - name: add cycle monitors to toolbar + if: env.cycleMonitor == 'true' + uses: nick-fields/retry@v3 + with: + timeout_minutes: 1 + max_attempts: 3 + shell: pwsh + continue-on-error: true + command: | + Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/cycle_monitor.diff -OutFile cycle_monitor.diff + git apply cycle_monitor.diff + + - name: use X for offline display instead of orange circle + if: env.xOffline == 'true' + uses: nick-fields/retry@v3 + with: + timeout_minutes: 1 + max_attempts: 3 + shell: pwsh + continue-on-error: true + command: | + Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff -OutFile xoffline.diff + git apply xoffline.diff + + - name: removeNewVersionNotif + continue-on-error: true + if: env.removeNewVersionNotif == 'true' + shell: bash + run: | + sed -i -e 's|updateUrl.isNotEmpty|false|' ./flutter/lib/desktop/pages/desktop_home_page.dart + sed -i '/let (request, url) =/,/Ok(())/{/Ok(())/!d}' ./src/common.rs + + - name: Report Status + uses: fjogeleit/http-request-action@v1 + continue-on-error: true + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}", "status": "25% complete"}' + + - name: replace flutter icons + if: ${{ env.iconlink_url != 'false' }} + continue-on-error: true + run: | + cd ./flutter + #flutter pub upgrade win32 + flutter pub get + flutter pub run flutter_launcher_icons + cd .. + + - name: Report Status + uses: fjogeleit/http-request-action@v1 + continue-on-error: true + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}", "status": "50% complete"}' + + - name: Build rustdesk + run: | + # Windows: build RustDesk + python3 .\build.py --portable --hwcodec --flutter --vram --skip-portable-pack + mv ./flutter/build/windows/x64/runner/Release ./rustdesk + + # Download usbmmidd_v2.zip and extract it to ./rustdesk + Invoke-WebRequest -Uri https://github.com/rustdesk-org/rdev/releases/download/usbmmidd_v2/usbmmidd_v2.zip -OutFile usbmmidd_v2.zip + Expand-Archive usbmmidd_v2.zip -DestinationPath . -Force + Remove-Item -Path usbmmidd_v2\Win32 -Recurse + Remove-Item -Path "usbmmidd_v2\deviceinstaller64.exe", "usbmmidd_v2\deviceinstaller.exe", "usbmmidd_v2\usbmmidd.bat" + mv -Force .\usbmmidd_v2 ./rustdesk + + # Download printer driver files and extract them to ./rustdesk + try { + Invoke-WebRequest -Uri https://github.com/rustdesk/hbb_common/releases/download/driver/rustdesk_printer_driver_v4-1.4.zip -OutFile rustdesk_printer_driver_v4-1.4.zip + Invoke-WebRequest -Uri https://github.com/rustdesk/hbb_common/releases/download/driver/printer_driver_adapter.zip -OutFile printer_driver_adapter.zip + Invoke-WebRequest -Uri https://github.com/rustdesk/hbb_common/releases/download/driver/sha256sums -OutFile sha256sums + + # Check and move the files + $checksum_driver = (Select-String -Path .\sha256sums -Pattern '^([a-fA-F0-9]{64}) \*rustdesk_printer_driver_v4-1.4\.zip$').Matches.Groups[1].Value + $downloadsum_driver = Get-FileHash -Path rustdesk_printer_driver_v4-1.4.zip -Algorithm SHA256 + $checksum_adapter = (Select-String -Path .\sha256sums -Pattern '^([a-fA-F0-9]{64}) \*printer_driver_adapter\.zip$').Matches.Groups[1].Value + $downloadsum_adapter = Get-FileHash -Path printer_driver_adapter.zip -Algorithm SHA256 + if ($checksum_driver -eq $downloadsum_driver.Hash -and $checksum_adapter -eq $downloadsum_adapter.Hash) { + Write-Output "rustdesk_printer_driver_v4-1.4, checksums match, extract the file." + Expand-Archive rustdesk_printer_driver_v4-1.4.zip -DestinationPath . + mkdir ./rustdesk/drivers + mv -Force .\rustdesk_printer_driver_v4-1.4 ./rustdesk/drivers/RustDeskPrinterDriver + Expand-Archive printer_driver_adapter.zip -DestinationPath . + mv -Force .\printer_driver_adapter.dll ./rustdesk + } elseif ($checksum_driver -ne $downloadsum_driver.Hash) { + Write-Output "rustdesk_printer_driver_v4-1.4, checksums do not match, ignore the file." + } else { + Write-Output "printer_driver_adapter.dll, checksums do not match, ignore the file." + } + } catch { + Write-Host "Ingore the printer driver error." + } + + - name: icon stuff + if: ${{ env.iconlink_url != 'false' }} + continue-on-error: true + run: | + mv ./rustdesk/data/flutter_assets/assets/icon.svg ./rustdesk/data/flutter_assets/assets/icon.svg.bak + magick ./res/icon.png ./rustdesk/data/flutter_assets/assets/icon.svg + + - name: logo stuff + if: ${{ env.logolink_url != 'false' }} + uses: nick-fields/retry@v3 + with: + timeout_minutes: 1 + max_attempts: 3 + shell: pwsh + continue-on-error: true + command: | + Invoke-WebRequest -Uri ${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}"&"uuid=${{ env.logolink_uuid }} -OutFile ./rustdesk/data/flutter_assets/assets/logo.png + + - name: find Runner.res + # Windows: find Runner.res (compiled from ./flutter/windows/runner/Runner.rc), copy to ./Runner.res + # Runner.rc does not contain actual version, but Runner.res does + continue-on-error: true + shell: bash + run: | + runner_res=$(find . -name "Runner.res"); + if [ "$runner_res" == "" ]; then + echo "Runner.res: not found"; + else + echo "Runner.res: $runner_res"; + cp $runner_res ./libs/portable/Runner.res; + echo "list ./libs/portable/Runner.res"; + ls -l ./libs/portable/Runner.res; + fi + + - name: Download RustDeskTempTopMostWindow artifacts + uses: actions/download-artifact@master + if: env.UPLOAD_ARTIFACT == 'true' + with: + name: topmostwindow-artifacts + path: "./rustdesk" + + - name: Report Status + uses: fjogeleit/http-request-action@v1 + continue-on-error: true + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}", "status": "70% complete"}' + + - name: zip dlls + continue-on-error: true + shell: pwsh + run: | + Compress-Archive -Path ./rustdesk/*.dll, ./rustdesk/*.exe -DestinationPath ./rustdesk/unsigned_files.zip -CompressionLevel Fastest + + - name: sign dlls + continue-on-error: true + shell: bash + run: | + if [ ! -z "${{ secrets.SIGN_BASE_URL }}" ] && [ ! -z "${{ secrets.SIGN_API_KEY }}" ]; then + curl -X POST -F "file=@./rustdesk/unsigned_files.zip" \ + -H "X-API-KEY: ${{ secrets.SIGN_API_KEY }}" \ + -m 900 \ + "${{ secrets.SIGN_BASE_URL }}/sign/" -o ./rustdesk/signed_files.zip + else + echo "Signing skipped - signing URL or API key not configured" + cp ./rustdesk/unsigned_files.zip ./rustdesk/signed_files.zip + fi + + - name: unzip dlls + continue-on-error: true + shell: pwsh + run: | + Expand-Archive -Path ./rustdesk/signed_files.zip -DestinationPath ./rustdesk/ -Force + Remove-Item ./rustdesk/unsigned_files.zip + Remove-Item ./rustdesk/signed_files.zip + + - name: Create custom.txt file + shell: bash + run: | + echo -n "${{ env.custom }}" | cat > ./rustdesk/custom_.txt + + - name: Build self-extracted executable + shell: bash + if: env.UPLOAD_ARTIFACT == 'true' + run: | + mv "./rustdesk/rustdesk.exe" "./rustdesk/${{ env.appname }}.exe" || echo "rustdesk.exe" + sed -i '/dpiAware/d' res/manifest.xml + pushd ./libs/portable + pip3 install -r requirements.txt + python3 ./generate.py -f ../../rustdesk/ -o . -e "../../rustdesk/${{ env.appname }}.exe" + popd + mkdir -p ./SignOutput + mv ./target/release/rustdesk-portable-packer.exe "./SignOutput/rustdesk.exe" + + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v2 + + - name: Build msi + continue-on-error: true + if: env.UPLOAD_ARTIFACT == 'true' + run: | + $myappname = "${{ env.appname }}" -replace '\s','_' + cp "rustdesk/${{ env.appname }}.exe" "rustdesk/${myappname}.exe" -ErrorAction SilentlyContinue + pushd ./res/msi + python preprocess.py --app-name "$myappname" --arp -d ../../rustdesk + nuget restore msi.sln + msbuild msi.sln -p:Configuration=Release -p:Platform=x64 /p:TargetVersion=Windows10 + cp ./Package/bin/x64/Release/en-us/Package.msi ../../SignOutput/rustdesk-latest.msi + mv ./Package/bin/x64/Release/en-us/Package.msi ../../SignOutput/rustdesk.msi + sha256sum ../../SignOutput/rustdesk.msi + + - name: Report Status + uses: fjogeleit/http-request-action@v1 + continue-on-error: true + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}", "status": "85% complete"}' + + - name: zip exe and msi + continue-on-error: true + shell: pwsh + run: | + Compress-Archive -Path ./SignOutput/*.exe, ./SignOutput/*.msi -DestinationPath ./SignOutput/unsigned_files.zip -CompressionLevel Fastest + + - name: sign exe and msi + continue-on-error: true + shell: bash + run: | + if [ ! -z "${{ secrets.SIGN_BASE_URL }}" ] && [ ! -z "${{ secrets.SIGN_API_KEY }}" ]; then + curl -X POST -F "file=@./SignOutput/unsigned_files.zip" \ + -H "X-API-KEY: ${{ secrets.SIGN_API_KEY }}" \ + -m 900 \ + "${{ secrets.SIGN_BASE_URL }}/sign/" -o ./SignOutput/signed_files.zip + else + echo "Signing skipped - signing URL or API key not configured" + cp ./SignOutput/unsigned_files.zip ./SignOutput/signed_files.zip + fi + + - name: unzip exe and msi + continue-on-error: true + shell: pwsh + run: | + Expand-Archive -Path ./SignOutput/signed_files.zip -DestinationPath ./SignOutput/ -Force + Remove-Item ./SignOutput/unsigned_files.zip + Remove-Item ./SignOutput/signed_files.zip + + - name: rename rustdesk.exe to filename.exe + run: | + mv ./SignOutput/rustdesk.exe "./SignOutput/${{ env.filename }}.exe" || echo "rustdesk" + + - name: rename rustdesk.msi to filename.msi + continue-on-error: true + run: | + mv ./SignOutput/rustdesk.msi "./SignOutput/${{ env.filename }}.msi" || echo "rustdesk" + + - name: send file to rdgen server + if: ${{ env.rdgen == 'true' }} + uses: nick-fields/retry@v3 + with: + timeout_minutes: 1 + max_attempts: 3 + shell: bash + command: | + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.exe" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.msi" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client || true + + - name: send file to api server + if: ${{ env.rdgen == 'false' }} + uses: nick-fields/retry@v3 + with: + timeout_minutes: 1 + max_attempts: 3 + shell: bash + command: | + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.exe" ${{ env.apiServer }}/api/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.msi" ${{ env.apiServer }}/api/save_custom_client || true + + - name: Report Status + uses: fjogeleit/http-request-action@v1 + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Success"}' + + - name: failed + if: failure() + uses: fjogeleit/http-request-action@v1 + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation failed, try again"}' + + - name: failed + if: cancelled() + uses: fjogeleit/http-request-action@v1 + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation cancelled, try again"}' + + cleanup: + needs: [build-for-windows-flutter] + runs-on: ubuntu-latest + continue-on-error: true + if: always() + steps: + - name: Delete secrets artifact + uses: geekyeggo/delete-artifact@v5 + with: + name: encrypted-secrets-zip diff --git a/docker-compose.yml b/docker-compose.yml index 68f22f4..d3610a9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,6 +13,7 @@ services: GHBRANCH: "master" PROTOCOL: "https" REPONAME: "rdgen" + SH_SECRET: "" ports: - "8000:8000" dns: diff --git a/rdgenerator/views.py b/rdgenerator/views.py index 65d8e39..8780258 100644 --- a/rdgenerator/views.py +++ b/rdgenerator/views.py @@ -21,6 +21,12 @@ def generator_view(request): if request.method == 'POST': form = GenerateForm(request.POST, request.FILES) if form.is_valid(): + user_secret = form.cleaned_data.get('sh_secret_field', '') + master_secret = os.getenv('SH_SECRET') + if master_secret and secrets.compare_digest(user_secret, master_secret): + selfhosted = True + else: + selfhosted = False platform = form.cleaned_data['platform'] version = form.cleaned_data['version'] delayFix = form.cleaned_data['delayFix'] @@ -225,6 +231,8 @@ def generator_view(request): ####from here run the github action, we need user, repo, access token. if platform == 'windows': url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-windows.yml/dispatches' + if selfhosted: + url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/sh-generator-windows.yml/dispatches' if platform == 'windows-x86': url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-windows-x86.yml/dispatches' elif platform == 'linux':