Custom Windows x86 Client Generator #601
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Custom Windows x86 Client Generator | |
| run-name: Custom Windows x86 Client Generator | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| server: | |
| description: 'Rendezvous Server' | |
| required: true | |
| default: '' | |
| type: string | |
| key: | |
| description: 'Public Key' | |
| 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 | |
| RUST_VERSION: "1.75" # sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html | |
| MAC_RUST_VERSION: "1.81" # 1.81 is requred for macos, because of https://github.com/yury/cidre requires 1.81 | |
| 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: 2025.08.27 | |
| # If we change the `VCPKG COMMIT_ID`, please remember: | |
| # 1. Call `$VCPKG_ROOT/vcpkg x-update-baseline` to update the baseline in `vcpkg.json`. | |
| # Or we may face build issue like | |
| # https://github.com/rustdesk/rustdesk/actions/runs/14414119794/job/40427970174 | |
| # 2. Update the `VCPKG_COMMIT_ID` in `ci.yml` and `playground.yml`. | |
| VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" | |
| ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" # 2025.01.13, got "/opt/artifacts/vcpkg/vcpkg: No such file or directory" with latest version | |
| VERSION: "${{ fromJson(inputs.extras).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: | |
| build-for-windows-sciter: | |
| name: ${{ matrix.job.target }} (${{ matrix.job.os }}) | |
| runs-on: ${{ matrix.job.os }} | |
| # Temporarily disable this action due to additional test is needed. | |
| # if: false | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| job: | |
| # - { target: i686-pc-windows-msvc , os: windows-2022 } | |
| # - { target: x86_64-pc-windows-gnu , os: windows-2022 } | |
| - { | |
| target: i686-pc-windows-msvc, | |
| os: windows-2022, | |
| arch: x86, | |
| vcpkg-triplet: x86-windows-static, | |
| } | |
| # - { target: aarch64-pc-windows-msvc, os: windows-2022 } | |
| steps: | |
| - 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: ${{ fromJson(inputs.extras).rdgen == 'true' }} | |
| run: | | |
| echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $env:GITHUB_ENV | |
| - name: Set rdgen value | |
| if: ${{ fromJson(inputs.extras).rdgen == 'false' }} | |
| run: | | |
| echo "STATUS_URL=${{ inputs.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": "${{ inputs.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: Install ImageMagick on Windows | |
| run: | | |
| choco install -y imagemagick.app --no-progress | |
| Get-ChildItem -Path "${env:ProgramFiles}" | % { $_.FullName } | Select-String -Pattern "[\/\\]ImageMagick[^\/\\]*$" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 | |
| - name: change appname to custom | |
| if: inputs.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 | |
| # ./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 | |
| # ./src/lang/en.rs | |
| find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ inputs.appname }}|' {} \; | |
| - name: fix registry if appname has a space | |
| if: contains(inputs.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: fromJson(inputs.extras).compname != 'Purslane Ltd' | |
| continue-on-error: true | |
| shell: bash | |
| run: | | |
| 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.|Copyright \© 2025 ${{ fromJson(inputs.extras).compname }}|' ./src/ui/index.tis | |
| 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.|${{ fromJson(inputs.extras).compname }}|' ./res/setup.nsi | |
| - name: change url to custom | |
| if: fromJson(inputs.extras).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|<div .link .custom-event url='https://rustdesk.com'>|<div .link .custom-event url='${{ fromJson(inputs.extras).urlLink }}'>|" ./src/ui/index.tis | |
| sed -i -e "s|<div .link .custom-event url='https://rustdesk.com/privacy.html'>|<div .link .custom-event url='${{ fromJson(inputs.extras).urlLink }}/privacy.html'>|" ./src/ui/index.tis | |
| sed -i -e "s|https://rustdesk.com/|${{fromJson(inputs.extras).urlLink }}|" ./res/setup.nsi | |
| - name: change download link to custom | |
| if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' | |
| continue-on-error: true | |
| shell: bash | |
| run: | | |
| sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).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|<span>{translate("Ready")}, <span .link #setup-server>{translate("setup_server_tip")}</span></span>|translate("Ready")|' ./src/ui/index.tis | |
| - name: allow custom.txt | |
| continue-on-error: true | |
| run: | | |
| Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.diff -OutFile allowCustom.diff | |
| git apply allowCustom.diff | |
| - name: Install LLVM and Clang | |
| uses: rustdesk-org/install-llvm-action-32bit@master | |
| with: | |
| version: ${{ env.LLVM_VERSION }} | |
| - 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": "${{ inputs.uuid }}", "status": "10% complete"}' | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@v1 | |
| with: | |
| toolchain: nightly-2023-10-13-${{ matrix.job.target }} # must use nightly here, because of abi_thiscall feature required | |
| targets: ${{ matrix.job.target }} | |
| components: "rustfmt" | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| prefix-key: ${{ matrix.job.os }}-sciter | |
| - 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": "${{ inputs.uuid }}", "status": "20% complete"}' | |
| - name: Setup vcpkg with Github Actions binary cache | |
| uses: lukka/run-vcpkg@v11 | |
| with: | |
| vcpkgDirectory: C:\vcpkg | |
| vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} | |
| doNotCache: false | |
| - name: Install vcpkg dependencies | |
| env: | |
| VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.job.vcpkg-triplet }} | |
| run: | | |
| if ! $VCPKG_ROOT/vcpkg \ | |
| install \ | |
| --triplet ${{ matrix.job.vcpkg-triplet }} \ | |
| --x-install-root="$VCPKG_ROOT/installed"; then | |
| find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do | |
| echo "$_1:" | |
| echo "======" | |
| cat "$_1" | |
| echo "======" | |
| echo "" | |
| done | |
| exit 1 | |
| fi | |
| head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true | |
| shell: bash | |
| - name: icon stuff | |
| if: ${{ inputs.iconlink != '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 | |
| - name: magick stuff | |
| if: ${{ inputs.iconlink != '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/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/[email protected] ./res/[email protected] | |
| 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/[email protected] | |
| - name: ui.rs icon | |
| if: ${{ inputs.iconlink != 'false' }} | |
| continue-on-error: true | |
| shell: bash | |
| run: | | |
| cp ./src/ui.rs ./src/ui.rs.bak | |
| b64=$(base64 -w0 < ./res/icon.png) | |
| sed -i -e "s|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|$(echo $b64)|" ./src/ui.rs | |
| b64="" | |
| - name: fix connection delay | |
| continue-on-error: true | |
| if: ${{ fromJson(inputs.extras).delayFix == 'true' }} | |
| shell: bash | |
| run: | | |
| sed -i -e 's|!key.is_empty()|false|' ./src/client.rs | |
| - name: removeNewVersionNotif | |
| continue-on-error: true | |
| if: fromJson(inputs.extras).removeNewVersionNotif == 'true' | |
| shell: bash | |
| run: | | |
| sed -i -e 's|{software_update_url ? <UpdateMe /> : ""}||' ./src/ui/index.tis | |
| 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": "${{ inputs.uuid }}", "status": "50% complete, this step takes about 5 minutes, be patient."}' | |
| - name: Build rustdesk | |
| id: build | |
| shell: bash | |
| run: | | |
| python3 res/inline-sciter.py | |
| # Patch sciter x86 | |
| sed -i 's/branch = "dyn"/branch = "dyn_x86"/g' ./Cargo.toml | |
| cargo build --features inline,vram,hwcodec --release --bins | |
| mkdir -p ./Release | |
| mv ./target/release/rustdesk.exe ./Release/rustdesk.exe | |
| curl -LJ -o ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll | |
| echo "output_folder=./Release" >> $GITHUB_OUTPUT | |
| curl -LJ -o ./usbmmidd_v2.zip https://github.com/rustdesk-org/rdev/releases/download/usbmmidd_v2/usbmmidd_v2.zip | |
| unzip usbmmidd_v2.zip | |
| # Do not remove x64 files, because the user may run the 32bit version on a 64bit system. | |
| # Do not remove ./usbmmidd_v2/deviceinstaller64.exe, as x86 exe cannot install and uninstall drivers when running on x64, | |
| # we need the x64 exe to install and uninstall the driver. | |
| rm -rf ./usbmmidd_v2/deviceinstaller.exe ./usbmmidd_v2/usbmmidd.bat | |
| mv ./usbmmidd_v2 ./Release || true | |
| - 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: 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": "${{ inputs.uuid }}", "status": "70% complete, this step takes about 5 minutes, be patient."}' | |
| - name: zip dlls | |
| continue-on-error: true | |
| shell: pwsh | |
| run: | | |
| Compress-Archive -Path ./Release/*.dll, ./Release/*.exe -DestinationPath ./Release/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=@./Release/unsigned_files.zip" \ | |
| -H "X-API-KEY: ${{ secrets.SIGN_API_KEY }}" \ | |
| -m 900 \ | |
| "${{ secrets.SIGN_BASE_URL }}/sign/" -o ./Release/signed_files.zip | |
| else | |
| echo "Signing skipped - signing URL or API key not configured" | |
| cp ./Release/unsigned_files.zip ./Release/signed_files.zip | |
| fi | |
| - name: unzip dlls | |
| continue-on-error: true | |
| shell: pwsh | |
| run: | | |
| Expand-Archive -Path ./Release/signed_files.zip -DestinationPath ./Release/ -Force | |
| Remove-Item ./Release/unsigned_files.zip | |
| Remove-Item ./Release/signed_files.zip | |
| - name: Create custom.txt file | |
| shell: bash | |
| run: | | |
| echo -n "${{ inputs.custom }}" | cat > ./Release/custom.txt | |
| - name: Build self-extracted executable | |
| shell: bash | |
| run: | | |
| mv "./Release/rustdesk.exe" "./Release/${{ inputs.appname }}.exe" || echo "rustdesk.exe" | |
| sed -i '/dpiAware/d' res/manifest.xml | |
| pushd ./libs/portable | |
| pip3 install -r requirements.txt | |
| python3 ./generate.py -f ../../Release/ -o . -e "../../Release/${{ inputs.appname }}.exe" | |
| popd | |
| mkdir -p ./SignOutput | |
| mv ./target/release/rustdesk-portable-packer.exe "./SignOutput/rustdesk.exe" | |
| - 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": "${{ inputs.uuid }}", "status": "85% complete"}' | |
| - name: zip exe | |
| continue-on-error: true | |
| shell: pwsh | |
| run: | | |
| Compress-Archive -Path ./SignOutput/*.exe -DestinationPath ./SignOutput/unsigned_files.zip -CompressionLevel Fastest | |
| - name: sign exe | |
| 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 | |
| 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/${{ inputs.filename }}.exe" || echo "rustdesk" | |
| - name: send file to rdgen server | |
| if: ${{ fromJson(inputs.extras).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 | |
| - name: send file to api server | |
| if: ${{ fromJson(inputs.extras).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 | |
| - name: Report Status | |
| uses: fjogeleit/http-request-action@v1 | |
| with: | |
| url: ${{ env.STATUS_URL }} | |
| method: 'POST' | |
| customHeaders: '{"Content-Type": "application/json"}' | |
| data: '{"uuid": "${{ inputs.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": "${{ inputs.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": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}' |