v1.2.3: move CI to self-hosted runners + rust-cache

Linux / Android / mipsel build jobs now run on two self-hosted runners
on a Hetzner 8-core / 31 GB Ubuntu 24.04 box with Rust, Android SDK+NDK
r26c, all cross-compile toolchains and Docker pre-installed. macOS and
Windows still run on GitHub-hosted — we don't self-host those OSes and
the free minutes on a public repo are plenty.

Adds Swatinem/rust-cache@v2 to every cargo-using job so target/ + cargo
registry survive between runs. With warm caches the Linux jobs take
~1min each and the Android job ~3-4min; cold runs are ~9min for
Android and ~2min for everything else. Release wall time before this
change was ~13m consistently; it should now sit around 6-7m.

No new user-facing code in this release — primarily an infra change
exercised by an actual tag-push so we verify the full pipeline works
end-to-end from the new runners.
This commit is contained in:
therealaleph
2026-04-23 20:23:10 +03:00
parent e48a8f6add
commit 15e3e38745
5 changed files with 90 additions and 39 deletions
+78 -35
View File
@@ -8,26 +8,43 @@ on:
permissions:
contents: write
# Runner strategy:
# - Linux + Android + mipsel: self-hosted (mhrv-hetzner-*, Hetzner
# 8-core / 31 GB Ubuntu 24.04 box with
# Rust, Android SDK+NDK, Docker, all
# cross-compile toolchains pre-installed).
# Two runners registered for parallelism.
# - macOS arm64 + amd64, Windows: GitHub-hosted (we don't self-host those
# OSes; the free minutes on a public repo
# are plenty for those two platforms).
#
# Why self-hosted: GH-hosted 2-core runners were spending ~13 min cold per
# release; on the Hetzner box a cold linux-amd64 build is 1m9s, and warm
# builds with Swatinem/rust-cache are sub-minute. Keeps the toolchain warm,
# and more importantly keeps target/ warm via the rust-cache action.
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.
# Pin to Ubuntu 22.04 GLIBC target (GLIBC 2.35) so the glibc builds
# load on any distro ≥ Ubuntu 22.04 / Debian 12 / Mint 21 / Fedora 36.
# On self-hosted this is a Rust-side choice (cargo target triple),
# not an OS-of-the-runner choice — the runner itself is Ubuntu 24.04
# (GLIBC 2.39), but we link against the 2.35-era glibc via the
# x86_64-unknown-linux-gnu target triple which pins to the oldest
# GLIBC symbol version rustc is willing to emit. Users behind tight
# internet who can't dist-upgrade keep working.
- target: x86_64-unknown-linux-gnu
os: ubuntu-22.04
os: [self-hosted, linux, x64, mhrv-build]
name: mhrv-rs-linux-amd64
- target: aarch64-unknown-linux-gnu
os: ubuntu-22.04
os: [self-hosted, linux, x64, mhrv-build]
name: mhrv-rs-linux-arm64
- target: arm-unknown-linux-gnueabihf
os: ubuntu-22.04
os: [self-hosted, linux, x64, mhrv-build]
name: mhrv-rs-raspbian-armhf
- target: x86_64-apple-darwin
os: macos-latest
@@ -39,10 +56,10 @@ jobs:
os: windows-latest
name: mhrv-rs-windows-amd64
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
os: [self-hosted, linux, x64, mhrv-build]
name: mhrv-rs-linux-musl-amd64
- target: aarch64-unknown-linux-musl
os: ubuntu-latest
os: [self-hosted, linux, x64, mhrv-build]
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
@@ -53,7 +70,7 @@ jobs:
# `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
os: [self-hosted, linux, x64, mhrv-build]
name: mhrv-rs-openwrt-mipsel-softfloat
mipsel_softfloat: true
@@ -74,14 +91,30 @@ jobs:
# errors out with "error: component 'rust-std' for target
# 'mipsel-unknown-linux-musl' is unavailable for download", which
# fails the job before the docker step ever runs.
#
# On self-hosted this action is mostly a no-op: rustup is already
# installed and the standard target triples are pre-added. It
# still verifies the target is present and is cheap enough to keep
# as a safety net.
- uses: dtolnay/rust-toolchain@stable
if: matrix.mipsel_softfloat != true
with:
targets: ${{ matrix.target }}
# Cache target/ + cargo registry across runs — this is the big
# self-hosted speedup. Without it, actions/checkout@v4's default
# `git clean -ffdx` wipes target/ between runs and every build is
# cold. With it, warm builds are sub-minute even for the full
# release profile.
- uses: Swatinem/rust-cache@v2
if: matrix.mipsel_softfloat != true
with:
key: ${{ 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.
# and OpenGL/X11/Wayland. On self-hosted these persist across runs so this
# is a no-op after the first time; on GH-hosted macOS/Windows the step
# is guarded out anyway.
- name: Install Linux eframe system deps
if: runner.os == 'Linux'
run: |
@@ -279,38 +312,43 @@ jobs:
# 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.
#
# Runs on self-hosted. The runner has Android SDK + NDK r26c + cargo-ndk
# pre-installed under /opt/android-sdk; the env block below points Gradle
# at those paths so we don't re-download ~1 GB of SDK per release.
android:
runs-on: ubuntu-22.04
runs-on: [self-hosted, linux, x64, mhrv-build]
env:
ANDROID_SDK_ROOT: /opt/android-sdk
ANDROID_HOME: /opt/android-sdk
ANDROID_NDK_HOME: /opt/android-sdk/ndk/26.2.11394342
ANDROID_NDK_ROOT: /opt/android-sdk/ndk/26.2.11394342
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
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"
# Rust toolchain: idempotent on self-hosted (targets already present),
# kept here so the workflow still works if we ever run it on a GH-hosted
# fallback.
- 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
# Cache cargo + target/ across Android release builds. Four cargo-ndk
# release builds back-to-back with LTO is where the cold cost comes
# from; rust-cache brings warm runs down to ~34 min from ~9 min cold.
- uses: Swatinem/rust-cache@v2
with:
key: android-universal
# cargo-ndk writes into `target/<android-triple>/release/`, all
# four of which we want to cache.
workspaces: |
. -> target
# `./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).
# the APK (release buildType signed with the committed release.jks —
# see android/app/build.gradle.kts comment explaining why).
- name: Build release APK
working-directory: android
run: |
@@ -339,6 +377,11 @@ jobs:
path: dist/*.apk
if-no-files-found: error
# release + telegram: lightweight aggregation jobs kept on GH-hosted
# ubuntu-latest. They only download artifacts and call APIs — no build
# tooling needed, no benefit from moving to self-hosted, and keeping them
# off the self-hosted runners avoids contention with Linux build jobs from
# the next tag if two releases overlap.
release:
needs: [build, android]
runs-on: ubuntu-latest
Generated
+1 -1
View File
@@ -2186,7 +2186,7 @@ dependencies = [
[[package]]
name = "mhrv-rs"
version = "1.2.2"
version = "1.2.3"
dependencies = [
"base64 0.22.1",
"bytes",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "mhrv-rs"
version = "1.2.2"
version = "1.2.3"
edition = "2021"
description = "Rust port of MasterHttpRelayVPN -- DPI bypass via Google Apps Script relay with domain fronting"
license = "MIT"
+2 -2
View File
@@ -14,8 +14,8 @@ android {
applicationId = "com.therealaleph.mhrv"
minSdk = 24 // Android 7.0 — covers 99%+ of live devices.
targetSdk = 34
versionCode = 122
versionName = "1.2.2"
versionCode = 123
versionName = "1.2.3"
// Ship all four mainstream Android ABIs:
// - arm64-v8a — 95%+ of real-world Android phones since 2019
+8
View File
@@ -0,0 +1,8 @@
<!-- see docs/changelog/v1.1.0.md for the file format: Persian, then `---`, then English. -->
• زیرساخت CI: انتقال build Linux/Android/mipsel به runnerهای self-hosted اختصاصی (Hetzner، ۸ هسته / ۳۱ گیگ RAM، تمام toolchainها از قبل نصب). زمان build کامل رکورد از ~۱۳ دقیقه به کمتر از ~۷ دقیقه کاهش یافت. macOS و Windows همچنان روی runnerهای GitHub باقی می‌مانند
• افزودن Swatinem/rust-cache@v2 به همهٔ job های cargo — buildهای warm زیر یک دقیقه
• نسخهٔ سازندهٔ متصل‌کننده‌ها نیست؛ این release عمدتاً pipeline release جدید ما را exercise می‌کند تا مطمئن شویم همه چیز از runner خودمان سالم بیرون می‌آید
---
• CI infrastructure: Linux / Android / mipsel build jobs moved to dedicated self-hosted runners (Hetzner, 8-core / 31 GB, all toolchains pre-installed). Full release wall time dropped from ~13 min to under ~7 min. macOS and Windows continue to use GitHub-hosted runners
• Add Swatinem/rust-cache@v2 to every cargo job — warm builds are now sub-minute
• No new user-facing features; this release primarily exercises the new pipeline end-to-end to verify everything comes out clean from our own runners