Initial commit: rdgen with GitHub/Gitea dual build backend

This commit is contained in:
naeeo
2026-09-29 19:56:02 +08:00
commit a3686a49d4
103 changed files with 20007 additions and 0 deletions
+134
View File
@@ -0,0 +1,134 @@
# Gitea Actions 适配副本:由 .github/workflows 同名文件适配(本地路径/cache/artifact 差异),修改时请同步两边。差异说明见 setup.md「Gitea 部署附录」。
# This yaml shares the build bridge steps with ci and nightly.
name: Build flutter-rust-bridge
# 2023-11-23 18:00:00+00:00
on:
workflow_call:
inputs:
version:
description: 'Rustdesk Version'
required: true
default: '1.3.1'
type: string
env:
CARGO_EXPAND_VERSION: "1.0.95"
FLUTTER_VERSION: "3.22.3"
FLUTTER_RUST_BRIDGE_VERSION: "1.80.1"
RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503
jobs:
generate_bridge:
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- {
target: x86_64-unknown-linux-gnu,
os: ubuntu-24.04,
extra-build-args: "",
}
steps:
- name: Checkout source code
if: ${{ inputs.version != 'master' }}
uses: actions/checkout@v4
with:
repository: rustdesk/rustdesk
ref: refs/tags/${{ inputs.version }}
submodules: recursive
- name: Checkout source code
if: ${{ inputs.version == 'master' }}
uses: actions/checkout@v4
with:
repository: rustdesk/rustdesk
submodules: recursive
- name: Install prerequisites
run: |
sudo apt-get install ca-certificates -y
sudo apt-get update -y
sudo apt-get install -y \
clang \
cmake \
curl \
gcc \
git \
g++ \
libclang-dev \
libgtk-3-dev \
llvm-dev \
nasm \
ninja-build \
pkg-config \
wget
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
targets: ${{ matrix.job.target }}
components: "rustfmt"
- uses: Swatinem/rust-cache@v2
with:
prefix-key: bridge-${{ matrix.job.os }}
- name: Cache cargo git
uses: actions/cache@v4
with:
path: ~/.cargo/git
key: bridge-cargo-git-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
bridge-cargo-git-
- name: Cache Bridge
id: cache-bridge
uses: actions/cache@v4
with:
path: /tmp/flutter_rust_bridge
key: vcpkg-${{ matrix.job.arch }}
- name: Install flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install flutter rust bridge deps
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
retry_wait_seconds: 15
shell: bash
command: |
cargo install cargo-expand --version ${{ env.CARGO_EXPAND_VERSION }} --locked
cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" --locked
pushd flutter && sed -i -e 's/extended_text: 14.0.0/extended_text: 13.0.0/g' pubspec.yaml && flutter pub get && popd
- name: Run flutter rust bridge
uses: nick-fields/retry@v3
with:
timeout_minutes: 10
max_attempts: 3
retry_wait_seconds: 15
shell: bash
command: |
~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart --c-output ./flutter/macos/Runner/bridge_generated.h
cp ./flutter/macos/Runner/bridge_generated.h ./flutter/ios/Runner/bridge_generated.h
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: bridge-artifact
path: |
./src/bridge_generated.rs
./src/bridge_generated.io.rs
./flutter/lib/generated_bridge.dart
./flutter/lib/generated_bridge.freezed.dart
./flutter/macos/Runner/bridge_generated.h
./flutter/ios/Runner/bridge_generated.h
@@ -0,0 +1,48 @@
# Gitea Actions 适配副本:由 .github/workflows 同名文件适配(本地路径/cache/artifact 差异),修改时请同步两边。差异说明见 setup.md「Gitea 部署附录」。
name: Fetch Encrypted Secrets
on:
workflow_call:
inputs:
zip_url_json:
required: true
type: string
jobs:
download-zip:
runs-on: ubuntu-latest
steps:
- name: Download with Retry
uses: nick-fields/retry@v3
with:
timeout_minutes: 5
max_attempts: 3
retry_wait_seconds: 15
shell: python
command: |
import requests
import json
import time
input_data = json.loads('${{ inputs.zip_url_json }}')
url = f"{input_data['url']}/get_zip?filename={input_data['file']}"
for attempt in range(5):
try:
print(f"Downloading (Attempt {attempt + 1})...")
r = requests.get(url, timeout=20)
r.raise_for_status()
with open('secrets.zip', 'wb') as f:
f.write(r.content)
break
except Exception as e:
if attempt < 4:
time.sleep(5 * (2 ** attempt))
else: raise e
- name: Upload Encrypted Artifact
uses: actions/upload-artifact@v4
with:
name: encrypted-secrets-zip
path: secrets.zip
retention-days: 1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+775
View File
@@ -0,0 +1,775 @@
# Gitea Actions 适配副本:由 .github/workflows 同名文件适配(本地路径/cache/artifact 差异),修改时请同步两边。差异说明见 setup.md「Gitea 部署附录」。
name: Custom macOS Client Generator
run-name: Custom macOS 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
MAC_RUST_VERSION: "1.81"
CARGO_NDK_VERSION: "3.1.2"
SCITER_ARMV7_CMAKE_VERSION: "3.29.7"
SCITER_NASM_DEBVERSION: "2.14-1"
LLVM_VERSION: "15.0.6"
FLUTTER_VERSION: "3.24.5"
ANDROID_FLUTTER_VERSION: "3.24.5" # >= 3.16 is very slow on my android phone, but work well on most of others. We may switch to new flutter after changing to texture rendering (I believe it can solve my problem).
FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # 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;files,/Users/runner/vcpkg-cache,readwrite"
# vcpkg version: 2024.07.12
VCPKG_COMMIT_ID: "${{ inputs.version == 'master' && '9e593bb18ea69cc5095e012465dcd675a822ed0d' || '120deac3062162151622ca4860575a33844ba10b' }}"
ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836"
VERSION: "${{ inputs.version }}"
NDK_VERSION: "r28c"
#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 }}"
jobs:
setup:
uses: ./.gitea/workflows/fetch-encrypted-secrets.yml
with:
zip_url_json: ${{ inputs.zip_url }}
generate-bridge:
uses: ./.gitea/workflows/bridge.yml
with:
version: ${{ inputs.version }}
build-for-macos:
name: ${{ matrix.job.target }}
runs-on: ${{ matrix.job.os }}
needs: [generate-bridge, setup]
strategy:
fail-fast: false
matrix:
job:
- {
target: x86_64-apple-darwin,
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,
}
- {
target: aarch64-apple-darwin,
os: macos-14,
# extra-build-args: "--disable-flutter-texture-render", # disable this for mac, because we see a lot of users reporting flickering both on arm and x64, and we can not confirm if texture rendering has better performance if htere is no vram, https://github.com/rustdesk/rustdesk/issues/6296
extra-build-args: "--screencapturekit",
arch: aarch64,
vcpkg-triplet: arm64-osx,
}
env:
STATUS_URL: "${{ secrets.GENURL }}/updategh"
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
path: .rdgen-src
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.gitea/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: 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 and potrace and nasm and and
shell: bash
run: |
brew install imagemagick potrace nasm cmake gcc wget ninja
echo "$(brew --prefix imagemagick)/bin" >> $GITHUB_PATH
- name: Update macOS Info.plist and settings
continue-on-error: false
shell: bash
run: |
# MACSTUFF Update Info.plist
sed -i '' -e 's|<key>CFBundleName</key>.*<string>.*</string>|<key>CFBundleName</key>\n\t<string>${{ env.appname }}</string>|' ./flutter/macos/Runner/Info.plist
sed -i '' -e 's|<key>CFBundleDisplayName</key>.*<string>.*</string>|<key>CFBundleDisplayName</key>\n\t<string>${{ env.appname }}</string>|' ./flutter/macos/Runner/Info.plist
sed -i '' -e 's|<key>CFBundleIdentifier</key>.*<string>.*</string>|<key>CFBundleIdentifier</key>\n\t<string>com.${{ env.appname }}.app</string>|' ./flutter/macos/Runner/Info.plist
# MACSTUFF Update AppInfo.xcconfig
sed -i '' -e 's|PRODUCT_NAME = .*|PRODUCT_NAME = ${{ env.appname }}|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig
sed -i '' -e 's|PRODUCT_BUNDLE_IDENTIFIER = .*|PRODUCT_BUNDLE_IDENTIFIER = com.${{ env.appname }}.app|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig
sed -i '' -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig
sed -i '' -e 's|Purslane Ltd.|${{ env.compname }}|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./Cargo.toml
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./libs/portable/Cargo.toml
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./src/main.rs
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 }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./src/main.rs
# Update Xcode project settings
sed -i '' -e 's/PRODUCT_NAME = "RustDesk"/PRODUCT_NAME = "${{ env.appname }}"/' ./flutter/macos/Runner.xcodeproj/project.pbxproj
sed -i '' -e 's/PRODUCT_BUNDLE_IDENTIFIER = ".*"/PRODUCT_BUNDLE_IDENTIFIER = "com.${{ env.appname }}.app"/' ./flutter/macos/Runner.xcodeproj/project.pbxproj
# Update CMake settings
if [ -f "./flutter/macos/CMakeLists.txt" ]; then
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 '"'"'Runner'"'"' do/' ./flutter/macos/Podfile
find ./src/lang -name "*.rs" -exec sed -i '' -e 's|RustDesk|${{ env.appname }}|' {} \;
sed -i '' -e 's|RustDesk|${{ env.appname }}|' ./src/lang/nl.rs
sed -i '' -e 's|https://rustdesk.com|${{ env.urlLink }}|' ./build.py
sed -i '' -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ env.urlLink }}'));|" ./flutter/lib/common.dart
sed -i '' -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ env.urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart
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: 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
sed -i -e 's|&download_url|"${{ env.downloadLink }}"|' ./src/updater.rs
sed -i -e 's|$downloadUrl/$downloadFile|${{ env.downloadLink }}|' ./flutter/lib/desktop/widgets/update_progress.dart
# Update slogan - About in en.rs
sed -i '' -e 's/("Slogan_tip", "Made with heart in this chaotic world!")/("Slogan_tip", "Powered by ${{ env.appname }}")/' ./src/lang/en.rs
sed -i '' -e 's/("About RustDesk", "")/("About RustDesk", "About ${{ env.appname }}")/' ./src/lang/en.rs
# Update slogan - About in nl.rs
sed -i '' -e 's/("Slogan_tip", "Ontwikkeld met het hart voor deze chaotische wereld!")/("Slogan_tip", "Powered by ${{ env.appname }}")/' ./src/lang/nl.rs
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
# Update pubspec.yaml with proper YAML formatting
cp ./flutter/pubspec.yaml ./flutter/pubspec.yaml.bak
echo " archive: ^3.6.1" > ./flutter/temp_dependency.txt
awk '/intl:/{print;system("cat ./flutter/temp_dependency.txt");next}1' ./flutter/pubspec.yaml > ./flutter/pubspec.yaml.tmp
mv ./flutter/pubspec.yaml.tmp ./flutter/pubspec.yaml
rm ./flutter/temp_dependency.txt
- name: set server, serverPort, key, and apiserver
continue-on-error: true
shell: bash
env:
SERVER_DOMAIN: ${{ env.server }}
SERVER_PORT_INPUT: ${{ env.serverPort }}
SERVER_KEY: ${{ env.key }}
API_SERVER: ${{ env.apiServer }}
run: |
# Pass secrets via bash env vars to avoid quoting issues
if [ ! -f "./libs/hbb_common/src/config.rs" ]; then
echo "Error: config.rs not found!"
exit 1
fi
if [ ! -f "./src/common.rs" ]; then
echo "Error: common.rs not found!"
exit 1
fi
# Port must be a number; views.py defaults it to 21116
if ! [[ "$SERVER_PORT_INPUT" =~ ^[0-9]+$ ]]; then
echo "Error: serverPort must be a number, got: '$SERVER_PORT_INPUT'"
exit 1
fi
# Derive the port block from the rendezvous port
# (defaults 21116/21117/21118/21119)
SERVER_PORT=$SERVER_PORT_INPUT
RELAY_PORT=$((SERVER_PORT + 1))
WS_RENDEZVOUS_PORT=$((RELAY_PORT + 1))
WS_RELAY_PORT=$((WS_RENDEZVOUS_PORT + 1))
echo "Setting server: $SERVER_DOMAIN"
echo "Setting ports: $SERVER_PORT, $RELAY_PORT, $WS_RENDEZVOUS_PORT, $WS_RELAY_PORT"
sed -i -e "s|rs-ny.rustdesk.com|$SERVER_DOMAIN|" ./libs/hbb_common/src/config.rs
# Match on numeric value so the step is independent of defaults
sed -i -e "s|pub const RENDEZVOUS_PORT: i32 = [0-9][0-9]*;|pub const RENDEZVOUS_PORT: i32 = $SERVER_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|pub const RELAY_PORT: i32 = [0-9][0-9]*;|pub const RELAY_PORT: i32 = $RELAY_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|pub const WS_RENDEZVOUS_PORT: i32 = [0-9][0-9]*;|pub const WS_RENDEZVOUS_PORT: i32 = $WS_RENDEZVOUS_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|pub const WS_RELAY_PORT: i32 = [0-9][0-9]*;|pub const WS_RELAY_PORT: i32 = $WS_RELAY_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|$SERVER_KEY|" ./libs/hbb_common/src/config.rs
sed -i -e "s|https://admin.rustdesk.com|$API_SERVER|" ./src/common.rs
echo "=== Verification ==="
grep -nE "RENDEZVOUS_PORT|RELAY_PORT|WS_" ./libs/hbb_common/src/config.rs
- name: allow custom.txt
uses: nick-fields/retry@v3
with:
timeout_minutes: 5
max_attempts: 3
retry_wait_seconds: 15
shell: bash
command: |
cp .rdgen-src/.github/patches/allowCustom.py .
python allowCustom.py
cp .rdgen-src/.github/patches/removeSetupServerTip.diff .
git apply removeSetupServerTip.diff
# sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml
- name: Install build runtime
run: |
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"
else
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:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
- name: Patch flutter
continue-on-error: true
run: |
cd $(dirname $(dirname $(which flutter)))
[[ "3.24.5" == ${{env.FLUTTER_VERSION}} ]] && git apply ${{ github.workspace }}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff
- name: Workaround for flutter issue
shell: bash
run: |
cd "$(dirname "$(which flutter)")"
# https://github.com/flutter/flutter/issues/133533
sed -i -e 's/_setFramesEnabledState(false);/\/\/_setFramesEnabledState(false);/g' ../packages/flutter/lib/src/scheduler/binding.dart
grep -n '_setFramesEnabledState(false);' ../packages/flutter/lib/src/scheduler/binding.dart
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: ${{ env.MAC_RUST_VERSION }}
targets: ${{ matrix.job.target }}
components: "rustfmt"
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.job.os }}
- name: Magick stuff for macOS
if: ${{ env.iconlink_url != 'false' }}
continue-on-error: false
shell: bash
run: |
# Create all necessary directories first
mkdir -p ./res
mkdir -p ./flutter/assets
mkdir -p ./flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset
mkdir -p ./macos/Runner/Assets.xcassets/AppIcon.appiconset
mkdir -p ./rustdesk/data/flutter_assets/assets
# Download icon using curl with additional SSL options
curl -k -L --tlsv1.2 --proto =https --ssl-reqd \
-H "User-Agent: Mozilla/5.0" \
"${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}&uuid=${{ env.iconlink_uuid }}" \
-o ./res/icon.png || wget --no-check-certificate -O ./res/icon.png "${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}&uuid=${{ env.iconlink_uuid }}"
# Backup existing files (if they exist)
[ -f "./res/32x32.png" ] && mv ./res/32x32.png ./res/32x32.png.bak
[ -f "./res/64x64.png" ] && mv ./res/64x64.png ./res/64x64.png.bak
[ -f "./res/128x128.png" ] && mv ./res/128x128.png ./res/128x128.png.bak
[ -f "./res/mac-icon.png" ] && mv ./res/mac-icon.png ./res/mac-icon.png.bak
[ -f "./flutter/assets/icon.png" ] && mv ./flutter/assets/icon.png ./flutter/assets/icon.png.bak
[ -f "./flutter/assets/icon.svg" ] && mv ./flutter/assets/icon.svg ./flutter/assets/icon.svg.bak
[ -f "./rustdesk/data/flutter_assets/assets/icon.svg" ] && mv ./rustdesk/data/flutter_assets/assets/icon.svg ./rustdesk/data/flutter_assets/assets/icon.svg.bak
# Create standard app icons
magick ./res/icon.png -resize 32x32 ./res/32x32.png
magick ./res/icon.png -resize 64x64 ./res/64x64.png
magick ./res/icon.png -resize 128x128 ./res/128x128.png
# Copy icon to Flutter assets
cp ./res/icon.png ./flutter/assets/icon.png
cp ./res/icon.png ./rustdesk/data/flutter_assets/assets/icon.png
# Convert PNG to SVG using potrace
magick ./res/icon.png -flatten ./temp_icon.pbm
potrace --svg -o ./flutter/assets/icon.svg ./temp_icon.pbm
cp ./flutter/assets/icon.svg ./rustdesk/data/flutter_assets/assets/icon.svg
rm ./temp_icon.pbm
# Create macOS app icons
magick ./res/icon.png -resize 16x16 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png"
magick ./res/icon.png -resize 32x32 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png"
magick ./res/icon.png -resize 64x64 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png"
magick ./res/icon.png -resize 128x128 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png"
magick ./res/icon.png -resize 256x256 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png"
magick ./res/icon.png -resize 512x512 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png"
magick ./res/icon.png -resize 1024x1024 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png"
# Create macOS specific icons
magick ./res/icon.png -resize 128x128 ./res/mac-icon.png
# Create dark mode tray icon (optimized for macOS menu bar)
magick ./res/icon.png -resize 22x22 -colorspace gray -alpha set -background none -channel A -evaluate set 100% ./res/mac-tray-dark-x2.png
# Create light mode tray icon (optimized for macOS menu bar)
magick ./res/icon.png -resize 22x22 -negate -colorspace gray -alpha set -background none -channel A -evaluate set 100% ./res/mac-tray-light-x2.png
# Create AppIcon.icns (macOS native icon format)
mkdir -p ./iconset.iconset
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png" "./iconset.iconset/icon_16x16.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png" "./iconset.iconset/icon_16x16@2x.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png" "./iconset.iconset/icon_32x32.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png" "./iconset.iconset/icon_32x32@2x.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png" "./iconset.iconset/icon_128x128.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png" "./iconset.iconset/icon_128x128@2x.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png" "./iconset.iconset/icon_256x256.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png" "./iconset.iconset/icon_256x256@2x.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png" "./iconset.iconset/icon_512x512.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png" "./iconset.iconset/icon_512x512@2x.png"
iconutil -c icns ./iconset.iconset -o ./flutter/macos/Runner/AppIcon.icns
rm -rf ./iconset.iconset
# Create Contents.json for macOS app icon
echo '{
"images": [
{"size":"16x16","idiom":"mac","filename":"app_icon_16.png","scale":"1x"},
{"size":"16x16","idiom":"mac","filename":"app_icon_32.png","scale":"2x"},
{"size":"32x32","idiom":"mac","filename":"app_icon_32.png","scale":"1x"},
{"size":"32x32","idiom":"mac","filename":"app_icon_64.png","scale":"2x"},
{"size":"128x128","idiom":"mac","filename":"app_icon_128.png","scale":"1x"},
{"size":"128x128","idiom":"mac","filename":"app_icon_256.png","scale":"2x"},
{"size":"256x256","idiom":"mac","filename":"app_icon_256.png","scale":"1x"},
{"size":"256x256","idiom":"mac","filename":"app_icon_512.png","scale":"2x"},
{"size":"512x512","idiom":"mac","filename":"app_icon_512.png","scale":"1x"},
{"size":"512x512","idiom":"mac","filename":"app_icon_1024.png","scale":"2x"}
],
"info": {
"version": 1,
"author": "xcode"
}
}' > "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json"
# Copy icons and Contents.json to both locations
cp -r flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/* macos/Runner/Assets.xcassets/AppIcon.appiconset/
# Verify files exist and show their sizes
echo "Verifying generated files:"
ls -lh ./res/mac-tray-dark-x2.png
ls -lh ./res/mac-tray-light-x2.png
ls -lh ./res/mac-icon.png
echo "Flutter macOS app icons:"
ls -lh flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/
echo "Flutter assets:"
ls -lh flutter/assets/icon.*
echo "RustDesk Flutter assets:"
ls -lh rustdesk/data/flutter_assets/assets/
- name: replace flutter icons
if: ${{ env.iconlink_url != 'false' }}
continue-on-error: false
shell: bash
run: |
cd ./flutter
# Create required directories and files
mkdir -p web
mkdir -p assets
echo '{"name":"${{ env.appname }}","short_name":"${{ env.appname }}","start_url":"/","display":"standalone","background_color":"#ffffff","theme_color":"#ffffff","description":"A remote desktop software."}' > web/manifest.json
echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>${{ env.appname }}</title></head><body></body></html>' > web/index.html
# Ensure the AppIcon.appiconset directory exists
mkdir -p macos/Runner/Assets.xcassets/AppIcon.appiconset
# Copy the processed icons to Flutter locations
cp ../res/mac-icon.png ./assets/icon.png
cp ../flutter/assets/icon.svg ./assets/icon.svg || true
flutter pub upgrade win32
flutter pub get
flutter pub run flutter_launcher_icons
cd ..
- name: ui.rs
if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true
shell: bash
run: |
cp ./src/ui.rs ./src/ui.rs.bak
if [ -f "./res/icon.png" ]; then
SEARCH_STR="iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAEiuAABIrgHwmhA7AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAEx9JREFUeJztnXmYHMV5h9"
b64=$(base64 < ./res/icon.png)
sed -i '' -e "s~$SEARCH_STR.*\"~$b64\"~" ./src/ui.rs
fi
- name: fix connection delay
continue-on-error: false
if: ${{ env.delayFix == 'true' }}
shell: bash
run: |
# Precise fix: only extend the direct-connection condition with the
# key check, instead of globally replacing !key.is_empty() with
# false (which would also disable TCP encryption and UDP logic).
sed -i -e 's#if is_local || peer_nat_type == NatType::SYMMETRIC {#if is_local || peer_nat_type == NatType::SYMMETRIC || !key.is_empty() {#' ./src/client.rs
grep -n "key.is_empty()" ./src/client.rs || true
- name: use X for offline display instead of orange circle
continue-on-error: true
if: env.xOffline == 'true'
run: |
cp .rdgen-src/.github/patches/xoffline.diff .
git apply xoffline.diff
- name: hide-cm
continue-on-error: true
if: env.hidecm == 'true'
run: |
cp .rdgen-src/.github/patches/hidecm.diff .
git apply hidecm.diff
- name: removeNewVersionNotif
continue-on-error: 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
- name: Restore bridge files
uses: actions/download-artifact@v4
with:
name: bridge-artifact
path: ./
- name: Setup vcpkg with Github Actions binary cache
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
doNotCache: false
- name: Install vcpkg dependencies
run: |
if ! $VCPKG_ROOT/vcpkg \
install \
--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
- name: Create MacOS directory structure
run: |
mkdir -p ./build/macos/Build/Products/Release/RustDesk.app/Contents/MacOS
- name: Build rustdesk
run: |
if [ "${{ matrix.job.target }}" = "aarch64-apple-darwin" ]; then
MIN_MACOS_VERSION="12.3"
sed -i -e "s/MACOSX_DEPLOYMENT_TARGET\=[0-9]*.[0-9]*/MACOSX_DEPLOYMENT_TARGET=${MIN_MACOS_VERSION}/" build.py
sed -i -e "s/platform :osx, '.*'/platform :osx, '${MIN_MACOS_VERSION}'/" flutter/macos/Podfile
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/\"${{ env.appname }}.app\"/" build.py
./build.py --flutter --hwcodec --unix-file-copy-paste ${{ matrix.job.extra-build-args }}
# - name: Copy service file
# run: |
# cp -rf ../target/release/service ./build/macos/Build/Products/Release/RustDesk.app/Contents/MacOS/
- name: Embed custom config into the .app bundle
shell: bash
run: |
APP=$(find ./flutter/build/macos/Build/Products/Release -maxdepth 1 -name "*.app" | head -n 1)
echo "App bundle: $APP"
echo -n '${{ env.custom }}' > "$APP/Contents/MacOS/custom_.txt"
ls -l "$APP/Contents/MacOS/custom_.txt"
- name: Install rcodesign tool
if: env.MACOS_P12_BASE64 != null
shell: bash
run: |
pushd /tmp
wget https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz
tar -zxvf apple-codesign-0.22.0-macos-universal.tar.gz
mv apple-codesign-0.22.0-macos-universal/rcodesign /usr/local/bin
popd
- name: Install build runtime
run: |
brew install llvm create-dmg nasm cmake gcc wget ninja
# 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"
else
brew install pkg-config
fi
- name: Show version information (Rust, cargo, Clang)
shell: bash
run: |
clang --version || true
rustup -V
rustup toolchain list
rustup default
cargo -V
rustc -V
- name: icon svg handling
if: ${{ env.iconlink_url != 'false' }}
continue-on-error: false
shell: bash
run: |
ASSETS_DIR="build/macos/Build/Products/Release/RustDesk.app/Contents/Frameworks/App.framework/Versions/Current/Resources/flutter_assets/assets"
mkdir -p "$ASSETS_DIR"
if [ -f "$ASSETS_DIR/icon.svg" ]; then
mv "$ASSETS_DIR/icon.svg" "$ASSETS_DIR/icon.svg.bak"
fi
# First convert PNG to PBM (bitmap)
magick convert ./res/icon.png ./temp_icon.pbm
# Then use potrace to convert to SVG
potrace --svg -o "$ASSETS_DIR/icon.svg" ./temp_icon.pbm
rm ./temp_icon.pbm
- name: logo handling
if: ${{ env.logolink_url != 'false' }}
continue-on-error: false
shell: bash
run: |
ASSETS_DIR="build/macos/Build/Products/Release/RustDesk.app/Contents/Frameworks/App.framework/Versions/Current/Resources/flutter_assets/assets"
mkdir -p "$ASSETS_DIR"
curl -k -L --tlsv1.2 --proto =https --ssl-reqd \
-H "User-Agent: Mozilla/5.0" \
"${{ 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" \
"${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}&uuid=${{ env.logolink_uuid }}"
- name: Sign macOS app bundle
if: env.MACOS_P12_BASE64 != ''
run: |
cd flutter/build/macos/Build/Products/Release
# Debug info
echo "Current directory contents:"
ls -la
# Rename RustDesk.app to the custom app name first
if [ -d "RustDesk.app" ]; then
# First rename the app if it's still called RustDesk.app
mv "RustDesk.app" "${{ env.appname }}.app"
echo "Renamed RustDesk.app to ${{ env.appname }}.app"
fi
echo "App bundle contents after rename:"
ls -la "${{ env.appname }}.app" || echo "App not found"
ls -la "${{ env.appname }}.app/Contents" || echo "Contents not found"
# Decode the certificate
echo "${{ secrets.MACOS_P12_BASE64 }}" | base64 --decode > certificate.p12
# Sign the app bundle and its contents
if [ -d "${{ env.appname }}.app/Contents/MacOS" ]; then
echo "Signing main executable..."
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"
else
echo "Main executable not found at expected path: $MAIN_EXECUTABLE"
# Try to find the actual executable
echo "Available executables in MacOS directory:"
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 "${{ env.appname }}.app/Contents/MacOS/$ACTUAL_EXECUTABLE"
fi
fi
echo "Signing frameworks..."
find "${{ env.appname }}.app/Contents/Frameworks" -type f -not -name ".*" -exec \
rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \
--code-signature-flags runtime {} \;
echo "Signing main bundle..."
rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \
--code-signature-flags runtime "${{ env.appname }}.app"
else
echo "Error: Invalid app bundle structure"
exit 1
fi
# Clean up
rm certificate.p12
- name: Ad-hoc Sign macOS app bundle (Fallback)
if: env.MACOS_P12_BASE64 == '' || env.MACOS_P12_BASE64 == null
shell: bash
run: |
cd flutter/build/macos/Build/Products/Release
# Renombrar RustDesk.app al nombre de la marca blanca si aplica
if [ -d "RustDesk.app" ]; then
mv "RustDesk.app" "${{ env.appname }}.app"
echo "RustDesk.app renombrado a ${{ env.appname }}.app"
fi
TARGET_APP="${{ env.appname }}.app"
if [ -d "$TARGET_APP" ]; then
echo "Limpiando atributos extendidos..."
xattr -cr "$TARGET_APP" || true
echo "Re-firmando $TARGET_APP de forma Ad-Hoc y recursiva..."
codesign --force --deep --sign - "$TARGET_APP"
echo "Firma Ad-Hoc completada con exito."
else
echo "Error: No se encontro la app bundle en $TARGET_APP para firmar."
exit 1
fi
- name: Create DMG
run: |
cd /Users/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/flutter/build/macos/Build/Products/Release
# Print directory contents for debugging
echo "Directory contents:"
ls -la
# Find the actual .app bundle
if [ -d "RustDesk.app" ]; then
# First rename the app if it's still called RustDesk.app
mv "RustDesk.app" "${{ env.appname }}.app"
fi
if [ ! -d "${{ env.appname }}.app" ]; then
echo "Could not find .app bundle!"
exit 1
fi
echo "Creating DMG for ${{ env.appname }}.app"
create-dmg \
--volname "${{ env.appname }}" \
--window-pos 200 120 \
--window-size 800 400 \
--icon-size 100 \
--icon "${{ env.appname }}.app" 200 190 \
--hide-extension "${{ env.appname }}.app" \
--app-drop-link 600 185 \
"${{ env.appname }}-${{ matrix.job.arch }}.dmg" \
"${{ env.appname }}.app"
mv "${{ env.appname }}-${{ matrix.job.arch }}.dmg" $GITHUB_WORKSPACE/
- name: Rename rustdesk
if: env.UPLOAD_ARTIFACT == 'true'
run: |
cd $GITHUB_WORKSPACE
echo "Directory contents:"
ls -la
# Find the DMG file dynamically
DMG_FILE=$(find . -name "${{ env.appname }}-${{ matrix.job.arch }}.dmg")
if [ -n "$DMG_FILE" ]; then
echo "Found DMG file: $DMG_FILE"
mv "$DMG_FILE" "${{ env.filename }}-${{ matrix.job.arch }}.dmg"
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: ${{ env.rdgen == 'true' }}
shell: bash
run: |
curl -i -X POST \
-H "Content-Type: multipart/form-data" \
-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: ${{ env.rdgen == 'false' }}
shell: bash
run: |
curl -i -X POST \
-H "Content-Type: multipart/form-data" \
-H "Authorization: Bearer ${{ env.token }}" \
-F "file=@$GITHUB_WORKSPACE/${{ env.filename }}-${{ matrix.job.arch }}.dmg" \
"${{ env.apiServer }}/api/save_custom_client"
cleanup:
needs: [build-for-macos]
runs-on: ubuntu-latest
continue-on-error: true
if: always()
steps:
- name: Delete secrets artifact
uses: geekyeggo/delete-artifact@v4
with:
name: encrypted-secrets-zip
File diff suppressed because one or more lines are too long
+750
View File
@@ -0,0 +1,750 @@
# Gitea Actions 适配副本:由 .github/workflows 同名文件适配(本地路径/cache/artifact 差异),修改时请同步两边。差异说明见 setup.md「Gitea 部署附录」。
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;files,D:\vcpkg-cache,readwrite"
# vcpkg version: 2024.07.12
VCPKG_COMMIT_ID: "${{ inputs.version == 'master' && '9e593bb18ea69cc5095e012465dcd675a822ed0d' || '120deac3062162151622ca4860575a33844ba10b' }}"
ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836"
VERSION: "${{ inputs.version }}"
NDK_VERSION: "r28c"
#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: ./.gitea/workflows/fetch-encrypted-secrets.yml
with:
zip_url_json: ${{ inputs.zip_url }}
generate-bridge:
uses: ./.gitea/workflows/bridge.yml
with:
version: ${{ inputs.version }}
build-RustDeskTempTopMostWindow:
needs: setup
uses: ./.gitea/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: ${{ matrix.job.os }}
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
with:
path: .rdgen-src
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.gitea/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: 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: 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@v4
with:
name: bridge-artifact
path: ./
- 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: fix flutter_gpu_texture_renderer (fixed in master rustdesk)
shell: bash
run: |
sed -i -e 's|2ded7f146437a761ffe6981e2f742038f85ca68d|08a471bb8ceccdd50483c81cdfa8b81b07b14b87|' ./flutter/pubspec.lock
sed -i -e 's|2ded7f146437a761ffe6981e2f742038f85ca68d|08a471bb8ceccdd50483c81cdfa8b81b07b14b87|' ./flutter/pubspec.yaml
- name: change appname to custom
if: env.appname != 'rustdesk'
continue-on-error: true
shell: bash
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 Tech Pte. Ltd.|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./res/msi/preprocess.py
sed -i -e 's|r"Purslane(?: Tech Pte\\.)? Ltd"|"${{ env.compname }}"|' ./res/msi/preprocess.py
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./Cargo.toml
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./libs/portable/Cargo.toml
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./res/msi/Package/License.rtf
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./src/main.rs
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|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
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./src/main.rs
- 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
sed -i -e 's|&download_url|"${{ env.downloadLink }}"|' ./src/updater.rs
sed -i -e 's|$downloadUrl/$downloadFile|${{ env.downloadLink }}|' ./flutter/lib/desktop/widgets/update_progress.dart
- name: set server, serverPort, key, and apiserver
continue-on-error: true
shell: bash
env:
SERVER_DOMAIN: ${{ env.server }}
SERVER_PORT_INPUT: ${{ env.serverPort }}
SERVER_KEY: ${{ env.key }}
API_SERVER: ${{ env.apiServer }}
run: |
# Pass secrets via bash env vars to avoid quoting issues
if [ ! -f "./libs/hbb_common/src/config.rs" ]; then
echo "Error: config.rs not found!"
exit 1
fi
if [ ! -f "./src/common.rs" ]; then
echo "Error: common.rs not found!"
exit 1
fi
# Port must be a number; views.py defaults it to 21116
if ! [[ "$SERVER_PORT_INPUT" =~ ^[0-9]+$ ]]; then
echo "Error: serverPort must be a number, got: '$SERVER_PORT_INPUT'"
exit 1
fi
# Derive the port block from the rendezvous port
# (defaults 21116/21117/21118/21119)
SERVER_PORT=$SERVER_PORT_INPUT
RELAY_PORT=$((SERVER_PORT + 1))
WS_RENDEZVOUS_PORT=$((RELAY_PORT + 1))
WS_RELAY_PORT=$((WS_RENDEZVOUS_PORT + 1))
echo "Setting server: $SERVER_DOMAIN"
echo "Setting ports: $SERVER_PORT, $RELAY_PORT, $WS_RENDEZVOUS_PORT, $WS_RELAY_PORT"
sed -i -e "s|rs-ny.rustdesk.com|$SERVER_DOMAIN|" ./libs/hbb_common/src/config.rs
# Match on numeric value so the step is independent of defaults
sed -i -e "s|pub const RENDEZVOUS_PORT: i32 = [0-9][0-9]*;|pub const RENDEZVOUS_PORT: i32 = $SERVER_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|pub const RELAY_PORT: i32 = [0-9][0-9]*;|pub const RELAY_PORT: i32 = $RELAY_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|pub const WS_RENDEZVOUS_PORT: i32 = [0-9][0-9]*;|pub const WS_RENDEZVOUS_PORT: i32 = $WS_RENDEZVOUS_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|pub const WS_RELAY_PORT: i32 = [0-9][0-9]*;|pub const WS_RELAY_PORT: i32 = $WS_RELAY_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|$SERVER_KEY|" ./libs/hbb_common/src/config.rs
sed -i -e "s|https://admin.rustdesk.com|$API_SERVER|" ./src/common.rs
echo "=== Verification ==="
grep -nE "RENDEZVOUS_PORT|RELAY_PORT|WS_" ./libs/hbb_common/src/config.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: 5
max_attempts: 3
retry_wait_seconds: 15
shell: pwsh
command: |
Copy-Item .rdgen-src/.github/patches/allowCustom.py allowCustom.py
python allowCustom.py
# Remove Setup Server Tip
Copy-Item .rdgen-src/.github/patches/removeSetupServerTip.diff removeSetupServerTip.diff
git apply removeSetupServerTip.diff
- name: Patch remote printer port to spool file
shell: python
run: |
import os, re
src = "."
app = os.environ.get("appname") or "RustDesk"
literal = "C:\\\\ProgramData\\\\%s\\\\printer-spool\\\\job.prn" % app
# atch Rust remote_printer lib.rs
rs = os.path.join(src, "libs", "remote_printer", "src", "lib.rs")
if os.path.isfile(rs):
with open(rs, "r", encoding="utf-8") as f:
t = f.read()
if "printer-spool" in t:
print("Rust printer port already points to spool file")
else:
old = ('fn get_port_name(app_name: &str) -> Vec<u16> {\n'
' format!("{} Printer", app_name)')
new = ('fn get_port_name(app_name: &str) -> Vec<u16> {\n'
' let base = std::env::var("ProgramData")\n'
' .unwrap_or_else(|_| "C:\\\\ProgramData".to_string());\n'
' let dir = format!("{}\\\\{}\\\\printer-spool", base, app_name);\n'
' let _ = std::fs::create_dir_all(&dir);\n'
' format!("{}\\\\job.prn", dir)')
if old in t:
with open(rs, "w", encoding="utf-8") as f:
f.write(t.replace(old, new, 1))
print("Successfully patched Rust printer port")
else:
print("Warning: get_port_name() signature not matched in Rust source")
# patch C++ MSI Custom Action
cpp = os.path.join(src, "res", "msi", "CustomActions", "RemotePrinter.cpp")
if os.path.isfile(cpp):
with open(cpp, "r", encoding="utf-8") as f:
t = f.read()
if "printer-spool" in t:
print("MSI printer port already points to spool file")
else:
pattern = r'(RD_PRINTER_PORT\s*=\s*)L"[^"]*"'
new_t, n = re.subn(pattern, lambda m: m.group(1) + 'L"%s"' % literal, t, count=1)
if n == 1:
with open(cpp, "w", encoding="utf-8") as f:
f.write(new_t)
print("Successfully patched MSI CustomAction printer port")
else:
print("Warning: RD_PRINTER_PORT not matched in RemotePrinter.cpp")
- name: Install LLVM and Clang
uses: KyleMayes/install-llvm-action@v1
with:
version: ${{ env.LLVM_VERSION }}
- name: Install flutter
uses: subosito/flutter-action@v2.12.0 #https://github.com/subosito/flutter-action/issues/277
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
# https://github.com/flutter/flutter/issues/155685
- name: Replace engine with rustdesk custom flutter engine
run: |
flutter doctor -v
flutter precache --windows
Invoke-WebRequest -Uri https://github.com/rustdesk/engine/releases/download/main/windows-x64-release.zip -OutFile windows-x64-release.zip
Expand-Archive -Path windows-x64-release.zip -DestinationPath windows-x64-release
mv -Force windows-x64-release/* C:/hostedtoolcache/windows/flutter/stable-${{ env.FLUTTER_VERSION }}-x64/bin/cache/artifacts/engine/windows-x64-release/
- name: Patch flutter
shell: bash
run: |
cp .github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff $(dirname $(dirname $(which flutter)))
cd $(dirname $(dirname $(which flutter)))
[[ "3.24.5" == ${{env.FLUTTER_VERSION}} ]] && git apply flutter_3.24.4_dropdown_menu_enableFilter.diff
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: ${{ env.SCITER_RUST_VERSION }}
targets: ${{ matrix.job.target }}
components: "rustfmt"
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.job.os }}
- 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: magick stuff
if: ${{ env.iconlink_url != 'false' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 5
max_attempts: 3
retry_wait_seconds: 15
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: |
# Precise fix: only extend the direct-connection condition with the
# key check, instead of globally replacing !key.is_empty() with
# false (which would also disable TCP encryption and UDP logic).
sed -i -e 's#if is_local || peer_nat_type == NatType::SYMMETRIC {#if is_local || peer_nat_type == NatType::SYMMETRIC || !key.is_empty() {#' ./src/client.rs
grep -n "key.is_empty()" ./src/client.rs || true
- name: use X for offline display instead of orange circle
if: env.xOffline == 'true'
uses: nick-fields/retry@v3
with:
timeout_minutes: 5
max_attempts: 3
retry_wait_seconds: 15
shell: pwsh
continue_on_error: true
command: |
Copy-Item .rdgen-src/.github/patches/xoffline.diff 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: run as admin
# continue-on-error: true
# if: ${{ env.runasadmin == 'true' }}
# shell: bash
# run: |
# sed -i '/<\/compatibility>/a \
# <security> \
# <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/> \
# </security>' ./flutter/windows/runner/runner.exe.manifest
- 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: Checkout printer-adapter crate
uses: actions/checkout@v4
with:
repository: ${{ github.repository }}
ref: ${{ github.ref_name }}
path: ./temp-repo
- name: Move printer-adapter into place
shell: pwsh
run: |
if (Test-Path "./temp-repo/printer-adapter") {
Move-Item -Path "./temp-repo/printer-adapter" -Destination "./printer-adapter" -Force
Remove-Item -Recurse -Force "./temp-repo"
Write-Host "Successfully placed printer-adapter into workspace."
} else {
Write-Host "Error: printer-adapter folder not found in repository."
}
- 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 and install driver package
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
Expand-Archive rustdesk_printer_driver_v4-1.4.zip -DestinationPath .
mkdir ./rustdesk/drivers -ErrorAction SilentlyContinue
mv -Force .\rustdesk_printer_driver_v4-1.4 ./rustdesk/drivers/RustDeskPrinterDriver
} catch {
Write-Host "Warning: Printer driver download failed."
}
# Build and install open printer adapter
if (Test-Path "./printer-adapter/Cargo.toml") {
Write-Host "Building open printer-adapter..."
pushd ./printer-adapter
cargo build --release --locked
popd
if (Test-Path "./printer-adapter/target/release/printer_driver_adapter.dll") {
cp -Force "./printer-adapter/target/release/printer_driver_adapter.dll" "./rustdesk/printer_driver_adapter.dll"
Write-Host "Replaced printer_driver_adapter.dll with open implementation."
} else {
Write-Host "Error: printer-adapter build succeeded but DLL was not found."
}
} else {
Write-Host "Warning: ./printer-adapter/Cargo.toml not found, remote printing will remain disabled."
}
- 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: 5
max_attempts: 3
retry_wait_seconds: 15
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@v4
if: env.UPLOAD_ARTIFACT == 'true'
with:
name: topmostwindow-artifacts
path: "./rustdesk"
- 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: 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: 10
max_attempts: 3
retry_wait_seconds: 15
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: 10
max_attempts: 3
retry_wait_seconds: 15
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
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@v4
with:
name: encrypted-secrets-zip
+668
View File
@@ -0,0 +1,668 @@
# Gitea Actions 适配副本:由 .github/workflows 同名文件适配(本地路径/cache/artifact 差异),修改时请同步两边。差异说明见 setup.md「Gitea 部署附录」。
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;files,D:\vcpkg-cache,readwrite"
# vcpkg version: 2024.07.12
VCPKG_COMMIT_ID: "${{ inputs.version == 'master' && '9e593bb18ea69cc5095e012465dcd675a822ed0d' || '120deac3062162151622ca4860575a33844ba10b' }}"
ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836"
VERSION: "${{ inputs.version }}"
NDK_VERSION: "r28c"
#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: ./.gitea/workflows/fetch-encrypted-secrets.yml
with:
zip_url_json: ${{ inputs.zip_url }}
generate-bridge:
uses: ./.gitea/workflows/bridge.yml
with:
version: ${{ inputs.version }}
build-RustDeskTempTopMostWindow:
needs: setup
uses: ./.gitea/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
with:
path: .rdgen-src
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.gitea/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: 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: 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@v4
with:
name: bridge-artifact
path: ./
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Setup NuGet
uses: NuGet/setup-nuget@v2
with:
nuget-version: 'latest'
- 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 Tech Pte. Ltd.|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./res/msi/preprocess.py
sed -i -e 's|r"Purslane(?: Tech Pte\\.)? Ltd"|"${{ env.compname }}"|' ./res/msi/preprocess.py
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./Cargo.toml
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./libs/portable/Cargo.toml
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./res/msi/Package/License.rtf
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./src/main.rs
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|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
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./src/main.rs
- 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
sed -i -e 's|&download_url|"${{ env.downloadLink }}"|' ./src/updater.rs
sed -i -e 's|$downloadUrl/$downloadFile|${{ env.downloadLink }}|' ./flutter/lib/desktop/widgets/update_progress.dart
- name: set server, serverPort, key, and apiserver
continue-on-error: true
shell: bash
env:
SERVER_DOMAIN: ${{ env.server }}
SERVER_PORT_INPUT: ${{ env.serverPort }}
SERVER_KEY: ${{ env.key }}
API_SERVER: ${{ env.apiServer }}
run: |
# Pass secrets via bash env vars to avoid quoting issues
if [ ! -f "./libs/hbb_common/src/config.rs" ]; then
echo "Error: config.rs not found!"
exit 1
fi
if [ ! -f "./src/common.rs" ]; then
echo "Error: common.rs not found!"
exit 1
fi
# Port must be a number; views.py defaults it to 21116
if ! [[ "$SERVER_PORT_INPUT" =~ ^[0-9]+$ ]]; then
echo "Error: serverPort must be a number, got: '$SERVER_PORT_INPUT'"
exit 1
fi
# Derive the port block from the rendezvous port
# (defaults 21116/21117/21118/21119)
SERVER_PORT=$SERVER_PORT_INPUT
RELAY_PORT=$((SERVER_PORT + 1))
WS_RENDEZVOUS_PORT=$((RELAY_PORT + 1))
WS_RELAY_PORT=$((WS_RENDEZVOUS_PORT + 1))
echo "Setting server: $SERVER_DOMAIN"
echo "Setting ports: $SERVER_PORT, $RELAY_PORT, $WS_RENDEZVOUS_PORT, $WS_RELAY_PORT"
sed -i -e "s|rs-ny.rustdesk.com|$SERVER_DOMAIN|" ./libs/hbb_common/src/config.rs
# Match on numeric value so the step is independent of defaults
sed -i -e "s|pub const RENDEZVOUS_PORT: i32 = [0-9][0-9]*;|pub const RENDEZVOUS_PORT: i32 = $SERVER_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|pub const RELAY_PORT: i32 = [0-9][0-9]*;|pub const RELAY_PORT: i32 = $RELAY_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|pub const WS_RENDEZVOUS_PORT: i32 = [0-9][0-9]*;|pub const WS_RENDEZVOUS_PORT: i32 = $WS_RENDEZVOUS_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|pub const WS_RELAY_PORT: i32 = [0-9][0-9]*;|pub const WS_RELAY_PORT: i32 = $WS_RELAY_PORT;|" ./libs/hbb_common/src/config.rs
sed -i -e "s|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|$SERVER_KEY|" ./libs/hbb_common/src/config.rs
sed -i -e "s|https://admin.rustdesk.com|$API_SERVER|" ./src/common.rs
echo "=== Verification ==="
grep -nE "RENDEZVOUS_PORT|RELAY_PORT|WS_" ./libs/hbb_common/src/config.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: |
Copy-Item .rdgen-src/.github/patches/allowCustom.py allowCustom.py
python allowCustom.py
# Remove Setup Server Tip
Copy-Item .rdgen-src/.github/patches/removeSetupServerTip.diff removeSetupServerTip.diff
git apply removeSetupServerTip.diff
- name: Patch remote printer port to spool file
shell: python
run: |
import os, re
src = "."
app = os.environ.get("appname") or "RustDesk"
literal = "C:\\\\ProgramData\\\\%s\\\\printer-spool\\\\job.prn" % app
# atch Rust remote_printer lib.rs
rs = os.path.join(src, "libs", "remote_printer", "src", "lib.rs")
if os.path.isfile(rs):
with open(rs, "r", encoding="utf-8") as f:
t = f.read()
if "printer-spool" in t:
print("Rust printer port already points to spool file")
else:
old = ('fn get_port_name(app_name: &str) -> Vec<u16> {\n'
' format!("{} Printer", app_name)')
new = ('fn get_port_name(app_name: &str) -> Vec<u16> {\n'
' let base = std::env::var("ProgramData")\n'
' .unwrap_or_else(|_| "C:\\\\ProgramData".to_string());\n'
' let dir = format!("{}\\\\{}\\\\printer-spool", base, app_name);\n'
' let _ = std::fs::create_dir_all(&dir);\n'
' format!("{}\\\\job.prn", dir)')
if old in t:
with open(rs, "w", encoding="utf-8") as f:
f.write(t.replace(old, new, 1))
print("Successfully patched Rust printer port")
else:
print("Warning: get_port_name() signature not matched in Rust source")
# patch C++ MSI Custom Action
cpp = os.path.join(src, "res", "msi", "CustomActions", "RemotePrinter.cpp")
if os.path.isfile(cpp):
with open(cpp, "r", encoding="utf-8") as f:
t = f.read()
if "printer-spool" in t:
print("MSI printer port already points to spool file")
else:
pattern = r'(RD_PRINTER_PORT\s*=\s*)L"[^"]*"'
new_t, n = re.subn(pattern, lambda m: m.group(1) + 'L"%s"' % literal, t, count=1)
if n == 1:
with open(cpp, "w", encoding="utf-8") as f:
f.write(new_t)
print("Successfully patched MSI CustomAction printer port")
else:
print("Warning: RD_PRINTER_PORT not matched in RemotePrinter.cpp")
- 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: |
# Precise fix: only extend the direct-connection condition with the
# key check, instead of globally replacing !key.is_empty() with
# false (which would also disable TCP encryption and UDP logic).
sed -i -e 's#if is_local || peer_nat_type == NatType::SYMMETRIC {#if is_local || peer_nat_type == NatType::SYMMETRIC || !key.is_empty() {#' ./src/client.rs
grep -n "key.is_empty()" ./src/client.rs || true
- name: 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: |
Copy-Item .rdgen-src/.github/patches/xoffline.diff 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: 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: Checkout printer-adapter crate
uses: actions/checkout@v4
with:
repository: ${{ github.repository }}
ref: ${{ github.ref_name }}
path: ./temp-repo
- name: Move printer-adapter into place
shell: pwsh
run: |
if (Test-Path "./temp-repo/printer-adapter") {
Move-Item -Path "./temp-repo/printer-adapter" -Destination "./printer-adapter" -Force
Remove-Item -Recurse -Force "./temp-repo"
Write-Host "Successfully placed printer-adapter into workspace."
} else {
Write-Host "Error: printer-adapter folder not found in repository."
}
- 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 and install driver package
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
Expand-Archive rustdesk_printer_driver_v4-1.4.zip -DestinationPath .
mkdir ./rustdesk/drivers -ErrorAction SilentlyContinue
mv -Force .\rustdesk_printer_driver_v4-1.4 ./rustdesk/drivers/RustDeskPrinterDriver
} catch {
Write-Host "Warning: Printer driver download failed."
}
# Build and install open printer adapter
if (Test-Path "./printer-adapter/Cargo.toml") {
Write-Host "Building open printer-adapter..."
pushd ./printer-adapter
cargo build --release --locked
popd
if (Test-Path "./printer-adapter/target/release/printer_driver_adapter.dll") {
cp -Force "./printer-adapter/target/release/printer_driver_adapter.dll" "./rustdesk/printer_driver_adapter.dll"
Write-Host "Replaced printer_driver_adapter.dll with open implementation."
} else {
Write-Host "Error: printer-adapter build succeeded but DLL was not found."
}
} else {
Write-Host "Warning: ./printer-adapter/Cargo.toml not found, remote printing will remain disabled."
}
- 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@v4
if: env.UPLOAD_ARTIFACT == 'true'
with:
name: topmostwindow-artifacts
path: "./rustdesk"
- 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: 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
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@v4
with:
name: encrypted-secrets-zip
@@ -0,0 +1,96 @@
# Gitea Actions 适配副本:由 .github/workflows 同名文件适配(本地路径/cache/artifact 差异),修改时请同步两边。差异说明见 setup.md「Gitea 部署附录」。
name: build RustDeskTempTopMostWindow
on:
workflow_call:
inputs:
upload-artifact:
type: boolean
default: true
target:
description: 'Target'
required: true
type: string
default: 'windows-2022'
configuration:
description: 'Configuration'
required: true
type: string
default: 'Release'
platform:
description: 'Platform'
required: true
type: string
default: 'x64'
target_version:
description: 'TargetVersion'
required: true
type: string
default: 'Windows10'
env:
project_path: WindowInjection/WindowInjection.vcxproj
jobs:
build-RustDeskTempTopMostWindow:
runs-on: ${{ inputs.target }}
strategy:
fail-fast: false
env:
build_output_dir: RustDeskTempTopMostWindow/WindowInjection/${{ inputs.platform }}/${{ inputs.configuration }}
steps:
- name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v3
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.gitea/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: Download the source code
run: |
git clone https://github.com/rustdesk-org/RustDeskTempTopMostWindow RustDeskTempTopMostWindow
# Build. commit 53b548a5398624f7149a382000397993542ad796 is tag v0.3
- name: Build the project
uses: nick-fields/retry@v3
with:
timeout_minutes: 5
max_attempts: 3
retry_wait_seconds: 15
shell: pwsh
command: |
cd RustDeskTempTopMostWindow && git checkout 53b548a5398624f7149a382000397993542ad796
if ($env:privacylink_url-ne "false") {
Invoke-WebRequest -Uri ${{ env.privacylink_url }}/get_png?filename=${{ env.privacylink_file }}"&"uuid=${{ env.privacylink_uuid }} -OutFile privacy.png
Copy-Item ../.github/patches/privacyScreen.py privacyScreen.py
python privacyScreen.py
rm ./WindowInjection/img.cpp
mv img.cpp ./WindowInjection/img.cpp
}
msbuild ${{ env.project_path }} -p:Configuration=${{ inputs.configuration }} -p:Platform=${{ inputs.platform }} /p:TargetVersion=${{ inputs.target_version }}
- name: Archive build artifacts
uses: actions/upload-artifact@v4
if: ${{ inputs.upload-artifact }}
with:
name: topmostwindow-artifacts
path: |
./${{ env.build_output_dir }}/WindowInjection.dll