mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-18 06:24:35 +03:00
v1.0.0: multi-arch Android APK + GitHub Actions release job + install docs (#30)
Version bump reflects the scope — a unified Rust core that now ships
for desktop (Linux/macOS/Windows) AND Android from the same crate.
Android changes:
- build.gradle.kts: ABI filters expanded to arm64-v8a + armeabi-v7a
+ x86_64 + x86. cargoBuild{Debug,Release} pass all four ABIs to
cargo-ndk in a single invocation. normalizeTun2proxySo() walks every
ABI dir now (was arm64-only).
- Release buildType signs with the debug keystore — no Play Store
target, so signature identity doesn't matter, installability does.
Gradle auto-provisions ~/.android/debug.keystore if absent, so CI
runners inherit this without extra setup.
- versionName 1.0.0, versionCode 100 (room to bump monotonically).
CI:
- release.yml gets a dedicated `android:` job that sets up JDK 17,
Android SDK/NDK 26, all four rust-android targets, installs
cargo-ndk, runs assembleRelease, and uploads a single universal APK
named `mhrv-rs-android-universal-v<version>.apk` into the same
`dist/` collected by the release job downstream.
- `release:` job now gates on `needs: [build, android]` so tagging
v1.0.0 triggers both build matrices before cutting the GitHub
release.
Docs:
- docs/android.md — full 10-step install walk-through: APK sideload,
Apps Script deployment (with "Advanced → Go to (unsafe) → Allow"
reality check), config paste, SNI reachability test, MITM CA
install with OEM-specific nav paths (Pixel / Samsung / Xiaomi),
Start, troubleshooting common failure modes. Also documents the
known limitations — Cloudflare Turnstile loops (inherent to the
Apps Script egress IP pool), UDP/QUIC not tunnelled, IPv6 leaks,
Apps Script daily quota — so users know what to expect before
trying it on a site that won't work.
- releases/README.md — APK row added to the English and Persian
tables, version bumped everywhere to v1.0.0.
- Top-level README — Android listed under Platforms with a link
to docs/android.md.
Release artifact:
- releases/mhrv-rs-android-universal-v1.0.0.apk — 38 MB universal
APK built locally from this tree. Installs + launches on API 24+.
The CI job will regenerate it on tag push; this is the copy
committed for users who can't reach GitHub Releases.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
96d1352728
commit
91015b0594
@@ -14,15 +14,19 @@ android {
|
||||
applicationId = "com.therealaleph.mhrv"
|
||||
minSdk = 24 // Android 7.0 — covers 99%+ of live devices.
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "0.1.0"
|
||||
versionCode = 100
|
||||
versionName = "1.0.0"
|
||||
|
||||
// Only arm64 for now — we can add armeabi-v7a in a second pass
|
||||
// if field reports need it. Android emulators on Apple Silicon
|
||||
// only run arm64 natively, so keeping things aarch64-only makes
|
||||
// the dev loop fast.
|
||||
// Ship all four mainstream Android ABIs:
|
||||
// - arm64-v8a — 95%+ of real-world Android phones since 2019
|
||||
// - armeabi-v7a — older/cheaper devices still on 32-bit ARM
|
||||
// - x86_64 — Android emulator on Intel Macs + Chromebooks
|
||||
// - x86 — legacy 32-bit Intel emulator; cheap to include
|
||||
// Per-ABI .so files push the APK up to ~50 MB, but users expect one
|
||||
// APK that Just Works rather than "pick the right ABI" which nobody
|
||||
// does correctly. Google Play would auto-split; we ship universal.
|
||||
ndk {
|
||||
abiFilters += listOf("arm64-v8a")
|
||||
abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64", "x86")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +37,15 @@ android {
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro",
|
||||
)
|
||||
// Sign release builds with the debug keystore so users can
|
||||
// sideload the APK without us shipping a proper release key.
|
||||
// The project has no Play Store presence, so signature
|
||||
// identity per-build doesn't matter — installability does.
|
||||
// Gradle auto-creates `~/.android/debug.keystore` on first use;
|
||||
// CI runners inherit that behaviour. Anyone rebuilding from
|
||||
// source gets their own signature, which is what we want for
|
||||
// an open-source project: trust the source, not a key we hold.
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,27 +109,34 @@ dependencies {
|
||||
val rustCrateDir = rootProject.projectDir.parentFile
|
||||
val jniLibsDir = file("src/main/jniLibs")
|
||||
|
||||
// After cargo-ndk dumps artifacts into jniLibs/arm64-v8a/, the tun2proxy
|
||||
// cdylib lands as `libtun2proxy-<hash>.so` (rustc's deps/ naming convention,
|
||||
// because tun2proxy is a transitive dep not a root crate). Android's
|
||||
// System.loadLibrary expects a stable name, and the hash changes between
|
||||
// builds, so we normalize it to `libtun2proxy.so` here. Also deletes any
|
||||
// stale hash-suffixed copies from previous builds.
|
||||
// After cargo-ndk dumps artifacts into each jniLibs/<abi>/ dir, the
|
||||
// tun2proxy cdylib lands as `libtun2proxy-<hash>.so` (rustc's deps/ naming
|
||||
// convention, because tun2proxy is a transitive dep not a root crate).
|
||||
// Android's System.loadLibrary expects a stable name, and the hash changes
|
||||
// between builds, so we normalize it to `libtun2proxy.so` in every ABI dir.
|
||||
// Also deletes any stale hash-suffixed copies from previous builds.
|
||||
fun normalizeTun2proxySo() {
|
||||
val abiDir = file("src/main/jniLibs/arm64-v8a")
|
||||
if (!abiDir.isDirectory) return
|
||||
val hashed = abiDir.listFiles { f -> f.name.matches(Regex("libtun2proxy-[0-9a-f]+\\.so")) }
|
||||
?: emptyArray()
|
||||
// Keep only the newest (release build) and rename it.
|
||||
val newest = hashed.maxByOrNull { it.lastModified() }
|
||||
if (newest != null) {
|
||||
val target = abiDir.resolve("libtun2proxy.so")
|
||||
if (target.exists()) target.delete()
|
||||
newest.copyTo(target, overwrite = true)
|
||||
val jniLibsRoot = file("src/main/jniLibs")
|
||||
if (!jniLibsRoot.isDirectory) return
|
||||
jniLibsRoot.listFiles()?.filter { it.isDirectory }?.forEach { abiDir ->
|
||||
val hashed = abiDir.listFiles { f -> f.name.matches(Regex("libtun2proxy-[0-9a-f]+\\.so")) }
|
||||
?: emptyArray()
|
||||
val newest = hashed.maxByOrNull { it.lastModified() }
|
||||
if (newest != null) {
|
||||
val target = abiDir.resolve("libtun2proxy.so")
|
||||
if (target.exists()) target.delete()
|
||||
newest.copyTo(target, overwrite = true)
|
||||
}
|
||||
hashed.forEach { it.delete() }
|
||||
}
|
||||
hashed.forEach { it.delete() }
|
||||
}
|
||||
|
||||
// All ABIs we ship. Keep in sync with `android.defaultConfig.ndk.abiFilters`
|
||||
// above; if these drift, the APK either includes .so files with no matching
|
||||
// ABI entry (dead weight) or advertises ABIs with no .so (runtime
|
||||
// UnsatisfiedLinkError on devices that pick that split).
|
||||
val androidAbis = listOf("arm64-v8a", "armeabi-v7a", "x86_64", "x86")
|
||||
|
||||
tasks.register<Exec>("cargoBuildDebug") {
|
||||
group = "build"
|
||||
// Intentionally ALWAYS uses --release. The Rust debug build is 80+MB
|
||||
@@ -124,27 +144,27 @@ tasks.register<Exec>("cargoBuildDebug") {
|
||||
// never worth it just for a Rust stack trace you wouldn't see in
|
||||
// logcat anyway. If you need Rust debug symbols, temporarily drop
|
||||
// `--release` below and accept the APK size.
|
||||
description = "Cross-compile mhrv_rs for arm64-v8a (release — same as cargoBuildRelease)"
|
||||
description = "Cross-compile mhrv_rs for all ABIs (release — same as cargoBuildRelease)"
|
||||
workingDir = rustCrateDir
|
||||
commandLine(
|
||||
"cargo", "ndk",
|
||||
"-t", "arm64-v8a",
|
||||
"-o", jniLibsDir.absolutePath,
|
||||
"build", "--release",
|
||||
)
|
||||
commandLine(buildList<String> {
|
||||
add("cargo"); add("ndk")
|
||||
androidAbis.forEach { add("-t"); add(it) }
|
||||
add("-o"); add(jniLibsDir.absolutePath)
|
||||
add("build"); add("--release")
|
||||
})
|
||||
doLast { normalizeTun2proxySo() }
|
||||
}
|
||||
|
||||
tasks.register<Exec>("cargoBuildRelease") {
|
||||
group = "build"
|
||||
description = "Cross-compile mhrv_rs for arm64-v8a (release)"
|
||||
description = "Cross-compile mhrv_rs for all ABIs (release)"
|
||||
workingDir = rustCrateDir
|
||||
commandLine(
|
||||
"cargo", "ndk",
|
||||
"-t", "arm64-v8a",
|
||||
"-o", jniLibsDir.absolutePath,
|
||||
"build", "--release",
|
||||
)
|
||||
commandLine(buildList<String> {
|
||||
add("cargo"); add("ndk")
|
||||
androidAbis.forEach { add("-t"); add(it) }
|
||||
add("-o"); add(jniLibsDir.absolutePath)
|
||||
add("build"); add("--release")
|
||||
})
|
||||
doLast { normalizeTun2proxySo() }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user