mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-18 07:34:36 +03:00
54b3956e23
The `telegram:` job now runs only when the repo-level variable
`TELEGRAM_NOTIFY_ENABLED` is set to the literal string \"true\".
Default (unset) = job skips silently.
Turn on: gh variable set TELEGRAM_NOTIFY_ENABLED --body true
Turn off: gh variable set TELEGRAM_NOTIFY_ENABLED --body false
(or `gh variable delete TELEGRAM_NOTIFY_ENABLED`)
The Python script, secrets, and changelog format all stay in place
so re-enabling is a one-liner, not a workflow edit. Forks that don't
need Telegram don't have to touch anything — they get the clean
no-notify behaviour out of the box.
423 lines
17 KiB
YAML
423 lines
17 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
|
|
|
|
# Skip the host-level rustup install for mipsel-softfloat — that
|
|
# target is tier-3 in stable Rust (no prebuilt stdlib available
|
|
# via rustup), and the docker image we use for this build ships
|
|
# its own Rust toolchain + std. Trying to pass
|
|
# `targets: mipsel-unknown-linux-musl` to dtolnay/rust-toolchain
|
|
# 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.
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
if: matrix.mipsel_softfloat != true
|
|
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. messense doesn't publish a
|
|
# `:mipsel-musl-softfloat` tag — the mipsel-musl image is
|
|
# hardfloat. We build soft-float anyway via
|
|
# `RUSTFLAGS=-C target-feature=+soft-float` + `-Z build-std` so
|
|
# libstd itself is recompiled to emit soft-float code. The
|
|
# gcc/musl shipping in the image is hardfloat but we never link
|
|
# anything more than libc (`ring` is pure asm for the crypto
|
|
# that matters), so musl's lack of softfloat libm doesn't bite.
|
|
# Requires nightly Rust since mipsel is Rust tier 3 in the
|
|
# stable channel — no prebuilt 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 \
|
|
-e RUSTFLAGS='-C target-feature=+soft-float' \
|
|
messense/rust-musl-cross:mipsel-musl \
|
|
sh -c "set -eux; \
|
|
# The image ships with a pre-installed nightly that rustup \
|
|
# can't cleanly upgrade — the expected \`clippy-preview/share/doc/clippy/README.md\` \
|
|
# is missing, which fails the in-place upgrade \
|
|
# (error: failure removing component 'clippy-preview...'). \
|
|
# Nuke it first, then install fresh with only the profile \
|
|
# bits we actually use. \
|
|
rustup toolchain uninstall nightly 2>/dev/null || true; \
|
|
rustup toolchain install nightly --profile minimal; \
|
|
rustup component add rust-src --toolchain nightly; \
|
|
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
|
|
|
|
# Notify the Persian-speaking Telegram channel with the CI-built
|
|
# Android APK + its sha256 + the per-version changelog from
|
|
# `docs/changelog/v<tag>.md`.
|
|
#
|
|
# Two Telegram API calls:
|
|
# 1. sendDocument — APK file + a short caption (Telegram caps
|
|
# captions at 1024 chars, and we have bigger changelogs than
|
|
# that).
|
|
# 2. sendMessage — full changelog as a reply to #1, Persian
|
|
# quote-block first then English, same pattern as the
|
|
# previous manual post. No emojis, as the user asked.
|
|
#
|
|
# Needs two repo secrets:
|
|
# TELEGRAM_BOT_TOKEN — bot the channel admits as poster
|
|
# TELEGRAM_CHAT_ID — numeric chat id (starts with -100...)
|
|
# Missing either => the whole job is skipped (not failed) so a
|
|
# forker who hasn't set up a Telegram channel gets a clean release.
|
|
telegram:
|
|
needs: [android, release]
|
|
runs-on: ubuntu-latest
|
|
# Gated on the repo variable `TELEGRAM_NOTIFY_ENABLED`. Default is
|
|
# OFF — the job skips silently unless the variable is set to the
|
|
# literal string "true". Toggle via:
|
|
#
|
|
# gh variable set TELEGRAM_NOTIFY_ENABLED --body true
|
|
# gh variable set TELEGRAM_NOTIFY_ENABLED --body false
|
|
#
|
|
# Keeping the machinery (script + secrets) in place so flipping
|
|
# the switch back on is a one-liner, not a workflow edit.
|
|
if: ${{ vars.TELEGRAM_NOTIFY_ENABLED == 'true' && needs.android.result == 'success' }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: mhrv-rs-android-universal
|
|
path: apk
|
|
|
|
- name: Post to Telegram
|
|
env:
|
|
BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
|
CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
|
# Python over curl/bash so we don't have to fight curl's -F
|
|
# value-interpretation rules. curl treats `-F "caption=<..."`
|
|
# as "read the caption from file named ..." when the value
|
|
# starts with `<`, which matches our `<b>` HTML-bold tags and
|
|
# silently turns the whole job into a "file not found" exit
|
|
# 26. Python stdlib has no such wart.
|
|
run: |
|
|
set -euo pipefail
|
|
VER="${GITHUB_REF#refs/tags/v}"
|
|
APK="apk/mhrv-rs-android-universal-v${VER}.apk"
|
|
|
|
if [ -z "${BOT_TOKEN:-}" ] || [ -z "${CHAT_ID:-}" ]; then
|
|
echo "::notice::TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID not set, skipping Telegram post"
|
|
exit 0
|
|
fi
|
|
|
|
if [ ! -f "$APK" ]; then
|
|
echo "::error::expected $APK to exist; got:"
|
|
ls -la apk/
|
|
exit 1
|
|
fi
|
|
|
|
python3 .github/scripts/telegram_release_notify.py \
|
|
--apk "$APK" \
|
|
--version "$VER" \
|
|
--repo "$GITHUB_REPOSITORY" \
|
|
--changelog "docs/changelog/v${VER}.md"
|