fix(ci): одна проверка бандла на Python вместо трёх копий grep (#34)

* 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.

* fix(ci): одна проверка бандла на Python вместо трёх копий grep

Проверка содержимого бандла, добавленная в #33, работала на Linux и давала
ложное «в бандле не GUI» на macOS. Причина в BSD grep: в UTF-8-локали он молча
не находит строки в бинарных данных там, где GNU grep находит. Из-за этого
задача macOS в релизе beta.4 упала уже после загрузки артефактов, а вместе с
ней снова пропустилась задача с CLI-бинарями.

Сами артефакты beta.4 при этом корректны — проверено скачиванием: в бандле
tglock на 20.3 МБ, universal/fat, признаки GUI на месте, признаков CLI нет.
Не хватает только tglock-cli-*.

Проверка вынесена в scripts/verify_bundle_binary.py и вызывается из всех трёх
мест: задачи CI bundle, проверки macOS и проверки Linux в релизе. Один скрипт
вместо трёх копий шелл-кода исключает и платформенные различия grep, и
расхождение копий между собой.

Скрипт проверен на реальных исторических артефактах:
  beta.4 (исправный)  → код 0
  beta.3 (сломанный)  → код 1, найдены allow-direct и secret-file
  beta.2 (сломанный)  → код 1, то же

---------

Co-authored-by: by-sonic <171230345+by-sonic@users.noreply.github.com>
This commit is contained in:
Никита Sonic
2026-07-30 16:26:32 +03:00
committed by GitHub
parent ed121d0968
commit 4ae755c6ae
7 changed files with 68 additions and 29 deletions
+60
View File
@@ -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))