3 Commits

Author SHA1 Message Date
by-sonic ef81d91ad1 Merge branch 'main' of https://github.com/by-sonic/tglock 2026-07-30 16:09:00 +03:00
Никита Sonic ed121d0968 fix(bundle): спрятать CLI за фичей — beta.2 и beta.3 упаковывали не тот бинарь (#33)
Мой предыдущий фикс (mainBinaryName в #31) фиксом не был. Он изменил только
имя выходного файла: бандлер по-прежнему брал headless CLI и просто
переименовывал его в tglock. Проверено по содержимому опубликованных
артефактов, а не по имени:

  версия  файл в MacOS/  размер  признаки CLI       признаки GUI
  beta.1  tglock         17.8 МБ  нет               ipc.localhost, wry×3206
  beta.2  tglock-cli      4.3 МБ  tglock-cli×6      нет
  beta.3  tglock          4.3 МБ  tglock-cli×6      нет

То есть beta.3 тоже не запускается, и моя же проверка CFBundleExecutable это
пропустила, потому что сверяла имя.

Настоящая причина найдена воспроизведением локально. Ломается только при явном
--target: без него бандлер выбирает GUI, с ним — CLI. Локальная сборка, на
которой я объявил фикс подтверждённым, шла без --target, а обе сборки в CI — с
ним. Обобщение было неправомерным.

Исправление убирает саму возможность выбора: CLI спрятан за фичей cli, которой
нет в default. При сборке приложения второго бинаря просто не существует.

Проверено против воспроизведённой поломки:
  до : tglock.exe 1.7 МБ, признаки CLI, GUI нет
  после: tglock.exe 9.0 МБ, признаки GUI, CLI нет, tglock-cli.exe не собран

Проверки переписаны на содержимое:
- release.yml распаковывает .app и .deb и ищет ipc.localhost (есть только в
  GUI) и allow-direct (есть только в CLI). Поймала бы и beta.2, и beta.3;
- новая задача CI bundle собирает бандл с явным --target aarch64-apple-darwin
  и проверяет его так же. Ловит до публикации, а не после;
- заодно исправлен шаблон grep для .deb: dpkg-deb -c выводит путь без ./, из-за
  чего проверка ложно падала на исправной сборке.

Команда сборки CLI теперь требует --features cli; обновлены README, ci.yml и
release.yml.

Co-authored-by: by-sonic <171230345+by-sonic@users.noreply.github.com>
2026-07-30 16:08:56 +03:00
by-sonic 2d2a9e9302 fix(bundle): спрятать CLI за фичей — beta.2 и beta.3 упаковывали не тот бинарь
Мой предыдущий фикс (mainBinaryName в #31) фиксом не был. Он изменил только
имя выходного файла: бандлер по-прежнему брал headless CLI и просто
переименовывал его в tglock. Проверено по содержимому опубликованных
артефактов, а не по имени:

  версия  файл в MacOS/  размер  признаки CLI       признаки GUI
  beta.1  tglock         17.8 МБ  нет               ipc.localhost, wry×3206
  beta.2  tglock-cli      4.3 МБ  tglock-cli×6      нет
  beta.3  tglock          4.3 МБ  tglock-cli×6      нет

То есть beta.3 тоже не запускается, и моя же проверка CFBundleExecutable это
пропустила, потому что сверяла имя.

Настоящая причина найдена воспроизведением локально. Ломается только при явном
--target: без него бандлер выбирает GUI, с ним — CLI. Локальная сборка, на
которой я объявил фикс подтверждённым, шла без --target, а обе сборки в CI — с
ним. Обобщение было неправомерным.

Исправление убирает саму возможность выбора: CLI спрятан за фичей cli, которой
нет в default. При сборке приложения второго бинаря просто не существует.

Проверено против воспроизведённой поломки:
  до : tglock.exe 1.7 МБ, признаки CLI, GUI нет
  после: tglock.exe 9.0 МБ, признаки GUI, CLI нет, tglock-cli.exe не собран

Проверки переписаны на содержимое:
- release.yml распаковывает .app и .deb и ищет ipc.localhost (есть только в
  GUI) и allow-direct (есть только в CLI). Поймала бы и beta.2, и beta.3;
- новая задача CI bundle собирает бандл с явным --target aarch64-apple-darwin
  и проверяет его так же. Ловит до публикации, а не после;
- заодно исправлен шаблон grep для .deb: dpkg-deb -c выводит путь без ./, из-за
  чего проверка ложно падала на исправной сборке.

Команда сборки CLI теперь требует --features cli; обновлены README, ci.yml и
release.yml.
2026-07-30 16:02:26 +03:00
7 changed files with 97 additions and 27 deletions
+45 -4
View File
@@ -58,6 +58,47 @@ jobs:
- name: Test
run: cargo test --all-targets
bundle:
name: Bundled app must be the GUI binary
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- name: Install Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: npm
- name: Install frontend dependencies
run: npm ci
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-darwin
# Явный --target обязателен: без него бандлер выбирал правильный бинарь, а
# с ним — нет, и именно так beta.2 и beta.3 уехали с headless CLI внутри.
- name: Bundle the app
run: npx tauri build --bundles app --target aarch64-apple-darwin
# Проверяется содержимое, а не имя файла. В beta.3 имя было уже
# правильным, потому что бандлер переименовал CLI, и проверка имени
# ничего не заметила.
- name: The bundled binary must actually be the GUI one
run: |
exe=$(find target -maxdepth 8 -path '*TGLock.app/Contents/MacOS/tglock' | head -1)
test -n "$exe" || { echo "исполняемый файл бандла не найден"; find target -name 'TGLock.app' -maxdepth 6; exit 1; }
ls -la "$exe"
if ! grep -qa 'ipc\.localhost' "$exe"; then
echo "в бандле не GUI: отсутствует признак ipc.localhost"; exit 1
fi
if grep -qa 'allow-direct' "$exe"; then
echo "в бандле оказался headless CLI: найден признак allow-direct"; exit 1
fi
echo "OK: в бандле GUI-бинарь"
headless:
name: Headless CLI (no WebView, no Node)
runs-on: ubuntu-22.04
@@ -73,13 +114,13 @@ jobs:
# libwebkit2gtk. It fails the moment anything drags the GUI back into the
# headless build, which is the whole point of issues #10 and #17.
- name: Lint
run: cargo clippy --no-default-features --lib --bins --all-targets -- -D warnings
run: cargo clippy --no-default-features --features cli --all-targets -- -D warnings
- name: Test
run: cargo test --no-default-features --lib --bins
run: cargo test --no-default-features --features cli --lib --bins
- name: Build
run: cargo build --release --no-default-features --bin tglock-cli
run: cargo build --release --no-default-features --features cli --bin tglock-cli
- name: Start, advertise a proxy link and stop on SIGTERM
run: |
@@ -123,7 +164,7 @@ jobs:
run: cargo check --locked
- name: Check the headless dependency graph too
run: cargo check --locked --no-default-features --lib --bins
run: cargo check --locked --no-default-features --features cli --lib --bins
frontend:
name: Frontend
+33 -13
View File
@@ -75,28 +75,48 @@ jobs:
prerelease: false
args: ${{ matrix.args }}
# Смотрит внутрь собранного бандла. v2.0.0-beta.2 опубликовался с зелёным
# CI, хотя .app и .deb содержали headless CLI вместо приложения: сборка
# была успешной, просто никто не проверял, что внутри.
- name: The bundle must contain the GUI binary
# Проверяет СОДЕРЖИМОЕ упакованного бинаря, а не его имя.
#
# v2.0.0-beta.2 и v2.0.0-beta.3 опубликовались с зелёным CI, хотя в бандле
# лежал headless CLI вместо приложения. В beta.3 имя файла было уже
# правильным — переименованным — поэтому проверка имени прошла. Отличить
# можно только по содержимому: `wry`/`ipc.localhost` есть исключительно в
# GUI, а `allow-direct` — исключительно в CLI.
- name: The bundled binary must actually be the GUI one
if: runner.os == 'macOS'
shell: bash
run: |
app=$(find target -maxdepth 6 -name 'TGLock.app' -type d | head -1)
test -n "$app" || { echo "TGLock.app не найден"; exit 1; }
exe=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "$app/Contents/Info.plist")
echo "CFBundleExecutable = $exe"
test "$exe" = "tglock" || { echo "в бандле не GUI-бинарь"; exit 1; }
test -x "$app/Contents/MacOS/tglock" || { echo "исполняемый файл отсутствует"; exit 1; }
exe="$app/Contents/MacOS/tglock"
test -f "$exe" || { echo "нет $exe:"; ls -la "$app/Contents/MacOS/"; exit 1; }
ls -la "$exe"
if ! grep -qa 'ipc\.localhost' "$exe"; then
echo "в бандле не GUI: отсутствует признак ipc.localhost"; exit 1
fi
if grep -qa 'allow-direct' "$exe"; then
echo "в бандле оказался headless CLI: найден признак allow-direct"; exit 1
fi
echo "OK: в бандле GUI-бинарь"
- name: The bundle must contain the GUI binary
- name: The bundled binary must actually be the GUI one
if: runner.os == 'Linux'
shell: bash
run: |
deb=$(find target -maxdepth 6 -name '*.deb' | head -1)
test -n "$deb" || { echo ".deb не найден"; exit 1; }
dpkg-deb -c "$deb" | grep -E ' \./usr/bin/tglock$' \
|| { echo "в .deb нет /usr/bin/tglock:"; dpkg-deb -c "$deb" | grep '/bin/'; exit 1; }
root=$(mktemp -d)
dpkg-deb -x "$deb" "$root"
exe="$root/usr/bin/tglock"
test -f "$exe" || { echo "нет /usr/bin/tglock:"; dpkg-deb -c "$deb" | grep '/bin/'; exit 1; }
ls -la "$exe"
if ! grep -qa 'ipc\.localhost' "$exe"; then
echo "в .deb не GUI: отсутствует признак ipc.localhost"; exit 1
fi
if grep -qa 'allow-direct' "$exe"; then
echo "в .deb оказался headless CLI: найден признак allow-direct"; exit 1
fi
echo "OK: в .deb GUI-бинарь"
cli:
name: Headless CLI ${{ matrix.platform }}
@@ -136,7 +156,7 @@ jobs:
IFS=',' read -ra targets <<< "${{ matrix.rust-targets }}"
binaries=()
for target in "${targets[@]}"; do
cargo build --release --locked --no-default-features \
cargo build --release --locked --no-default-features --features cli \
--bin tglock-cli --target "$target"
binaries+=("target/$target/release/tglock-cli")
done
@@ -152,7 +172,7 @@ jobs:
if: runner.os == 'Windows'
shell: bash
run: |
cargo build --release --locked --no-default-features \
cargo build --release --locked --no-default-features --features cli \
--bin tglock-cli --target ${{ matrix.rust-targets }}
cp "target/${{ matrix.rust-targets }}/release/tglock-cli.exe" "${{ matrix.asset }}"
./"${{ matrix.asset }}" --version
Generated
+1 -1
View File
@@ -3553,7 +3553,7 @@ dependencies = [
[[package]]
name = "tglock"
version = "2.0.0-beta.3"
version = "2.0.0-beta.4"
dependencies = [
"aes",
"cipher",
+11 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tglock"
version = "2.0.0-beta.3"
version = "2.0.0-beta.4"
edition = "2021"
rust-version = "1.88"
description = "Telegram unblock via local WebSocket tunnel"
@@ -13,6 +13,14 @@ default = ["gui"]
# frontend bundle from the build, which is what makes headless servers and
# machines without a GPU or monitor able to build and run TGLock at all.
gui = ["dep:tauri", "dep:tauri-build", "dep:open"]
# The headless binary. Deliberately NOT in `default`.
#
# When both binaries exist in one build, the Tauri bundler picks the wrong one
# as the application: `tauri build --target <triple>` packaged tglock-cli and
# shipped it as the app in v2.0.0-beta.2 and v2.0.0-beta.3. Keeping the CLI
# behind its own non-default feature means it simply does not exist during an
# application build, so there is nothing to pick wrongly.
cli = ["dep:clap"]
[lib]
name = "tglock"
@@ -26,11 +34,12 @@ required-features = ["gui"]
[[bin]]
name = "tglock-cli"
path = "src/bin/cli.rs"
required-features = ["cli"]
[dependencies]
tauri = { version = "2", features = [], optional = true }
open = { version = "5", optional = true }
clap = { version = "4", features = ["derive"] }
clap = { version = "4", features = ["derive"], optional = true }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = [
+5 -5
View File
@@ -122,7 +122,7 @@ TGLock — это **локальный прокси** на твоём компь
3. **Собрать самому.** Для CLI это одна команда и никаких зависимостей кроме Rust:
```bash
cargo build --release --locked --no-default-features --bin tglock-cli
cargo build --release --locked --no-default-features --features cli --bin tglock-cli
```
Полная сборка с интерфейсом — [ниже](#-сборка-из-исходников).
@@ -220,7 +220,7 @@ journalctl -u tglock -f
FROM rust:1.88 AS build
WORKDIR /src
COPY . .
RUN cargo build --release --locked --no-default-features --bin tglock-cli
RUN cargo build --release --locked --no-default-features --features cli --bin tglock-cli
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
@@ -435,7 +435,7 @@ npm run tauri build
### Только CLI, без графики
```bash
cargo build --release --locked --no-default-features --bin tglock-cli
cargo build --release --locked --no-default-features --features cli --bin tglock-cli
```
Ни Node.js, ни фронтенда, ни `libwebkit2gtk` для этого не нужно — при выключенной фиче `gui` Tauri и системный WebView в сборку не попадают вообще. Именно так CLI собирается на голом сервере.
@@ -445,9 +445,9 @@ cargo build --release --locked --no-default-features --bin tglock-cli
```bash
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo clippy --no-default-features --lib --bins --all-targets -- -D warnings
cargo clippy --no-default-features --features cli --all-targets -- -D warnings
cargo test --all-targets
cargo test --no-default-features --lib --bins
cargo test --no-default-features --features cli --lib --bins
```
Тестов 59: разбор `obfuscated2`, каскад маршрутов и его cooldown, протокольные отказы SOCKS5, устойчивость секрета к перезапуску, плюс сквозной тест туннеля против мок-сервера, который реализует сторону Telegram и проверяет, что до неё доходит ровно тот открытый текст, который отправил клиент. Единственный тест с пометкой `#[ignore]` — тот, что требует живой сети.
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "tglock-ui",
"private": true,
"version": "2.0.0-beta.3",
"version": "2.0.0-beta.4",
"type": "module",
"scripts": {
"dev": "vite --port 1420",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "TGLock",
"version": "2.0.0-beta.3",
"version": "2.0.0-beta.4",
"identifier": "com.bysonic.tglock",
"mainBinaryName": "tglock",
"build": {