Files
rustdesk-android-build/.gitea/workflows/android.yml
T
gandc 48e3aaa640
Build RustDesk Android / Build RustDesk Android ARM64 (push) Failing after 1h0m25s
first commit
2026-09-09 20:21:30 +03:00

725 lines
21 KiB
YAML

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 <<EOF
storePassword=${ANDROID_KEYSTORE_PASSWORD}
keyPassword=${ANDROID_KEYSTORE_PASSWORD}
keyAlias=${ANDROID_KEY_ALIAS}
storeFile=rustdesk-release.jks
EOF
chmod 600 \
key.properties
if [ ! -s app/rustdesk-release.jks ]; then
echo "ERROR: Restored keystore is empty"
exit 1
fi
echo "Checking keystore..."
keytool \
-list \
-keystore app/rustdesk-release.jks \
-storepass "${ANDROID_KEYSTORE_PASSWORD}" \
-alias "${ANDROID_KEY_ALIAS}" \
>/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