Skip to content

Commit 4a512c0

Browse files
Merge remote-tracking branch 'origin/main' into feature/google-api
# Conflicts: # src/bin/ui.rs # src/config.rs
2 parents ced5cf1 + ce3030f commit 4a512c0

35 files changed

Lines changed: 1426 additions & 123 deletions

.github/scripts/telegram_publish_files.py

Lines changed: 550 additions & 0 deletions
Large diffs are not rendered by default.

.github/workflows/release.yml

Lines changed: 81 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,17 @@ jobs:
7777
- target: x86_64-pc-windows-gnu
7878
os: windows-latest
7979
name: mhrv-rs-windows-amd64
80-
- target: i686-pc-windows-msvc
81-
os: windows-latest
82-
name: mhrv-rs-windows-i686
83-
# Pin Rust 1.77.2 specifically for this target. Rust 1.78
84-
# (May 2024) raised the Windows MSRV from Win7 to Win10 by
85-
# switching std::time to GetSystemTimePreciseAsFileTime, a
86-
# kernel32 export that doesn't exist on Win7. The whole
87-
# reason this target ships is to support legacy Win7 32-bit
88-
# boxes (#272), so a stock-stable build defeats the purpose.
89-
# 1.77.2 is the last stable that produces a Win7-loadable
90-
# binary; other targets stay on @stable. (Fixes #318.)
91-
rust_toolchain: "1.77.2"
80+
# i686-pc-windows-msvc target was attempted in v1.7.7-v1.7.10
81+
# to support Windows 7 32-bit users (#272, #318). Removed in
82+
# v1.7.11 because keeping it on Rust 1.77.2 (last Win7-stable)
83+
# is fundamentally fragile: every transitive crate that bumps
84+
# its MSRV (e.g. `time` 0.3.47 needs Cargo manifest features
85+
# only available in Rust 1.78+) breaks the build, and pinning
86+
# transitives is brittle across releases. Win7 users should
87+
# self-build per the README; the project no longer ships a
88+
# prebuilt i686 Win7 binary. Replaced by the existing
89+
# x86_64-pc-windows-gnu (windows-amd64) which covers ~99% of
90+
# active Windows installs (incl. all WoA64 emulation).
9291
- target: x86_64-unknown-linux-musl
9392
os: [self-hosted, linux, x64, mhrv-build]
9493
name: mhrv-rs-linux-musl-amd64
@@ -112,12 +111,7 @@ jobs:
112111
# mipsel-softfloat is best-effort: the Rust tier-3 target occasionally
113112
# regresses. Letting it fail keeps the main release going so
114113
# desktop/Android users aren't blocked by MT7621 router support.
115-
# i686-pc-windows-msvc is similarly best-effort — pinned to Rust
116-
# 1.77.2 for Win7 compat (#318), so a future dep MSRV bump above
117-
# 1.77 will fail this one target. Letting it skip keeps the rest
118-
# of the release unblocked; we'd then choose between dropping the
119-
# target or moving to the tier-3 win7-msvc target with build-std.
120-
continue-on-error: ${{ matrix.mipsel_softfloat == true || matrix.target == 'i686-pc-windows-msvc' }}
114+
continue-on-error: ${{ matrix.mipsel_softfloat == true }}
121115

122116
steps:
123117
# Heal any root-owned leftovers from a previous mipsel docker
@@ -165,21 +159,6 @@ jobs:
165159
toolchain: ${{ matrix.rust_toolchain || 'stable' }}
166160
targets: ${{ matrix.target }}
167161

168-
# Cargo.lock from main is generated by stable Rust (1.78+ writes
169-
# version=4 lockfiles). Rust 1.77 only understands version=3, so
170-
# the i686 build with the pinned 1.77.2 toolchain bails with
171-
# "failed to parse lock file at: Cargo.lock" before it ever
172-
# touches our code. Drop the committed lockfile only on the pinned
173-
# job — cargo will regenerate it with version=3 against the same
174-
# Cargo.toml. We don't ship a `Cargo.lock` for binary reproducibility
175-
# contracts, so regenerating per-build is safe.
176-
- name: Regenerate Cargo.lock for older toolchain (Win7 i686 only)
177-
if: matrix.rust_toolchain == '1.77.2'
178-
shell: bash
179-
run: |
180-
rm -f Cargo.lock
181-
cargo +${{ matrix.rust_toolchain }} generate-lockfile
182-
183162
# Cache target/ + cargo registry across runs — this is the big
184163
# self-hosted speedup. Without it, actions/checkout@v4's default
185164
# `git clean -ffdx` wipes target/ between runs and every build is
@@ -619,10 +598,33 @@ jobs:
619598
with:
620599
fetch-depth: 0
621600

622-
- uses: actions/download-artifact@v4
623-
with:
624-
path: dist
625-
merge-multiple: true
601+
# `actions/download-artifact@v4` has been intermittently flaking on
602+
# this workflow with "5 retries exhausted" on a single artifact (~10
603+
# of 13). Wrap it in a manual retry — usually the second attempt
604+
# succeeds, the third nails any laggards. We use `gh run download`
605+
# against the current run so we don't depend on the release page
606+
# existing yet (it doesn't until the softprops step below runs).
607+
- name: Download all build artifacts (with retries)
608+
env:
609+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
610+
run: |
611+
mkdir -p dist
612+
for attempt in 1 2 3; do
613+
if gh run download "${GITHUB_RUN_ID}" --dir dist --repo "${GITHUB_REPOSITORY}"; then
614+
echo "downloaded all artifacts on attempt $attempt"
615+
# `gh run download` puts each artifact in its own subdir;
616+
# flatten so downstream steps that expect dist/<file> work
617+
# the same as `merge-multiple: true` did.
618+
find dist -type f -mindepth 2 -exec mv -f {} dist/ \;
619+
find dist -type d -empty -delete
620+
ls -la dist/
621+
exit 0
622+
fi
623+
echo "download attempt $attempt failed; retrying in 30s..."
624+
sleep 30
625+
done
626+
echo "::error::failed to download artifacts after 3 attempts"
627+
exit 1
626628
627629
# Compose the GitHub release body from `docs/changelog/v<ver>.md`
628630
# so the Releases page tells humans what actually changed —
@@ -826,43 +828,58 @@ jobs:
826828
# isn't gated by the same protection.
827829
git push origin HEAD:main
828830
829-
# Notify the Persian-speaking Telegram channel with the CI-built
830-
# Android APK + its sha256 + the per-version changelog from
831-
# `docs/changelog/v<tag>.md`.
831+
# ─────────── LEGACY — DORMANT BY DEFAULT ───────────
832832
#
833-
# Two Telegram API calls:
834-
# 1. sendDocument — APK file + a short caption (Telegram caps
835-
# captions at 1024 chars, and we have bigger changelogs than
836-
# that).
837-
# 2. sendMessage — full changelog as a reply to #1, Persian
838-
# quote-block first then English, same pattern as the
839-
# previous manual post. No emojis, as the user asked.
833+
# Posts the universal APK + per-version changelog to the **main**
834+
# Telegram channel as one big sendDocument + sendMessage pair.
840835
#
841-
# Needs two repo secrets:
842-
# TELEGRAM_BOT_TOKEN — bot the channel admits as poster
843-
# TELEGRAM_CHAT_ID — numeric chat id (starts with -100...)
844-
# Missing either => the whole job is skipped (not failed) so a
845-
# forker who hasn't set up a Telegram channel gets a clean release.
836+
# Superseded as of v1.8.0+ by `.github/workflows/telegram-publish-files.yml`,
837+
# which posts each platform's artifact individually to the **files**
838+
# channel (with SHA-256 captions) and then a single cross-link
839+
# message to the main channel pointing at the files-channel anchor.
840+
#
841+
# This job stays in the source tree, dormant, in case we ever want
842+
# to revert to the bundled-changelog-on-main-channel pattern (or
843+
# use both at once during a transition). To turn it back on:
844+
#
845+
# gh variable set TELEGRAM_NOTIFY_ENABLED --body true
846+
#
847+
# Note: with the new workflow active too, that produces TWO posts
848+
# to the main channel per release (the legacy APK+changelog *and*
849+
# the new cross-link). Pick one.
850+
#
851+
# Default state is disabled.
846852
telegram:
847853
needs: [android, release]
848854
runs-on: ubuntu-latest
849855
# Gated on the repo variable `TELEGRAM_NOTIFY_ENABLED`. Default is
850-
# OFF — the job skips silently unless the variable is set to the
851-
# literal string "true". Toggle via:
852-
#
853-
# gh variable set TELEGRAM_NOTIFY_ENABLED --body true
854-
# gh variable set TELEGRAM_NOTIFY_ENABLED --body false
855-
#
856-
# Keeping the machinery (script + secrets) in place so flipping
857-
# the switch back on is a one-liner, not a workflow edit.
856+
# off — the job skips silently unless the variable is set to the
857+
# literal string "true".
858858
if: ${{ vars.TELEGRAM_NOTIFY_ENABLED == 'true' && needs.android.result == 'success' }}
859859
steps:
860860
- uses: actions/checkout@v4
861861

862-
- uses: actions/download-artifact@v4
863-
with:
864-
name: mhrv-rs-android-universal
865-
path: apk
862+
# Same retry pattern as the `release` job above — `actions/download-artifact@v4`
863+
# has been flaking on this workflow with 5-retries-exhausted errors.
864+
- name: Download universal APK (with retries)
865+
env:
866+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
867+
run: |
868+
mkdir -p apk
869+
for attempt in 1 2 3; do
870+
if gh run download "${GITHUB_RUN_ID}" \
871+
--name mhrv-rs-android-universal \
872+
--dir apk \
873+
--repo "${GITHUB_REPOSITORY}"; then
874+
echo "downloaded universal APK on attempt $attempt"
875+
ls -la apk/
876+
exit 0
877+
fi
878+
echo "download attempt $attempt failed; retrying in 30s..."
879+
sleep 30
880+
done
881+
echo "::error::failed to download universal APK after 3 attempts"
882+
exit 1
866883
867884
- name: Post to Telegram
868885
env:
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
name: Telegram publish release files
2+
3+
# Posts every release artifact (Android APKs, Windows ZIP, macOS, Linux,
4+
# OpenWRT, Raspbian) to the Telegram channel as individual messages with
5+
# Persian captions and a #v<MAJOR><MINOR><PATCH> hashtag. Files larger
6+
# than the bot API's 50 MB ceiling are split into ~45 MB byte chunks
7+
# server-side and posted as `<name>.part_aa`, `.part_ab`, ... — recipients
8+
# reassemble with `cat <name>.part_* > <name>`.
9+
#
10+
# This workflow is decoupled from `release.yml` so it can be re-triggered
11+
# for any historical tag (e.g. to re-post v1.8.0 after a Telegram channel
12+
# wipe) without rebuilding artifacts. It downloads from the GitHub Release
13+
# page directly via `gh release download`, so the assets must already
14+
# exist there.
15+
16+
on:
17+
workflow_dispatch:
18+
inputs:
19+
version:
20+
description: 'Release tag to publish (with or without the v prefix, e.g. 1.8.0 or v1.8.0)'
21+
required: true
22+
type: string
23+
# Auto-trigger after a successful `release` workflow run. Posts files
24+
# to Telegram once the release page exists. The `head_branch` of the
25+
# triggering run is the tag name (e.g. `v1.8.0`) on tag-pushed releases,
26+
# which is what we feed `gh release download`.
27+
workflow_run:
28+
workflows: [release]
29+
types: [completed]
30+
31+
permissions:
32+
contents: read
33+
34+
jobs:
35+
publish:
36+
# Skip when triggered by a `release` run that didn't succeed — no
37+
# point posting half a release. Manual `workflow_dispatch` always
38+
# runs (the user explicitly asked for it).
39+
if: |
40+
github.event_name == 'workflow_dispatch'
41+
|| github.event.workflow_run.conclusion == 'success'
42+
runs-on: ubuntu-latest
43+
steps:
44+
- uses: actions/checkout@v4
45+
with:
46+
# Sparse checkout would be nicer but stock checkout is fast
47+
# enough for a 5 MB workflow file + ~200 KB script.
48+
fetch-depth: 1
49+
50+
- name: Resolve version + hashtag
51+
id: ver
52+
env:
53+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54+
run: |
55+
set -euo pipefail
56+
if [ -n "${{ inputs.version || '' }}" ]; then
57+
VER="${{ inputs.version }}"
58+
else
59+
# workflow_run path. `head_branch` for a tag-pushed release
60+
# workflow is the tag name (e.g. `v1.8.0`).
61+
VER="${{ github.event.workflow_run.head_branch || '' }}"
62+
fi
63+
if [ -z "$VER" ]; then
64+
echo "::error::could not determine version from inputs or workflow_run trigger"
65+
exit 1
66+
fi
67+
# Strip the leading `v` if present.
68+
VER="${VER#v}"
69+
# Hashtag: `#v` + version with dots removed. So 1.8.0 → #v180,
70+
# 1.8.10 → #v1810, 2.0.0 → #v200. Predictable across releases.
71+
HASHTAG="#v$(echo "$VER" | tr -d '.')"
72+
echo "version=$VER" >> "$GITHUB_OUTPUT"
73+
echo "hashtag=$HASHTAG" >> "$GITHUB_OUTPUT"
74+
echo "Resolved: version=$VER hashtag=$HASHTAG"
75+
76+
- name: Download release assets
77+
env:
78+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
79+
run: |
80+
set -euo pipefail
81+
mkdir -p assets
82+
# Mirror the retry pattern from `release.yml`'s download step —
83+
# GitHub's release-asset CDN occasionally times out on cold
84+
# tags. Three attempts with 30 s backoff covers most flakes.
85+
for attempt in 1 2 3; do
86+
if gh release download "v${{ steps.ver.outputs.version }}" \
87+
--dir assets \
88+
--repo "${GITHUB_REPOSITORY}"; then
89+
echo "downloaded release assets on attempt $attempt"
90+
ls -la assets/
91+
exit 0
92+
fi
93+
echo "attempt $attempt failed; retrying in 30s..."
94+
sleep 30
95+
done
96+
echo "::error::failed to download release assets after 3 attempts"
97+
exit 1
98+
99+
- name: Publish files to Telegram channel
100+
env:
101+
BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
102+
# The files channel — supergroup-style negative ID, hard-coded
103+
# rather than templated as a repo variable because there's only
104+
# ever one of these and putting it in source makes the workflow
105+
# auditable. The bot token already has post permissions there.
106+
CHAT_ID: '-1003966234444'
107+
# The main announcement channel. Receives a single cross-link
108+
# message per release pointing at the file-channel anchor post,
109+
# instead of the previous behaviour of attaching the universal
110+
# APK + full changelog. Sourced from the same secret the
111+
# legacy `telegram` job in release.yml used.
112+
MAIN_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
113+
# Public-username form of the files channel link. Used for
114+
# both (a) the post-link in the main-channel cross-post — so
115+
# `t.me/<name>/<msg>` works for everyone, not just members
116+
# via `t.me/c/<id>/<msg>` — and (b) one of the two
117+
# channel-join links rendered at the bottom of the cross-post.
118+
# Defaults to `mhrv_rs` (current public username); override via
119+
# repo variable if the channel is renamed.
120+
FILES_CHANNEL_USERNAME: ${{ vars.FILES_CHANNEL_USERNAME || 'mhrv_rs' }}
121+
# `t.me/+<hash>` invite link for the files channel. Rendered
122+
# as the second channel-join option in the main-channel
123+
# cross-post — the only join path that works for users coming
124+
# from outside Telegram search (private/restricted channels)
125+
# or whose Telegram client doesn't resolve usernames cleanly.
126+
# Override via repo variable if the channel's invite hash is
127+
# rotated.
128+
FILES_CHANNEL_INVITE: ${{ vars.FILES_CHANNEL_INVITE || 'https://t.me/+R1OyoHX2boA1ZDgx' }}
129+
run: |
130+
if [ -z "${BOT_TOKEN:-}" ]; then
131+
echo "::error::TELEGRAM_BOT_TOKEN not set; can't publish"
132+
exit 1
133+
fi
134+
python3 .github/scripts/telegram_publish_files.py \
135+
--assets-dir assets \
136+
--version "${{ steps.ver.outputs.version }}" \
137+
--hashtag "${{ steps.ver.outputs.hashtag }}"

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mhrv-rs"
3-
version = "1.7.10"
3+
version = "1.8.1"
44
edition = "2021"
55
description = "Rust port of MasterHttpRelayVPN -- DPI bypass via Google Apps Script relay with domain fronting"
66
license = "MIT"

android/app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ android {
1414
applicationId = "com.therealaleph.mhrv"
1515
minSdk = 24 // Android 7.0 — covers 99%+ of live devices.
1616
targetSdk = 34
17-
versionCode = 155
18-
versionName = "1.7.10"
17+
versionCode = 158
18+
versionName = "1.8.1"
1919

2020
// Ship all four mainstream Android ABIs:
2121
// - arm64-v8a — 95%+ of real-world Android phones since 2019

0 commit comments

Comments
 (0)