From 48e3aaa640fd413ee2464802a2e7e5ce061bbaa5 Mon Sep 17 00:00:00 2001 From: GandC Date: Wed, 9 Sep 2026 20:21:30 +0300 Subject: [PATCH] first commit --- .gitea/workflows/android.yml | 724 +++++++++++++++++++++++++++++++++++ .gitignore | 3 + 2 files changed, 727 insertions(+) create mode 100644 .gitea/workflows/android.yml create mode 100644 .gitignore diff --git a/.gitea/workflows/android.yml b/.gitea/workflows/android.yml new file mode 100644 index 0000000..461de7c --- /dev/null +++ b/.gitea/workflows/android.yml @@ -0,0 +1,724 @@ +name: Build RustDesk Android + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - ".gitea/workflows/android.yml" + +env: + RUSTDESK_VERSION: "1.4.9" + + RUST_VERSION: "1.75" + CARGO_NDK_VERSION: "3.1.2" + + CARGO_EXPAND_VERSION: "1.0.95" + FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" + + BRIDGE_FLUTTER_VERSION: "3.22.3" + ANDROID_FLUTTER_VERSION: "3.24.5" + + ANDROID_NDK_VERSION: "28.2.13676358" + + VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" + +jobs: + build-android: + name: Build RustDesk Android ARM64 + runs-on: ubuntu-24.04 + + steps: + # ----------------------------------------------------------------------- + # System dependencies + # ----------------------------------------------------------------------- + + - name: Install system dependencies + run: | + set -euxo pipefail + + sudo apt-get update + + sudo apt-get install -y \ + ca-certificates \ + clang \ + cmake \ + curl \ + gcc \ + gcc-multilib \ + g++ \ + g++-multilib \ + git \ + libayatana-appindicator3-dev \ + libasound2-dev \ + libc6-dev \ + libclang-dev \ + libunwind-dev \ + libgstreamer1.0-dev \ + libgstreamer-plugins-base1.0-dev \ + libgtk-3-dev \ + libpam0g-dev \ + libpulse-dev \ + libva-dev \ + libxcb-randr0-dev \ + libxcb-shape0-dev \ + libxcb-xfixes0-dev \ + libxdo-dev \ + libxfixes-dev \ + llvm-dev \ + nasm \ + ninja-build \ + openjdk-17-jdk-headless \ + pkg-config \ + tree \ + unzip \ + wget \ + xz-utils + + # ----------------------------------------------------------------------- + # RustDesk source + # ----------------------------------------------------------------------- + + - name: Clone RustDesk + run: | + set -euxo pipefail + + git clone \ + --branch "${RUSTDESK_VERSION}" \ + --depth 1 \ + --recurse-submodules \ + --shallow-submodules \ + https://github.com/rustdesk/rustdesk.git \ + rustdesk + + cd rustdesk + + echo "RustDesk commit:" + git rev-parse HEAD + + echo "RustDesk tag:" + git describe --tags --always + + echo + echo "Submodules:" + git submodule status + + # ----------------------------------------------------------------------- + # Custom RustDesk ID server + public key + # + # Relay is NOT modified. + # ----------------------------------------------------------------------- + + - name: Configure custom ID server and key + env: + CUSTOM_ID_SERVER: ${{ secrets.RUSTDESK_ID_SERVER }} + CUSTOM_SERVER_KEY: ${{ secrets.RUSTDESK_KEY }} + run: | + set -euo pipefail + + if [ -z "${CUSTOM_ID_SERVER}" ]; then + echo "ERROR: RUSTDESK_ID_SERVER secret is empty" + exit 1 + fi + + if [ -z "${CUSTOM_SERVER_KEY}" ]; then + echo "ERROR: RUSTDESK_KEY secret is empty" + exit 1 + fi + + cd rustdesk + + python3 <<'PY' + import os + import re + from pathlib import Path + + path = Path("libs/hbb_common/src/config.rs") + + if not path.exists(): + raise SystemExit( + "libs/hbb_common/src/config.rs does not exist. " + "Check that submodules were cloned correctly." + ) + + text = path.read_text(encoding="utf-8") + + id_server = os.environ["CUSTOM_ID_SERVER"].strip() + server_key = os.environ["CUSTOM_SERVER_KEY"].strip() + + if not id_server: + raise SystemExit("RUSTDESK_ID_SERVER is empty") + + if not server_key: + raise SystemExit("RUSTDESK_KEY is empty") + + text, id_count = re.subn( + r'pub const RENDEZVOUS_SERVERS:\s*&\[&str\]\s*=\s*&\[[^\n]*\];', + f'pub const RENDEZVOUS_SERVERS: &[&str] = &["{id_server}"];', + text, + count=1, + ) + + text, key_count = re.subn( + r'pub const RS_PUB_KEY:\s*&str\s*=\s*"[^"]*";', + f'pub const RS_PUB_KEY: &str = "{server_key}";', + text, + count=1, + ) + + if id_count != 1: + raise SystemExit( + "Failed to patch RENDEZVOUS_SERVERS. " + "RustDesk source layout may have changed." + ) + + if key_count != 1: + raise SystemExit( + "Failed to patch RS_PUB_KEY. " + "RustDesk source layout may have changed." + ) + + path.write_text(text, encoding="utf-8") + + print(f"ID server configured: {id_server}") + print("Server public key configured successfully") + PY + + echo + echo "Checking patched configuration..." + + grep -n "RENDEZVOUS_SERVERS" \ + libs/hbb_common/src/config.rs + + if grep -q 'rs-ny\.rustdesk\.com' \ + libs/hbb_common/src/config.rs; then + + echo "ERROR: Default RustDesk ID server is still present" + exit 1 + fi + + if ! grep -q 'pub const RS_PUB_KEY' \ + libs/hbb_common/src/config.rs; then + + echo "ERROR: RS_PUB_KEY was not found" + exit 1 + fi + + echo "RS_PUB_KEY patched: yes" + echo "Relay configuration untouched: yes" + + # ----------------------------------------------------------------------- + # Rust + # ----------------------------------------------------------------------- + + - name: Install Rust + run: | + set -euxo pipefail + + curl \ + --proto '=https' \ + --tlsv1.2 \ + -sSf \ + https://sh.rustup.rs \ + | sh -s -- \ + -y \ + --default-toolchain "${RUST_VERSION}" + + source "${HOME}/.cargo/env" + + rustc --version + cargo --version + + rustup target add aarch64-linux-android + + # ----------------------------------------------------------------------- + # Android SDK + NDK + # ----------------------------------------------------------------------- + + - name: Install Android SDK + uses: android-actions/setup-android@v3 + + - name: Install Android packages + run: | + set -euxo pipefail + + yes | sdkmanager --licenses >/dev/null || true + + sdkmanager \ + "platform-tools" \ + "platforms;android-34" \ + "build-tools;34.0.0" \ + "ndk;${ANDROID_NDK_VERSION}" + + echo \ + "ANDROID_NDK_HOME=${ANDROID_SDK_ROOT}/ndk/${ANDROID_NDK_VERSION}" \ + >> "${GITHUB_ENV}" + + echo \ + "ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk/${ANDROID_NDK_VERSION}" \ + >> "${GITHUB_ENV}" + + echo \ + "ANDROID_NDK=${ANDROID_SDK_ROOT}/ndk/${ANDROID_NDK_VERSION}" \ + >> "${GITHUB_ENV}" + + echo "Android SDK:" + echo "${ANDROID_SDK_ROOT}" + + echo + echo "Android NDK:" + ls -la \ + "${ANDROID_SDK_ROOT}/ndk/${ANDROID_NDK_VERSION}" + + echo + echo "Android build tools:" + ls -la \ + "${ANDROID_SDK_ROOT}/build-tools/34.0.0" + + # ----------------------------------------------------------------------- + # Flutter 3.22.3 for flutter-rust-bridge generation + # ----------------------------------------------------------------------- + + - name: Install Flutter for bridge generation + run: | + set -euxo pipefail + + mkdir -p "${HOME}/flutter-sdks" + + curl \ + -L \ + "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${BRIDGE_FLUTTER_VERSION}-stable.tar.xz" \ + -o /tmp/flutter-bridge.tar.xz + + tar \ + -xf /tmp/flutter-bridge.tar.xz \ + -C "${HOME}/flutter-sdks" + + mv \ + "${HOME}/flutter-sdks/flutter" \ + "${HOME}/flutter-sdks/flutter-${BRIDGE_FLUTTER_VERSION}" + + "${HOME}/flutter-sdks/flutter-${BRIDGE_FLUTTER_VERSION}/bin/flutter" \ + config \ + --no-analytics + + "${HOME}/flutter-sdks/flutter-${BRIDGE_FLUTTER_VERSION}/bin/flutter" \ + --version + + # ----------------------------------------------------------------------- + # Generate Flutter/Rust bridge + # ----------------------------------------------------------------------- + + - name: Generate Flutter Rust bridge + run: | + set -euxo pipefail + + source "${HOME}/.cargo/env" + + export PATH="${HOME}/flutter-sdks/flutter-${BRIDGE_FLUTTER_VERSION}/bin:${PATH}" + + cargo install \ + cargo-expand \ + --version "${CARGO_EXPAND_VERSION}" \ + --locked + + cargo install \ + flutter_rust_bridge_codegen \ + --version "${FLUTTER_RUST_BRIDGE_VERSION}" \ + --features uuid \ + --locked + + cd rustdesk + + sed -i \ + 's/extended_text: 14.0.0/extended_text: 13.0.0/g' \ + flutter/pubspec.yaml + + pushd flutter + + flutter pub get + + popd + + 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 + + test -f src/bridge_generated.rs + test -f src/bridge_generated.io.rs + test -f flutter/lib/generated_bridge.dart + test -f flutter/lib/generated_bridge.freezed.dart + test -f flutter/macos/Runner/bridge_generated.h + test -f flutter/ios/Runner/bridge_generated.h + + echo "Flutter Rust bridge generated successfully" + + # ----------------------------------------------------------------------- + # Flutter 3.24.5 for Android build + # ----------------------------------------------------------------------- + + - name: Install Android Flutter + run: | + set -euxo pipefail + + curl \ + -L \ + "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${ANDROID_FLUTTER_VERSION}-stable.tar.xz" \ + -o /tmp/flutter-android.tar.xz + + tar \ + -xf /tmp/flutter-android.tar.xz \ + -C "${HOME}/flutter-sdks" + + mv \ + "${HOME}/flutter-sdks/flutter" \ + "${HOME}/flutter-sdks/flutter-${ANDROID_FLUTTER_VERSION}" + + export PATH="${HOME}/flutter-sdks/flutter-${ANDROID_FLUTTER_VERSION}/bin:${PATH}" + + flutter config \ + --no-analytics + + flutter config \ + --android-sdk "${ANDROID_SDK_ROOT}" + + flutter --version + + flutter doctor -v || true + + cd \ + "${HOME}/flutter-sdks/flutter-${ANDROID_FLUTTER_VERSION}" + + git apply \ + "${GITHUB_WORKSPACE}/rustdesk/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff" + + # ----------------------------------------------------------------------- + # vcpkg + # ----------------------------------------------------------------------- + + - name: Install vcpkg + run: | + set -euxo pipefail + + sudo rm -rf /opt/vcpkg + + sudo git clone \ + https://github.com/microsoft/vcpkg.git \ + /opt/vcpkg + + sudo git \ + -C /opt/vcpkg \ + checkout "${VCPKG_COMMIT_ID}" + + sudo chown \ + -R "$(id -u):$(id -g)" \ + /opt/vcpkg + + /opt/vcpkg/bootstrap-vcpkg.sh \ + -disableMetrics + + echo \ + "VCPKG_ROOT=/opt/vcpkg" \ + >> "${GITHUB_ENV}" + + /opt/vcpkg/vcpkg version + + # ----------------------------------------------------------------------- + # Android native dependencies + # ----------------------------------------------------------------------- + + - name: Build Android dependencies + run: | + set -euxo pipefail + + source "${HOME}/.cargo/env" + + export PATH="${HOME}/flutter-sdks/flutter-${ANDROID_FLUTTER_VERSION}/bin:${PATH}" + + export VCPKG_ROOT=/opt/vcpkg + + export ANDROID_NDK_HOME="${ANDROID_SDK_ROOT}/ndk/${ANDROID_NDK_VERSION}" + export ANDROID_NDK_ROOT="${ANDROID_NDK_HOME}" + export ANDROID_NDK="${ANDROID_NDK_HOME}" + + cd rustdesk + + if ! ./flutter/build_android_deps.sh arm64-v8a; then + echo + echo "Android dependency build failed." + echo "Dumping vcpkg logs:" + echo + + find "${VCPKG_ROOT}/" \ + -name "*.log" \ + -type f \ + | while read -r logfile; do + echo "===== ${logfile} =====" + cat "${logfile}" || true + echo + done + + exit 1 + fi + + # ----------------------------------------------------------------------- + # Rust native Android library + # ----------------------------------------------------------------------- + + - name: Build RustDesk native library + run: | + set -euxo pipefail + + source "${HOME}/.cargo/env" + + export VCPKG_ROOT=/opt/vcpkg + + export ANDROID_NDK_HOME="${ANDROID_SDK_ROOT}/ndk/${ANDROID_NDK_VERSION}" + export ANDROID_NDK_ROOT="${ANDROID_NDK_HOME}" + export ANDROID_NDK="${ANDROID_NDK_HOME}" + + cargo install \ + cargo-ndk \ + --version "${CARGO_NDK_VERSION}" \ + --locked + + rustup target add \ + aarch64-linux-android + + cd rustdesk + + ./flutter/ndk_arm64.sh + + test -f \ + target/aarch64-linux-android/release/liblibrustdesk.so + + echo + echo "Native library:" + ls -lh \ + target/aarch64-linux-android/release/liblibrustdesk.so + + # ----------------------------------------------------------------------- + # Permanent Android release signing + # + # key.properties: + # flutter/android/key.properties + # + # keystore: + # flutter/android/app/rustdesk-release.jks + # + # RustDesk's app/build.gradle resolves storeFile relative to app/. + # ----------------------------------------------------------------------- + + - name: Configure Android release signing + env: + ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + run: | + set -euo pipefail + + if [ -z "${ANDROID_KEYSTORE_BASE64}" ]; then + echo "ERROR: ANDROID_KEYSTORE_BASE64 secret is empty" + exit 1 + fi + + if [ -z "${ANDROID_KEYSTORE_PASSWORD}" ]; then + echo "ERROR: ANDROID_KEYSTORE_PASSWORD secret is empty" + exit 1 + fi + + if [ -z "${ANDROID_KEY_ALIAS}" ]; then + echo "ERROR: ANDROID_KEY_ALIAS secret is empty" + exit 1 + fi + + cd rustdesk/flutter/android + + echo "Restoring Android release keystore..." + + printf '%s' "${ANDROID_KEYSTORE_BASE64}" \ + | base64 --decode \ + > app/rustdesk-release.jks + + chmod 600 \ + app/rustdesk-release.jks + + cat > key.properties </dev/null + + echo "Keystore is valid." + echo "Android release signing configured successfully." + + # ----------------------------------------------------------------------- + # Flutter Android APK + # ----------------------------------------------------------------------- + + - name: Build signed APK + run: | + set -euxo pipefail + + source "${HOME}/.cargo/env" + + export PATH="${HOME}/flutter-sdks/flutter-${ANDROID_FLUTTER_VERSION}/bin:${PATH}" + + export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 + export PATH="${JAVA_HOME}/bin:${PATH}" + + export VCPKG_ROOT=/opt/vcpkg + + export ANDROID_NDK_HOME="${ANDROID_SDK_ROOT}/ndk/${ANDROID_NDK_VERSION}" + export ANDROID_NDK_ROOT="${ANDROID_NDK_HOME}" + export ANDROID_NDK="${ANDROID_NDK_HOME}" + + cd rustdesk + + mkdir -p \ + flutter/android/app/src/main/jniLibs/arm64-v8a + + cp \ + target/aarch64-linux-android/release/liblibrustdesk.so \ + flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so + + cp \ + "${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so" \ + flutter/android/app/src/main/jniLibs/arm64-v8a/libc++_shared.so + + sed -i \ + 's/org.gradle.jvmargs=-Xmx1024M/org.gradle.jvmargs=-Xmx2g/g' \ + flutter/android/gradle.properties + + echo + echo "JNI libraries:" + ls -lh \ + flutter/android/app/src/main/jniLibs/arm64-v8a/ + + echo + echo "Signing files:" + ls -lh \ + flutter/android/key.properties \ + flutter/android/app/rustdesk-release.jks + + pushd flutter + + flutter clean + + flutter pub get + + flutter build apk \ + --release \ + --target-platform android-arm64 \ + --split-per-abi + + popd + + test -f \ + flutter/build/app/outputs/flutter-apk/app-arm64-v8a-release.apk + + mkdir -p output + + cp \ + flutter/build/app/outputs/flutter-apk/app-arm64-v8a-release.apk \ + "output/rustdesk-${RUSTDESK_VERSION}-custom-arm64.apk" + + echo + echo "Built APK:" + ls -lh \ + "output/rustdesk-${RUSTDESK_VERSION}-custom-arm64.apk" + + echo + echo "SHA256:" + sha256sum \ + "output/rustdesk-${RUSTDESK_VERSION}-custom-arm64.apk" + + # ----------------------------------------------------------------------- + # Verify APK release signature + # ----------------------------------------------------------------------- + + - name: Verify APK signature + run: | + set -euxo pipefail + + APK="rustdesk/output/rustdesk-${RUSTDESK_VERSION}-custom-arm64.apk" + + APKSIGNER="${ANDROID_SDK_ROOT}/build-tools/34.0.0/apksigner" + + if [ ! -x "${APKSIGNER}" ]; then + echo "ERROR: apksigner not found:" + echo "${APKSIGNER}" + exit 1 + fi + + if [ ! -f "${APK}" ]; then + echo "ERROR: APK not found:" + echo "${APK}" + exit 1 + fi + + echo + echo "Verifying APK signature..." + echo + + "${APKSIGNER}" verify \ + --verbose \ + --print-certs \ + "${APK}" + + echo + echo "APK signature verification successful." + + # ----------------------------------------------------------------------- + # APK checksum + # ----------------------------------------------------------------------- + + - name: Create checksum + run: | + set -euxo pipefail + + cd rustdesk/output + + sha256sum \ + "rustdesk-${RUSTDESK_VERSION}-custom-arm64.apk" \ + > "rustdesk-${RUSTDESK_VERSION}-custom-arm64.apk.sha256" + + cat \ + "rustdesk-${RUSTDESK_VERSION}-custom-arm64.apk.sha256" + + # ----------------------------------------------------------------------- + # Upload Gitea Actions artifact + # ----------------------------------------------------------------------- + + - name: Upload APK + uses: actions/upload-artifact@v4.6.2 + with: + name: rustdesk-${{ env.RUSTDESK_VERSION }}-custom-arm64 + path: | + rustdesk/output/rustdesk-${{ env.RUSTDESK_VERSION }}-custom-arm64.apk + rustdesk/output/rustdesk-${{ env.RUSTDESK_VERSION }}-custom-arm64.apk.sha256 + if-no-files-found: error diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6bbbb1f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +rustdesk-release.jks.base64 +rustdesk-release.jks +rustdesk-release.pass