mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-18 05:36:56 +03:00
28be8f67d5
Major feature release across Android + desktop. Six items the user asked for, verified end-to-end on the emulator. Android ------- * Unified Connect/Disconnect button. Single large button swaps between green "Connect" (when the service is down) and red "Disconnect" (when it's up). Tracks the real service state via a new process-wide `VpnState` singleton flipped from the service's startEverything() / teardown() — not optimistic, the button only reports what the service actually did. * Connection mode dropdown (issue #37). Two options: VPN (TUN) — routes every app — and Proxy only — user configures per-app via Wi-Fi proxy to 127.0.0.1:8080 (HTTP) / :1081 (SOCKS5). PROXY_ONLY skips VpnService.prepare() entirely (no OS VPN grant prompt) and the service just keeps the foreground listeners up. Default is VPN_TUN so existing behaviour is preserved for users who upgrade without looking at the dropdown. * App splitting. In VPN_TUN mode you can pick All / Only selected / All except selected, with a picker dialog that lists installed user-visible apps (LazyColumn with search, "show system apps" toggle, multi-select checkboxes). ONLY calls `Builder.addAllowedApplication()` for each chosen package; EXCEPT calls `addDisallowedApplication()` additive to the mandatory self-exclude. Requires QUERY_ALL_PACKAGES — added to the manifest along with a `<queries>` launcher-intent filter so the picker rows can render app labels, not just package strings. * Persian/English UI toggle with RTL. Top-bar TextButton cycles AUTO → FA → EN → AUTO. Persian strings live in `res/values-fa/strings.xml`; English in `res/values/strings.xml`. `AppCompatDelegate.setApplicationLocales()` is used as the persistence layer (plus `AppLocalesMetadataHolderService` meta and `locales_config.xml` for the per-app-language OS entry on API 33+). MainActivity overrides `attachBaseContext` to wrap the context with the right locale at the earliest possible moment — otherwise a saved preference wouldn't apply until the SECOND process after toggling. RTL swaps automatically because Persian is script="Arab" in Android's locale database. * Collapsible How-to-use card. The big instruction block that used to dominate the bottom of the screen now lives inside a CollapsibleSection that starts expanded for a fresh install (empty deployment URLs / auth_key) and collapsed otherwise. * Update check auto-fires on first composition, silent-on-up-to-date, snackbar-only-if-available. Still surfaces via the version badge tap for manual checks. * MhrvVpnService teardown guard was kept from v1.0.2 — `AtomicBoolean` makes the second caller a no-op, which is the SIGSEGV fix for "tap Stop, app closes" from before. Stress-tested under rapid Connect/Disconnect cycles. Desktop ------- * Fix: Advanced section silently resetting on every Save. `ConfigWire` was missing `fetch_ips_from_api` / `max_ips_to_scan` / `scan_batch_size` / `google_ip_validation` — every persist dropped them, every reload fell back to the serde defaults, user saw their Advanced toggles reset. Added the fields to the wire struct (issue surfaced by the user as "Advanced resets after reopening the app"). * Windows renderer fallback (issue #28). `eframe` is now built with BOTH `glow` (OpenGL 2+) and `wgpu` (DX12/Vulkan/Metal); runtime defaults to glow for compat but honours `MHRV_RENDERER=wgpu` for boxes that crash with "egui_glow requires opengl 2.0+" — old Windows hardware, RDP sessions, VMs without GPU acceleration. `run.bat` auto-retries the UI with `MHRV_RENDERER=wgpu` if the first launch exits non-zero, so users don't need to know about the flag. CI -- * Added OpenWRT mipsel-softfloat build target (issue #26). MT7621 routers specifically need soft-float because the CPU has no FPU; a hard-float binary segfaults on first fp op. Built via `messense/rust-musl-cross:mipsel-musl-softfloat` docker image + nightly Rust with `-Z build-std` (mipsel is Rust tier 3 since 1.72, no pre-built std). Marked `continue-on-error: true` — the tier-3 target occasionally regresses and we'd rather ship the rest of the release than block on MT7621 support. Signature / versioning ---------------------- * versionCode 110, versionName 1.1.0; Cargo bumped to 1.1.0. * Release APK signed with the committed `release.jks` (same as v1.0.2), so v1.0.2 → v1.1.0 upgrades install in-place without the uninstall-first dance. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
329 lines
13 KiB
YAML
329 lines
13 KiB
YAML
name: release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*'
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
jobs:
|
|
build:
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
# Pin to Ubuntu 22.04 (GLIBC 2.35) so the glibc builds run on any
|
|
# distro that's ≥ Ubuntu 22.04 / Debian 12 / Mint 21 / Fedora 36.
|
|
# ubuntu-latest points at 24.04 (GLIBC 2.39) which bakes in a
|
|
# too-new GLIBC symbol requirement and rejects loading on older
|
|
# distros. For users behind tight internet who literally can't
|
|
# dist-upgrade, this matters.
|
|
- target: x86_64-unknown-linux-gnu
|
|
os: ubuntu-22.04
|
|
name: mhrv-rs-linux-amd64
|
|
- target: aarch64-unknown-linux-gnu
|
|
os: ubuntu-22.04
|
|
name: mhrv-rs-linux-arm64
|
|
- target: arm-unknown-linux-gnueabihf
|
|
os: ubuntu-22.04
|
|
name: mhrv-rs-raspbian-armhf
|
|
- target: x86_64-apple-darwin
|
|
os: macos-latest
|
|
name: mhrv-rs-macos-amd64
|
|
- target: aarch64-apple-darwin
|
|
os: macos-latest
|
|
name: mhrv-rs-macos-arm64
|
|
- target: x86_64-pc-windows-gnu
|
|
os: windows-latest
|
|
name: mhrv-rs-windows-amd64
|
|
- target: x86_64-unknown-linux-musl
|
|
os: ubuntu-latest
|
|
name: mhrv-rs-linux-musl-amd64
|
|
- target: aarch64-unknown-linux-musl
|
|
os: ubuntu-latest
|
|
name: mhrv-rs-linux-musl-arm64
|
|
# OpenWRT MT7621 (soft-float mipsel 32-bit). Dozens of cheap
|
|
# home routers run this chipset and they *specifically* need
|
|
# the soft-float variant — MT7621 has no hardware FPU and a
|
|
# hard-float binary segfaults on the first fp op. Tier-3 in
|
|
# Rust since 1.72; we build it via messense's musl-cross
|
|
# docker image which still has a mipsel-softfloat toolchain.
|
|
# `continue-on-error: true` so a regression here doesn't block
|
|
# the rest of the release. Issue #26.
|
|
- target: mipsel-unknown-linux-musl
|
|
os: ubuntu-latest
|
|
name: mhrv-rs-openwrt-mipsel-softfloat
|
|
mipsel_softfloat: true
|
|
|
|
runs-on: ${{ matrix.os }}
|
|
# mipsel-softfloat is best-effort: the Rust tier-3 target occasionally
|
|
# regresses. Letting it fail keeps the main release going so
|
|
# desktop/Android users aren't blocked by MT7621 router support.
|
|
continue-on-error: ${{ matrix.mipsel_softfloat == true }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: ${{ matrix.target }}
|
|
|
|
# eframe needs a few system libs on Linux for window management, keyboard,
|
|
# and OpenGL/X11/Wayland. We install them on the Ubuntu runners regardless
|
|
# of arch so both CLI-only and UI builds succeed.
|
|
- name: Install Linux eframe system deps
|
|
if: runner.os == 'Linux'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y \
|
|
libxkbcommon-dev \
|
|
libwayland-dev \
|
|
libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev \
|
|
libx11-dev \
|
|
libgl1-mesa-dev libglib2.0-dev libgtk-3-dev
|
|
|
|
- name: Install aarch64 cross-compile toolchain (Linux only)
|
|
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y gcc-aarch64-linux-gnu
|
|
echo '[target.aarch64-unknown-linux-gnu]' >> ~/.cargo/config.toml
|
|
echo 'linker = "aarch64-linux-gnu-gcc"' >> ~/.cargo/config.toml
|
|
|
|
- name: Install armhf cross-compile toolchain (Linux only)
|
|
if: matrix.target == 'arm-unknown-linux-gnueabihf'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y gcc-arm-linux-gnueabihf
|
|
echo '[target.arm-unknown-linux-gnueabihf]' >> ~/.cargo/config.toml
|
|
echo 'linker = "arm-linux-gnueabihf-gcc"' >> ~/.cargo/config.toml
|
|
|
|
- name: Install Windows MinGW toolchain
|
|
if: matrix.target == 'x86_64-pc-windows-gnu'
|
|
id: msys2
|
|
uses: msys2/setup-msys2@v2
|
|
with:
|
|
msystem: MINGW64
|
|
update: true
|
|
install: mingw-w64-x86_64-gcc
|
|
|
|
- name: Configure Windows GNU linker
|
|
if: matrix.target == 'x86_64-pc-windows-gnu'
|
|
shell: pwsh
|
|
run: |
|
|
$gcc = "${{ steps.msys2.outputs.msys2-location }}\mingw64\bin\gcc.exe" -replace '\\','/'
|
|
New-Item -ItemType Directory -Force -Path $env:USERPROFILE/.cargo | Out-Null
|
|
Add-Content -Path $env:USERPROFILE/.cargo/config.toml -Value '[target.x86_64-pc-windows-gnu]'
|
|
Add-Content -Path $env:USERPROFILE/.cargo/config.toml -Value "linker = '$gcc'"
|
|
|
|
- name: Build CLI
|
|
if: "!endsWith(matrix.target, '-linux-musl')"
|
|
run: cargo build --release --target ${{ matrix.target }} --bin mhrv-rs
|
|
|
|
# Fully-static musl builds for OpenWRT / Alpine / libc-less systems.
|
|
# messense/rust-musl-cross ships a pre-built musl toolchain so `ring`
|
|
# (rustls' crypto backend) cross-compiles cleanly on both archs.
|
|
- name: Build CLI (musl via docker)
|
|
if: matrix.target == 'x86_64-unknown-linux-musl'
|
|
run: |
|
|
docker run --rm -v "$PWD":/src -w /src \
|
|
messense/rust-musl-cross:x86_64-musl \
|
|
cargo build --release --target x86_64-unknown-linux-musl --bin mhrv-rs
|
|
sudo chown -R "$(id -u):$(id -g)" target
|
|
|
|
- name: Build CLI (musl via docker, arm64)
|
|
if: matrix.target == 'aarch64-unknown-linux-musl'
|
|
run: |
|
|
docker run --rm -v "$PWD":/src -w /src \
|
|
messense/rust-musl-cross:aarch64-musl \
|
|
cargo build --release --target aarch64-unknown-linux-musl --bin mhrv-rs
|
|
sudo chown -R "$(id -u):$(id -g)" target
|
|
|
|
# OpenWRT MT7621 / mipsel-softfloat. The messense image tag
|
|
# `mipsel-musl-softfloat` ships a toolchain that emits soft-float
|
|
# insn exclusively — matches the MT7621's FPU-less reality.
|
|
# Requires Rust nightly + -Z build-std because mipsel is tier 3
|
|
# in the stable channel, which means no pre-built std.
|
|
- name: Build CLI (mipsel-softfloat via docker)
|
|
if: matrix.target == 'mipsel-unknown-linux-musl' && matrix.mipsel_softfloat == true
|
|
run: |
|
|
docker run --rm -v "$PWD":/src -w /src \
|
|
messense/rust-musl-cross:mipsel-musl-softfloat \
|
|
sh -c "rustup toolchain install nightly --profile minimal --component rust-src && \
|
|
cargo +nightly build --release \
|
|
-Z build-std=std,panic_abort \
|
|
--target mipsel-unknown-linux-musl \
|
|
--bin mhrv-rs"
|
|
sudo chown -R "$(id -u):$(id -g)" target
|
|
|
|
# UI build: we try to build the UI binary on every platform. If it fails
|
|
# on cross-compile for linux-arm64 (missing arm64 system libs cross),
|
|
# we still ship the CLI. We also skip the UI on musl targets (OpenWRT etc.
|
|
# are headless, bundling X11 makes no sense).
|
|
- name: Build UI
|
|
if: matrix.target != 'aarch64-unknown-linux-gnu' && matrix.target != 'arm-unknown-linux-gnueabihf' && !endsWith(matrix.target, '-linux-musl')
|
|
run: cargo build --release --target ${{ matrix.target }} --features ui --bin mhrv-rs-ui
|
|
|
|
- name: Package (unix)
|
|
if: runner.os != 'Windows'
|
|
run: |
|
|
mkdir -p dist
|
|
cp target/${{ matrix.target }}/release/mhrv-rs dist/mhrv-rs
|
|
chmod +x dist/mhrv-rs
|
|
if [ -f target/${{ matrix.target }}/release/mhrv-rs-ui ]; then
|
|
cp target/${{ matrix.target }}/release/mhrv-rs-ui dist/mhrv-rs-ui
|
|
chmod +x dist/mhrv-rs-ui
|
|
fi
|
|
# OpenWRT / musl archives get the procd init script instead of run.sh,
|
|
# since routers don't have a CA to install and run headless via procd.
|
|
case "${{ matrix.target }}" in
|
|
*-linux-musl)
|
|
cp assets/openwrt/mhrv-rs.init dist/mhrv-rs.init
|
|
chmod +x dist/mhrv-rs.init
|
|
;;
|
|
*)
|
|
cp assets/launchers/run.sh dist/run.sh
|
|
chmod +x dist/run.sh
|
|
if [ "${{ runner.os }}" = "macOS" ]; then
|
|
cp assets/launchers/run.command dist/run.command
|
|
chmod +x dist/run.command
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
- name: Build macOS .app bundle
|
|
if: runner.os == 'macOS'
|
|
run: |
|
|
VER="${GITHUB_REF#refs/tags/v}"
|
|
./assets/macos/build-app.sh dist/mhrv-rs-ui "$VER" dist
|
|
# Make a clean zip of just the .app for the release
|
|
cd dist
|
|
zip -qry "${{ matrix.name }}-app.zip" mhrv-rs.app
|
|
|
|
- name: Package (windows)
|
|
if: runner.os == 'Windows'
|
|
shell: pwsh
|
|
run: |
|
|
New-Item -ItemType Directory -Force -Path dist | Out-Null
|
|
Copy-Item target/${{ matrix.target }}/release/mhrv-rs.exe dist/mhrv-rs.exe
|
|
if (Test-Path target/${{ matrix.target }}/release/mhrv-rs-ui.exe) {
|
|
Copy-Item target/${{ matrix.target }}/release/mhrv-rs-ui.exe dist/mhrv-rs-ui.exe
|
|
}
|
|
Copy-Item assets/launchers/run.bat dist/run.bat
|
|
|
|
- name: Make archive
|
|
shell: bash
|
|
run: |
|
|
cd dist
|
|
case "${{ matrix.target }}" in
|
|
*-pc-windows-*)
|
|
7z a -tzip "${{ matrix.name }}.zip" mhrv-rs.exe mhrv-rs-ui.exe run.bat
|
|
;;
|
|
*-apple-darwin)
|
|
tar czf "${{ matrix.name }}.tar.gz" mhrv-rs mhrv-rs-ui run.sh run.command
|
|
;;
|
|
*-linux-musl)
|
|
tar czf "${{ matrix.name }}.tar.gz" mhrv-rs mhrv-rs.init
|
|
;;
|
|
*)
|
|
tar czf "${{ matrix.name }}.tar.gz" mhrv-rs mhrv-rs-ui run.sh 2>/dev/null || tar czf "${{ matrix.name }}.tar.gz" mhrv-rs run.sh
|
|
;;
|
|
esac
|
|
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: ${{ matrix.name }}
|
|
path: |
|
|
dist/${{ matrix.name }}.tar.gz
|
|
dist/${{ matrix.name }}.zip
|
|
dist/${{ matrix.name }}-app.zip
|
|
if-no-files-found: ignore
|
|
|
|
# Android build — separate job so it doesn't inflate the matrix. The
|
|
# Rust side here cross-compiles to FOUR ABIs (arm64-v8a, armeabi-v7a,
|
|
# x86_64, x86) via cargo-ndk and drops the .so files into the Gradle
|
|
# project's jniLibs/ tree, which then packages them into a single
|
|
# universal APK. Users pick it once, no per-ABI split.
|
|
android:
|
|
runs-on: ubuntu-22.04
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Set up JDK 17
|
|
uses: actions/setup-java@v4
|
|
with:
|
|
distribution: temurin
|
|
java-version: 17
|
|
|
|
- name: Set up Android SDK
|
|
uses: android-actions/setup-android@v3
|
|
with:
|
|
cmdline-tools-version: 11076708
|
|
|
|
- name: Install NDK
|
|
run: |
|
|
yes | sdkmanager --install "ndk;26.1.10909125" >/dev/null
|
|
echo "ANDROID_NDK_HOME=$ANDROID_HOME/ndk/26.1.10909125" >> "$GITHUB_ENV"
|
|
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android,i686-linux-android
|
|
|
|
- name: Install cargo-ndk
|
|
run: cargo install cargo-ndk --locked
|
|
|
|
# `./gradlew :app:assembleRelease` triggers cargoBuildRelease first
|
|
# which invokes cargo-ndk with all four targets, then Gradle packages
|
|
# the APK (release buildType signed with the debug keystore — see
|
|
# android/app/build.gradle.kts comment explaining why).
|
|
- name: Build release APK
|
|
working-directory: android
|
|
run: |
|
|
chmod +x ./gradlew
|
|
./gradlew :app:assembleRelease --no-daemon --stacktrace
|
|
|
|
- name: Rename APK with version
|
|
working-directory: android
|
|
run: |
|
|
VER="${GITHUB_REF#refs/tags/v}"
|
|
SRC="app/build/outputs/apk/release/app-release.apk"
|
|
if [ ! -f "$SRC" ]; then
|
|
# Some AGP versions name it differently when the release config
|
|
# can't be auto-signed. Catch that up front with a clear error
|
|
# instead of a silent missing-artifact later.
|
|
echo "::error::expected $SRC to exist; actual outputs:"
|
|
find app/build/outputs/apk -type f -name '*.apk' -print
|
|
exit 1
|
|
fi
|
|
mkdir -p ../dist
|
|
cp "$SRC" "../dist/mhrv-rs-android-universal-v${VER}.apk"
|
|
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: mhrv-rs-android-universal
|
|
path: dist/*.apk
|
|
if-no-files-found: error
|
|
|
|
release:
|
|
needs: [build, android]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
path: dist
|
|
merge-multiple: true
|
|
|
|
- name: Release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
files: dist/*
|
|
generate_release_notes: true
|