mirror of
https://github.com/by-sonic/tglock.git
synced 2026-07-31 15:55:11 +03:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 96eb62b097 | |||
| 4ae755c6ae | |||
| 00d24a8647 | |||
| ef81d91ad1 | |||
| ed121d0968 | |||
| 2d2a9e9302 |
@@ -58,6 +58,40 @@ jobs:
|
|||||||
- name: Test
|
- name: Test
|
||||||
run: cargo test --all-targets
|
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 -maxdepth 6 -name 'TGLock.app'; exit 1; }
|
||||||
|
python3 scripts/verify_bundle_binary.py "$exe"
|
||||||
|
|
||||||
headless:
|
headless:
|
||||||
name: Headless CLI (no WebView, no Node)
|
name: Headless CLI (no WebView, no Node)
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
@@ -73,13 +107,13 @@ jobs:
|
|||||||
# libwebkit2gtk. It fails the moment anything drags the GUI back into the
|
# libwebkit2gtk. It fails the moment anything drags the GUI back into the
|
||||||
# headless build, which is the whole point of issues #10 and #17.
|
# headless build, which is the whole point of issues #10 and #17.
|
||||||
- name: Lint
|
- 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
|
- name: Test
|
||||||
run: cargo test --no-default-features --lib --bins
|
run: cargo test --no-default-features --features cli --lib --bins
|
||||||
|
|
||||||
- name: Build
|
- 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
|
- name: Start, advertise a proxy link and stop on SIGTERM
|
||||||
run: |
|
run: |
|
||||||
@@ -123,7 +157,7 @@ jobs:
|
|||||||
run: cargo check --locked
|
run: cargo check --locked
|
||||||
|
|
||||||
- name: Check the headless dependency graph too
|
- 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:
|
frontend:
|
||||||
name: Frontend
|
name: Frontend
|
||||||
|
|||||||
@@ -75,28 +75,34 @@ jobs:
|
|||||||
prerelease: false
|
prerelease: false
|
||||||
args: ${{ matrix.args }}
|
args: ${{ matrix.args }}
|
||||||
|
|
||||||
# Смотрит внутрь собранного бандла. v2.0.0-beta.2 опубликовался с зелёным
|
# Проверяет СОДЕРЖИМОЕ упакованного бинаря, а не его имя.
|
||||||
# CI, хотя .app и .deb содержали headless CLI вместо приложения: сборка
|
#
|
||||||
# была успешной, просто никто не проверял, что внутри.
|
# v2.0.0-beta.2 и v2.0.0-beta.3 опубликовались с зелёным CI, хотя в бандле
|
||||||
- name: The bundle must contain the GUI binary
|
# лежал 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'
|
if: runner.os == 'macOS'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
app=$(find target -maxdepth 6 -name 'TGLock.app' -type d | head -1)
|
app=$(find target -maxdepth 6 -name 'TGLock.app' -type d | head -1)
|
||||||
test -n "$app" || { echo "TGLock.app не найден"; exit 1; }
|
test -n "$app" || { echo "TGLock.app не найден"; exit 1; }
|
||||||
exe=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "$app/Contents/Info.plist")
|
exe="$app/Contents/MacOS/tglock"
|
||||||
echo "CFBundleExecutable = $exe"
|
test -f "$exe" || { echo "нет $exe:"; ls -la "$app/Contents/MacOS/"; exit 1; }
|
||||||
test "$exe" = "tglock" || { echo "в бандле не GUI-бинарь"; exit 1; }
|
python3 scripts/verify_bundle_binary.py "$exe"
|
||||||
test -x "$app/Contents/MacOS/tglock" || { echo "исполняемый файл отсутствует"; exit 1; }
|
|
||||||
|
|
||||||
- name: The bundle must contain the GUI binary
|
- name: The bundled binary must actually be the GUI one
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
deb=$(find target -maxdepth 6 -name '*.deb' | head -1)
|
deb=$(find target -maxdepth 6 -name '*.deb' | head -1)
|
||||||
test -n "$deb" || { echo ".deb не найден"; exit 1; }
|
test -n "$deb" || { echo ".deb не найден"; exit 1; }
|
||||||
dpkg-deb -c "$deb" | grep -E ' \./usr/bin/tglock$' \
|
root=$(mktemp -d)
|
||||||
|| { echo "в .deb нет /usr/bin/tglock:"; dpkg-deb -c "$deb" | grep '/bin/'; exit 1; }
|
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; }
|
||||||
|
python3 scripts/verify_bundle_binary.py "$exe"
|
||||||
|
|
||||||
cli:
|
cli:
|
||||||
name: Headless CLI ${{ matrix.platform }}
|
name: Headless CLI ${{ matrix.platform }}
|
||||||
@@ -136,7 +142,7 @@ jobs:
|
|||||||
IFS=',' read -ra targets <<< "${{ matrix.rust-targets }}"
|
IFS=',' read -ra targets <<< "${{ matrix.rust-targets }}"
|
||||||
binaries=()
|
binaries=()
|
||||||
for target in "${targets[@]}"; do
|
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"
|
--bin tglock-cli --target "$target"
|
||||||
binaries+=("target/$target/release/tglock-cli")
|
binaries+=("target/$target/release/tglock-cli")
|
||||||
done
|
done
|
||||||
@@ -152,7 +158,7 @@ jobs:
|
|||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cargo build --release --locked --no-default-features \
|
cargo build --release --locked --no-default-features --features cli \
|
||||||
--bin tglock-cli --target ${{ matrix.rust-targets }}
|
--bin tglock-cli --target ${{ matrix.rust-targets }}
|
||||||
cp "target/${{ matrix.rust-targets }}/release/tglock-cli.exe" "${{ matrix.asset }}"
|
cp "target/${{ matrix.rust-targets }}/release/tglock-cli.exe" "${{ matrix.asset }}"
|
||||||
./"${{ matrix.asset }}" --version
|
./"${{ matrix.asset }}" --version
|
||||||
|
|||||||
Generated
+1
-1
@@ -3553,7 +3553,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tglock"
|
name = "tglock"
|
||||||
version = "2.0.0-beta.3"
|
version = "2.0.0-beta.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"cipher",
|
"cipher",
|
||||||
|
|||||||
+11
-2
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tglock"
|
name = "tglock"
|
||||||
version = "2.0.0-beta.3"
|
version = "2.0.0-beta.5"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.88"
|
rust-version = "1.88"
|
||||||
description = "Telegram unblock via local WebSocket tunnel"
|
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
|
# 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.
|
# machines without a GPU or monitor able to build and run TGLock at all.
|
||||||
gui = ["dep:tauri", "dep:tauri-build", "dep:open"]
|
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]
|
[lib]
|
||||||
name = "tglock"
|
name = "tglock"
|
||||||
@@ -26,11 +34,12 @@ required-features = ["gui"]
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "tglock-cli"
|
name = "tglock-cli"
|
||||||
path = "src/bin/cli.rs"
|
path = "src/bin/cli.rs"
|
||||||
|
required-features = ["cli"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "2", features = [], optional = true }
|
tauri = { version = "2", features = [], optional = true }
|
||||||
open = { version = "5", 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 = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
tokio = { version = "1", features = [
|
tokio = { version = "1", features = [
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ TGLock — это **локальный прокси** на твоём компь
|
|||||||
3. **Собрать самому.** Для CLI это одна команда и никаких зависимостей кроме Rust:
|
3. **Собрать самому.** Для CLI это одна команда и никаких зависимостей кроме Rust:
|
||||||
|
|
||||||
```bash
|
```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
|
FROM rust:1.88 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY . .
|
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
|
FROM debian:bookworm-slim
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
|
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
|
||||||
@@ -435,7 +435,7 @@ npm run tauri build
|
|||||||
### Только CLI, без графики
|
### Только CLI, без графики
|
||||||
|
|
||||||
```bash
|
```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 собирается на голом сервере.
|
Ни Node.js, ни фронтенда, ни `libwebkit2gtk` для этого не нужно — при выключенной фиче `gui` Tauri и системный WebView в сборку не попадают вообще. Именно так CLI собирается на голом сервере.
|
||||||
@@ -445,9 +445,9 @@ cargo build --release --locked --no-default-features --bin tglock-cli
|
|||||||
```bash
|
```bash
|
||||||
cargo fmt --check
|
cargo fmt --check
|
||||||
cargo clippy --all-targets -- -D warnings
|
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 --all-targets
|
||||||
cargo test --no-default-features --lib --bins
|
cargo test --no-default-features --features cli --lib --bins
|
||||||
```
|
```
|
||||||
|
|
||||||
Тестов 59: разбор `obfuscated2`, каскад маршрутов и его cooldown, протокольные отказы SOCKS5, устойчивость секрета к перезапуску, плюс сквозной тест туннеля против мок-сервера, который реализует сторону Telegram и проверяет, что до неё доходит ровно тот открытый текст, который отправил клиент. Единственный тест с пометкой `#[ignore]` — тот, что требует живой сети.
|
Тестов 59: разбор `obfuscated2`, каскад маршрутов и его cooldown, протокольные отказы SOCKS5, устойчивость секрета к перезапуску, плюс сквозной тест туннеля против мок-сервера, который реализует сторону Telegram и проверяет, что до неё доходит ровно тот открытый текст, который отправил клиент. Единственный тест с пометкой `#[ignore]` — тот, что требует живой сети.
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "tglock-ui",
|
"name": "tglock-ui",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.0.0-beta.3",
|
"version": "2.0.0-beta.5",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --port 1420",
|
"dev": "vite --port 1420",
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Проверяет, что в бандл приложения попал GUI-бинарь, а не headless CLI.
|
||||||
|
|
||||||
|
Зачем это существует. В v2.0.0-beta.2 и v2.0.0-beta.3 бандлер Tauri упаковал
|
||||||
|
`tglock-cli` как исполняемый файл приложения, и оно не запускалось ни на одной
|
||||||
|
платформе. В beta.3 имя файла было уже правильным — бандлер переименовал CLI —
|
||||||
|
поэтому проверка имени или `CFBundleExecutable` ничего не заметила. Отличить
|
||||||
|
можно только по содержимому.
|
||||||
|
|
||||||
|
Почему на Python, а не grep. BSD grep на macOS в UTF-8-локали молча не находит
|
||||||
|
строки в бинарных данных там, где GNU grep находит: проверка проходила на
|
||||||
|
Linux и давала ложное «не GUI» на macOS. Один скрипт для всех платформ и для
|
||||||
|
всех трёх мест, где проверка вызывается, исключает подобные расхождения.
|
||||||
|
|
||||||
|
Использование:
|
||||||
|
python3 scripts/verify_bundle_binary.py <путь-к-бинарю>
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Присутствуют только в GUI: строка CSP из tauri.conf.json и имя движка WebView.
|
||||||
|
GUI_MARKERS = (b"ipc.localhost", b"wry")
|
||||||
|
# Присутствуют только в CLI: имена флагов clap.
|
||||||
|
CLI_MARKERS = (b"allow-direct", b"secret-file")
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str]) -> int:
|
||||||
|
if len(argv) != 2:
|
||||||
|
print(f"использование: {argv[0]} <путь-к-бинарю>", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
path = argv[1]
|
||||||
|
try:
|
||||||
|
with open(path, "rb") as handle:
|
||||||
|
data = handle.read()
|
||||||
|
except OSError as error:
|
||||||
|
print(f"не удалось прочитать {path}: {error}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
found_gui = [marker.decode() for marker in GUI_MARKERS if marker in data]
|
||||||
|
found_cli = [marker.decode() for marker in CLI_MARKERS if marker in data]
|
||||||
|
|
||||||
|
print(f"файл: {path}")
|
||||||
|
print(f"размер: {len(data) / 1048576:.1f} МБ")
|
||||||
|
print(f"GUI: {found_gui or 'признаков нет'}")
|
||||||
|
print(f"CLI: {found_cli or 'признаков нет'}")
|
||||||
|
|
||||||
|
if found_cli:
|
||||||
|
print("ОШИБКА: в бандле headless CLI вместо приложения", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
if not found_gui:
|
||||||
|
print("ОШИБКА: это не GUI-бинарь, признаков GUI нет", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print("OK: в бандле GUI-бинарь")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main(sys.argv))
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "TGLock",
|
"productName": "TGLock",
|
||||||
"version": "2.0.0-beta.3",
|
"version": "2.0.0-beta.5",
|
||||||
"identifier": "com.bysonic.tglock",
|
"identifier": "com.bysonic.tglock",
|
||||||
"mainBinaryName": "tglock",
|
"mainBinaryName": "tglock",
|
||||||
"build": {
|
"build": {
|
||||||
|
|||||||
Reference in New Issue
Block a user