diff --git a/desktop/README.md b/desktop/README.md index 30ec71b..7618e77 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -32,6 +32,12 @@ open desktop/src-tauri/target/release/bundle/macos/glchat.app Finder через AppleScript, поэтому из песочницы агента и из headless-окружения он падает (см. «Что не сделано»). +`open --args` из песочницы агента аргументы не доставляет (проверено на +macOS 27: и на бандле glchat, и на системном TextEdit — процесс запускается без +них; из обычного терминала то же самое работает). Поэтому `make desktop-run +ARGS='--minimized'` и замер свёрнутого окна в `scripts/desktop-perf.py` надо +запускать из обычного терминала, а не из песочницы. + Сборка релиза с артефактами автообновления использует ключ подписи: ```bash @@ -53,6 +59,7 @@ make desktop-build | `--minimized` | запуск свёрнутым в трей (используется автозапуском) | | `--settings` | сразу открыть страницу настроек обёртки | | `--autostart on\|off` | включить или выключить автозапуск при установке скриптом | +| `--update-now` | сразу проверить обновление и поставить его без диалога (тихое обновление) | ## Настройки @@ -87,6 +94,33 @@ make desktop-build `GET {instance}/updates/{target}/{arch}/{current_version}` (target: `darwin`/`windows`/`linux`, arch: `aarch64`/`x86_64`; см. `src/updates.rs`). +Найденное обновление показывается в интерфейсе, установка спрашивается +отдельным диалогом («во время звонка перезапуск без согласия недопустим»). +Для управляемых машин есть тихий режим — флаг `--update-now`: + +```bash +open -a /Applications/glchat.app --args --update-now # из обычного терминала +``` + +С этим флагом обёртка проверяет обновление сразу при старте (не через 30 секунд) +и ставит найденное без вопросов, после чего перезапускается; диалогов нет вовсе, +а если обновления нет — приложение просто работает дальше. По умолчанию флаг +выключен, поведение обычного запуска не меняется. Подпись артефакта проверяется +в обоих режимах: флаг убирает только вопрос пользователю, но не проверку +(docs/DECISIONS.md, D-077). + +В журнале (`~/Library/Logs/su.mhspx.glchat/glchat.log`) видны и версия запущенной +сборки, и ход обновления: + +``` +обёртка запущена: версия=0.1.0, адрес=https://gl.mhspx.su, окно=видимо, автозапуск=true +тихое обновление по флагу --update-now: проверка сразу при старте +доступно обновление 0.1.1 +тихое обновление по флагу: 0.1.0 → 0.1.1 +обновление 0.1.1 установлено, перезапуск +обёртка запущена: версия=0.1.1, … +``` + Манифест отдаёт сам инстанс (`internal/server/updates.go`), отдельная статика в Caddy не нужна: и встроенный Caddy профиля, и внешний прокси стенда проксируют весь домен на приложение, поэтому `/updates/...` доходит до него без правок @@ -145,6 +179,54 @@ curl -s https://gl.mhspx.su/updates/darwin/aarch64/0.1.0 | jq . подтверждение в диалоге (во время звонка перезапуск без согласия не делается) → подпись проверяется до установки, downgrade отклоняется. +### Локальная проверка «поверх старой версии» (без стенда) + +Установка обновления проверена локально на настоящем сервере инстанса: сборка +0.1.0 → манифест 0.1.1 → скачивание → проверка подписи → установка → перезапуск +на 0.1.1. Порядок действий (все данные — в каталоге репозитория, стенд не +затрагивается): + +```bash +# 1. артефакт текущей версии и приложение 0.1.0 (с ключом подписи) +make desktop-build-app +# 2. версия 0.1.1 в desktop/src-tauri/tauri.conf.json и Cargo.toml, затем: +make desktop-build-app +# 3. раскладка манифеста в свой каталог, ссылки — на локальный сервер +bash scripts/desktop-release.sh --artifact \ + desktop/src-tauri/target/release/bundle/macos/glchat.app.tar.gz \ + --version 0.1.1 --base-url http://127.0.0.1:8100 --dir /tmp/updates +# 4. настоящий сервер инстанса со своим каталогом обновлений и клиентом +LISTEN_ADDR=127.0.0.1:8099 DATA_DIR=/tmp/glchat-e2e/data WEB_ROOT=$PWD/web/dist \ + UPDATES_DIR=/tmp/updates DOMAIN=127.0.0.1:8099 TLS_ENABLED=false \ + APP_VERSION=0.1.0-local SESSION_PEPPER=… MASTER_KEY=… TOTP_ENCRYPTION_KEY=… \ + ./build/glchat +# 5. запуск 0.1.0 с тихим обновлением (адрес инстанса — локальный) +/Applications/glchat.app/Contents/MacOS/glchat-desktop \ + --instance http://127.0.0.1:8099 --update-now +``` + +Что нужно знать, повторяя это: + +- **локальный стенд — http, а `tauri-plugin-updater` по умолчанию требует + `https`** («The configured updater endpoint must use a secure protocol»), для + проверки сборку делают с временным флагом + `tauri build --config '{"plugins":{"updater":{"dangerousInsecureTransportProtocol":true}}}'`. + В релизной сборке этого флага быть не должно: боевой адрес — `https://`; +- **приложение должно лежать на пути без символических ссылок**: macOS-проверка + `tauri-utils` отвергает `current_exe()` с symlink-компонентом, поэтому + `/tmp/glchat-e2e/…` не годится (`/tmp` — ссылка на `/private/tmp`), а + `/private/tmp/glchat-e2e/…` годится. Тот же смысл у требования ставить + приложение в `/Applications`, а не запускать через ссылку; +- **`--update-now` обязателен**: без него установку спрашивают диалогом, а + кликнуть в headless-прогоне некому; +- диагностика в логе: `GET /updates/darwin/aarch64/0.1.0` в журнале сервера, + затем `доступно обновление 0.1.1` → `тихое обновление по флагу: 0.1.0 → 0.1.1` + → `обновление 0.1.1 установлено, перезапуск` → строка запуска с + `версия=0.1.1` и запрос манифеста уже для `0.1.1`; +- проверка подписи проверяется и «от обратного»: если испортить байт в + артефакте, установка обязана упасть с `The signature verification failed`, + а версия на диске — остаться прежней. + ## Уведомления и переход в канал Решение «показывать ли уведомление» принимает веб-клиент (упоминания и личные @@ -196,16 +278,21 @@ python3 scripts/desktop-perf.py --runs 3 # 3 холодных старта + ## Что не сделано (открытые пункты Фазы 6) -- **Автообновление end-to-end не проверено на реальном релизе**: манифест - отдаётся и разбирается (проверено на стенде), но установка «поверх старой - версии» требует публикации версии с бо́льшим номером — это действие оператора - (см. «Публикация релиза» выше). +- **Автообновление проверено локально «поверх старой версии»** (2026-09-26): + 0.1.0 → манифест 0.1.1 → скачивание → проверка подписи → установка → + перезапуск на 0.1.1, подробности и команды — «Локальная проверка» выше. + Открытым остаётся публикация реального релиза на стенде (номер версии + поднимает оператор) и то, что локальная проверка шла по http с временным + флагом `dangerousInsecureTransportProtocol` — на стенде канал https. - **Аватары в уведомлениях** (thumb/буква) и группировка — сейчас иконка приложения. - **Захват экрана и микрофон**: разрешения macOS выдаёт webview, отдельных экранов-инструкций у обёртки нет. - **Developer ID и нотаризация** macOS, EV/Trusted Signing для Windows — только в CI с платными сертификатами (ниже — точные команды). -- **`.dmg`**: собирается только из обычного терминала (Finder/AppleScript). +- **`.dmg`**: собирается из обычного терминала (Finder/AppleScript для раскладки + окна); в песочнице агента `hdiutil create` и AppleScript запрещены, но образ + **без оформления окна** собирается флагом `--sandbox-safe` к `bundle_dmg.sh` + (проверено 2026-09-26, см. ниже). - **Windows/Linux-бандлы**: конфигурация кросс-платформенная, но собирались и проверялись только macOS-arm64. @@ -213,9 +300,10 @@ python3 scripts/desktop-perf.py --runs 3 # 3 холодных старта + ### macOS: `.dmg` -`bundle_dmg.sh` монтирует образ (`hdiutil`) и управляет Finder через AppleScript, -поэтому из песочницы агента и из headless-сессии он падает. Из обычного -терминала на macOS: +`bundle_dmg.sh` создаёт образ через `hdiutil create` и раскладывает иконки через +AppleScript/Finder. Из песочницы агента и из headless-сессии обе операции +запрещены (`hdiutil create` → «Операция не разрешена», AppleScript → -10004). +Из обычного терминала на macOS: ```bash cd /Volumes/Samsung/projects/glchat @@ -223,6 +311,26 @@ make desktop-build # .app + .dmg + артефакты обнов ls -lh desktop/src-tauri/target/release/bundle/dmg/ ``` +Если терминала с графической сессией нет (CI, headless), тот же образ +собирается без оформления окна — тем же скриптом Tauri с флагом +`--sandbox-safe` (он пропускает AppleScript и `hdiutil create`): + +```bash +cd /Volumes/Samsung/projects/glchat +make desktop-build-app # .app и артефакты обновления +rm -rf /tmp/dmg-stage && mkdir -p /tmp/dmg-stage +cp -R desktop/src-tauri/target/release/bundle/macos/glchat.app /tmp/dmg-stage/ +cd desktop/src-tauri/target/release/bundle/dmg +bash bundle_dmg.sh --volname glchat --volicon icon.icns \ + --icon glchat.app 180 170 --app-drop-link 480 170 --hide-extension glchat.app \ + --sandbox-safe glchat_0.1.0_aarch64.dmg /tmp/dmg-stage +hdiutil verify glchat_0.1.0_aarch64.dmg +``` + +Такой `.dmg` монтируется и содержит `glchat.app` и ссылку `Applications`, но без +красивой раскладки иконок — для локальных проверок и CI этого достаточно, для +публикации лучше собрать обычным `make desktop-build`. + Артефакты без `.dmg` (для автообновления) собираются где угодно: `make desktop-build-app`.