From 63f8177132c90933ab947e67aaf55592f4fb0668 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 5 Aug 2026 20:40:15 +0800 Subject: [PATCH] ci: publish msi templates for custom client builds Custom clients rebuild the msi through WiX for every customer, though the package only differs by the app name, a few GUIDs and four files. Build the msi once more per release with a __RDAPPNAME__ placeholder and ship it unsigned in the unsigned tarball, so a customer's build can patch it rather than run msbuild. It stays unsigned because patching would invalidate a signature anyway. Doing this in CI is what makes ARM custom clients possible: preprocess.py runs the packaged exe to read its version and build date, so an arm64 msi can only be produced on a native arm64 machine, which the runner already is and the build agents are not. Patching runs no exe, so an x64 agent can then patch the arm64 template. preprocess.py rewrites res/msi in place and locates the app as .exe inside the dist, so the tree is reset around the second build and the dist copy is renamed to match. Sciter x86 ships no msi and is untouched. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Q7fBdTwziR5BHTkSz7Tzcm --- .github/workflows/flutter-build.yml | 51 ++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 418338dfe..da03231a0 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -387,6 +387,42 @@ jobs: mv $msi.FullName ../../SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.msi sha256sum ../../SignOutput/rustdesk-*.msi + - name: Build msi template for custom clients + # Custom client builds patch this template instead of running WiX per customer, + # so it is built once here with a placeholder app name, and left unsigned since + # the per-customer patch would invalidate a signature anyway. + # + # Two things this works around: preprocess.py rewrites res/msi in place, so the + # tree is reset around this second variant; and it locates the app as + # .exe inside the dist, so the dist copy is renamed to match. + # + # Building the arm64 template on the native arm64 runner is what makes ARM + # custom clients possible at all: the build agents are x64 and cannot run + # preprocess.py against an ARM exe. + if: env.UPLOAD_ARTIFACT == 'true' + run: | + git checkout -- res/msi + cp -r ./rustdesk ./rustdesk-msi-template + mv ./rustdesk-msi-template/rustdesk.exe ./rustdesk-msi-template/__RDAPPNAME__.exe + pushd ./res/msi + python preprocess.py --arp -d ../../rustdesk-msi-template --app-name __RDAPPNAME__ + $msiPlatform = if ('${{ matrix.job.arch }}' -eq 'aarch64') { 'ARM64' } else { 'x64' } + msbuild msi.sln -t:clean -p:Configuration=Release -p:Platform=$msiPlatform + msbuild msi.sln -p:Configuration=Release -p:Platform=$msiPlatform /p:TargetVersion=Windows10 + $msi = Get-ChildItem ./Package/bin/*/Release/en-us/Package.msi | Select-Object -First 1 + popd + mkdir ./msi-template + mv $msi.FullName ./msi-template/rustdesk-template-${{ matrix.job.arch }}.msi + git checkout -- res/msi + rm -r -fo ./rustdesk-msi-template + + - name: Upload unsigned msi template + if: env.UPLOAD_ARTIFACT == 'true' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: rustdesk-unsigned-msi-template-${{ matrix.job.arch }} + path: ./msi-template + - name: Sign rustdesk self-extracted file if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '-2' shell: bash @@ -931,9 +967,22 @@ jobs: name: rustdesk-unsigned-windows-x86 path: ./windows-x86/ + # Custom client builds patch these instead of running WiX per customer. + - name: Download Artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: rustdesk-unsigned-msi-template-x86_64 + path: ./msi-template/ + + - name: Download Artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: rustdesk-unsigned-msi-template-aarch64 + path: ./msi-template/ + - name: Combine unsigned app run: | - tar czf rustdesk-${{ env.VERSION }}-unsigned.tar.gz *.dmg windows-x86_64 windows-aarch64 windows-x86 + tar czf rustdesk-${{ env.VERSION }}-unsigned.tar.gz *.dmg windows-x86_64 windows-aarch64 windows-x86 msi-template - name: Publish unsigned app uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1