diff --git a/README.md b/README.md index ab34a8a..d61d277 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ Chezmoi использует отдельный source dir `chezmoi/` внутр | `wezterm` | Общий для Linux и Windows конфиг: одинаковые оформление и горячие клавиши | | `kitty` | `kitty.conf` и тема; `~/.config/kitty` остаётся настоящим каталогом, чтобы файлы kitty не попадали в репозиторий | | `vscode` | `server-env-setup` для VS Code Server в WSL: секреты + авто-прокси | +| `chatgpt` | Отдельный ярлык `ChatGPT Proxy` с запретом прямого запуска при ошибке | `install.sh` дополнительно настраивает opencode (симлинки, MCP-конфиг), claude (симлинкает `settings.local.json` из репозитория) и подключает bash_custom. @@ -128,6 +129,15 @@ PowerShell и VS Code: одного общего файла у Windows и WSL н поправь и копии. Расхождение ловит `tests/test_proxy_env_sync.py` — раньше копии разъезжались, и один и тот же хост то шёл мимо прокси, то нет. +На Linux Stow-пакет `chatgpt` добавляет отдельный пользовательский ярлык +`ChatGPT Proxy`. Штатный `ChatGPT` остаётся прямым. Прокси-ярлык получает +активные настройки от `proxy-client`, передаёт Chromium явные параметры прокси +и сохраняет proxy-переменные для дочерних процессов. Повторный запуск поднимает +окно уже работающего проксированного экземпляра. Если ChatGPT открыт напрямую +или прокси недоступен, новый режим не подмешивается: GNOME показывает +уведомление с инструкцией. Файлы Debian-пакета не меняются, поэтому настройка +переживает его обновление. + ### Git baseline На всех платформах фиксируются настройки для предсказуемого поведения: @@ -197,6 +207,8 @@ PowerShell и VS Code: одного общего файла у Windows и WSL н | `~/.local/bin/start-claude-proxy.ps1` | Скрипт запуска Claude Desktop для Windows с переменными прокси и параметрами Electron | | `~/.local/bin/start-claude-proxy.vbs` | Скрытый запуск PowerShell-скрипта без привязки Claude к видимому окну терминала | | `~/.local/bin/start-codex-proxy.ps1` | Запуск Codex для Windows через системный идентификатор; прокси Electron задаётся параметрами, а `codex.exe` читает `~/.codex/.env` | +| `~/.local/bin/start-chatgpt-proxy` | Запуск Linux-приложения ChatGPT через активную настройку `proxy-client` | +| `~/.local/share/applications/chatgpt-proxy.desktop` | Отдельный Linux-ярлык `ChatGPT Proxy`, не подменяющий штатный `ChatGPT` | | `~/.config/powershell/profile.extra.ps1` | Локальные дополнения к pwsh-профилю | | `~/.config/powershell/profile.d/*.ps1` | Фрагменты, загружаемые по алфавиту | | `~/.claude/settings.json` | Управляется Claude Code: `model`, `effortLevel`, «always allow» permissions | diff --git a/chatgpt/.local/bin/start-chatgpt-proxy b/chatgpt/.local/bin/start-chatgpt-proxy new file mode 100755 index 0000000..3c68bfa --- /dev/null +++ b/chatgpt/.local/bin/start-chatgpt-proxy @@ -0,0 +1,134 @@ +#!/usr/bin/env bash +set -euo pipefail + +CHATGPT_BIN="${CHATGPT_BIN:-/usr/bin/chatgpt}" +CHATGPT_PROCESS_NAME="${CHATGPT_PROCESS_NAME:-ChatGPT}" +PROC_ROOT="${PROC_ROOT:-/proc}" +PROXY_CLIENT_SHELL="${PROXY_CLIENT_SHELL:-${XDG_CONFIG_HOME:-$HOME/.config}/proxy-client/shell.bash}" + +fail_with_notification() { + local message="$1" + printf 'Ошибка: %s\n' "$message" >&2 + if command -v notify-send >/dev/null 2>&1; then + notify-send \ + --app-name='ChatGPT Proxy' \ + --icon=chatgpt \ + 'ChatGPT Proxy' \ + "$message" \ + >/dev/null 2>&1 || true + fi + exit 1 +} + +chromium_bypass_list() { + local source_list="$1" item result='' + local -a items=() + IFS=',' read -r -a items <<< "$source_list" + + for item in "${items[@]}"; do + item="${item#"${item%%[![:space:]]*}"}" + item="${item%"${item##*[![:space:]]}"}" + [ -n "$item" ] || continue + case "$item" in + .*) item="*$item" ;; + esac + if [ -n "$result" ]; then + result+=';' + fi + result+="$item" + done + + printf '%s\n' "$result" +} + +detect_existing_chatgpt() { + local pgrep_output pgrep_status pid cmdline_text + local is_main has_proxy_server has_proxy_bypass + + if pgrep_output="$(pgrep -x "$CHATGPT_PROCESS_NAME" 2>/dev/null)"; then + pgrep_status=0 + else + pgrep_status=$? + fi + + case "$pgrep_status" in + 0) ;; + 1) + printf '%s\n' none + return 0 + ;; + *) return 1 ;; + esac + + while IFS= read -r pid; do + [ -n "$pid" ] || continue + [ -r "$PROC_ROOT/$pid/cmdline" ] || continue + is_main=1 + has_proxy_server=0 + has_proxy_bypass=0 + + cmdline_text="$(tr '\0' ' ' < "$PROC_ROOT/$pid/cmdline")" + case " $cmdline_text " in + *" --type="*) is_main=0 ;; + esac + case " $cmdline_text " in + *" --proxy-server=$HTTP_PROXY "*) has_proxy_server=1 ;; + esac + case " $cmdline_text " in + *" --proxy-bypass-list=$proxy_bypass_list "*) has_proxy_bypass=1 ;; + esac + + [ "$is_main" -eq 1 ] || continue + if [ "$has_proxy_server" -eq 1 ] && [ "$has_proxy_bypass" -eq 1 ]; then + printf '%s\n' proxied + else + printf '%s\n' direct + fi + return 0 + done <<< "$pgrep_output" + + printf '%s\n' none +} + +if [ ! -x "$CHATGPT_BIN" ]; then + fail_with_notification "ChatGPT не найден: $CHATGPT_BIN" +fi + +if [ ! -r "$PROXY_CLIENT_SHELL" ]; then + fail_with_notification "Модуль proxy-client не найден: $PROXY_CLIENT_SHELL" +fi + +if [ -z "${PROXY_CLIENT_BIN:-}" ] && [ -x "$HOME/.local/bin/proxy-client" ]; then + PROXY_CLIENT_BIN="$HOME/.local/bin/proxy-client" + export PROXY_CLIENT_BIN +fi + +# shellcheck source=/dev/null +source "$PROXY_CLIENT_SHELL" + +if ! proxy_on >/dev/null; then + fail_with_notification 'Прокси недоступен. Проверьте его командой: proxy-client status' +fi + +if [ -z "${HTTP_PROXY:-}" ] || [ -z "${NO_PROXY:-}" ]; then + fail_with_notification 'proxy-client вернул неполные настройки прокси.' +fi + +proxy_bypass_list="$(chromium_bypass_list "$NO_PROXY")" + +if ! command -v pgrep >/dev/null 2>&1; then + fail_with_notification 'Не удалось проверить запущенные процессы: команда pgrep не найдена.' +fi + +if ! existing_chatgpt="$(detect_existing_chatgpt)"; then + fail_with_notification 'Не удалось проверить, запущен ли ChatGPT.' +fi + +if [ "$existing_chatgpt" = direct ]; then + fail_with_notification 'ChatGPT уже работает без прокси. В системном лотке выберите «Quit ChatGPT», затем снова откройте «ChatGPT Proxy». Кнопка «Перезапустить» сохраняет прямой режим.' +fi + +exec "$CHATGPT_BIN" \ + "--proxy-server=$HTTP_PROXY" \ + "--proxy-bypass-list=$proxy_bypass_list" \ + "$@" diff --git a/chatgpt/.local/share/applications/chatgpt-proxy.desktop b/chatgpt/.local/share/applications/chatgpt-proxy.desktop new file mode 100644 index 0000000..84eaabb --- /dev/null +++ b/chatgpt/.local/share/applications/chatgpt-proxy.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Name=ChatGPT Proxy +Comment=ChatGPT через настроенный proxy-client +GenericName=AI assistant +Exec=start-chatgpt-proxy %U +TryExec=start-chatgpt-proxy +Icon=chatgpt +Type=Application +Terminal=false +StartupNotify=true +Categories=Utility; +Keywords=OpenAI;ChatGPT;Proxy; diff --git a/docs/specs/2026-08-17-chatgpt-proxy-launcher-design.md b/docs/specs/2026-08-17-chatgpt-proxy-launcher-design.md new file mode 100644 index 0000000..f77e13e --- /dev/null +++ b/docs/specs/2026-08-17-chatgpt-proxy-launcher-design.md @@ -0,0 +1,91 @@ +# Отдельный запуск ChatGPT через прокси + +## Проблема + +Linux-приложение ChatGPT не применяет обычные proxy-переменные ко всему +Chromium-трафику и при штатном запуске выходит в интернет напрямую. Правка +файлов Debian-пакета недолговечна: обновление приложения их заменит. + +## Цели + +- Дать отдельный и явно названный ярлык `ChatGPT Proxy`. +- Использовать активные настройки и проверку доступности `proxy-client`. +- Не допускать тихого запуска напрямую при ошибке прокси. +- Сохранить штатный ярлык `ChatGPT` для прямого запуска. +- Переживать обновления Debian-пакета ChatGPT. + +## Не входит в задачу + +- Изменение системного прокси GNOME. +- Перенаправление трафика других приложений. +- Изменение файлов в `/usr/lib/chatgpt` и `/usr/share/applications`. +- Автоматический запуск или переключение прокси. + +## Рассмотренные варианты + +- Подменить штатный пользовательский ярлык. Удобно для обязательного прокси, + но стирает различие между прямым и проксированным запуском. +- Добавить отдельный пользовательский ярлык. Оба режима видны явно, а файлы + приложения остаются нетронутыми. +- Включить системный прокси GNOME. Решение переживает обновления, но меняет + поведение всех приложений рабочего стола. +- Перенаправлять трафик на сетевом уровне. Это даёт более жёсткую изоляцию, но + заметно усложняет личную конфигурацию. +- Оставить всё как есть. Тогда пользователь должен каждый раз помнить о + прямом соединении ChatGPT. + +## Решение + +В dotfiles хранится отдельный пользовательский ярлык `ChatGPT Proxy` и его +запускатель. Штатный `ChatGPT` остаётся прямым аналогично паре `codex` и +`pcodex`. + +Запускатель получает окружение от `proxy-client`, а для сетевого слоя Chromium +добавляет явные параметры сервера и списка обхода. Если прокси недоступен, +ChatGPT не запускается, а GNOME показывает уведомление с причиной и командой +для диагностики. + +Если ChatGPT уже работает через тот же прокси, повторный запуск передаёт ему +запрос: Electron поднимает существующее окно. Если работает прямой экземпляр +или экземпляр с другими настройками прокси, запуск отклоняется. Уведомление +предлагает полностью завершить ChatGPT через системный лоток. Встроенный +перезапуск для обновления сохраняет режим, в котором приложение было открыто. + +Ярлык и запускатель устанавливаются в пользовательские каталоги через Stow. +Они не заменяют одноимённые файлы Debian-пакета, поэтому обновление ChatGPT их +не затрагивает. + +## Отклонённые решения + +- Подмена штатного ярлыка отклонена: отдельное имя лучше показывает выбранный + режим и не скрывает возможность прямого запуска. +- Системный прокси отклонён из-за слишком широкой области действия. +- Сетевое перенаправление отложено как избыточное для одного приложения. +- Переход на прямое соединение при ошибке запрещён: имя `ChatGPT Proxy` должно + быть гарантией, а не пожеланием. + +## Риски и защита + +- Уже работающий прямой процесс не примет новые параметры. Запускатель отличает + его от экземпляра с теми же параметрами прокси и отказывает только при + конфликте режимов. +- Одних proxy-переменных может быть недостаточно для Chromium. Запускатель + передаёт параметры прокси явно и одновременно сохраняет окружение для + дочерних процессов. +- Ошибка графического запуска может остаться незаметной. Причина выводится в + stderr и дублируется уведомлением GNOME. +- Формат списка обхода отличается у `NO_PROXY` и Chromium. Запускатель явно + преобразует список перед запуском приложения. + +## Проверка + +- При доступном прокси ChatGPT получает proxy-переменные и параметры Chromium. +- Адреса из `NO_PROXY` попадают в список обхода Chromium. +- При недоступном прокси приложение не запускается и появляется уведомление. +- При повторном прокси-запуске поднимается окно уже работающего проксированного + экземпляра. +- При работающем прямом экземпляре появляется уведомление с инструкцией + завершить его через системный лоток. +- Штатный системный ярлык не меняется. +- Повторное применение dotfiles восстанавливает пользовательский ярлык после + обновления Debian-пакета. diff --git a/install.sh b/install.sh index c2e2d3a..d114d36 100755 --- a/install.sh +++ b/install.sh @@ -370,7 +370,7 @@ if ! command -v "$STOW_BIN" &>/dev/null; then exit 1 fi -PACKAGES=(bash tmux vim wezterm kitty vscode agents codex gemini proxy-client) +PACKAGES=(bash tmux vim wezterm kitty vscode agents codex gemini proxy-client chatgpt) AVAILABLE_PACKAGES=() info "Применяю stow пакеты..." @@ -392,6 +392,14 @@ if [ -d "$HOME/.local" ] && [ ! -L "$HOME/.local" ] \ && [ ! -L "$HOME/.local/bin" ]; then mkdir -p "$HOME/.local/bin" fi +if [ -d "$HOME/.local" ] && [ ! -L "$HOME/.local" ] \ + && [ ! -L "$HOME/.local/share" ]; then + mkdir -p "$HOME/.local/share" +fi +if [ -d "$HOME/.local/share" ] && [ ! -L "$HOME/.local/share" ] \ + && [ ! -L "$HOME/.local/share/applications" ]; then + mkdir -p "$HOME/.local/share/applications" +fi if [ ! -L "$HOME/.config" ]; then mkdir -p "$HOME/.config" diff --git a/tests/test_chatgpt_proxy_launcher.sh b/tests/test_chatgpt_proxy_launcher.sh new file mode 100755 index 0000000..dd5a80c --- /dev/null +++ b/tests/test_chatgpt_proxy_launcher.sh @@ -0,0 +1,193 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +LAUNCHER="$REPO_ROOT/chatgpt/.local/bin/start-chatgpt-proxy" +DESKTOP_FILE="$REPO_ROOT/chatgpt/.local/share/applications/chatgpt-proxy.desktop" +SHELL_MODULE="$REPO_ROOT/proxy-client/.config/proxy-client/shell.bash" +TEST_ROOT="$(mktemp -d /tmp/chatgpt-proxy-launcher-test.XXXXXX)" +trap 'rm -rf "$TEST_ROOT"' EXIT + +pass_count=0 + +fail() { + echo "FAIL: $*" >&2 + exit 1 +} + +ok() { + pass_count=$((pass_count + 1)) + echo "ok - $*" +} + +assert_contains() { + local file="$1" expected="$2" message="$3" + grep -Fq -- "$expected" "$file" || fail "$message" +} + +assert_not_exists() { + local path="$1" message="$2" + [ ! -e "$path" ] || fail "$message" +} + +setup_fixture() { + local fixture="$1" + mkdir -p "$fixture/bin" "$fixture/home" "$fixture/proc" "$fixture/results" + + cat > "$fixture/bin/pgrep" <<'EOF' +#!/usr/bin/env bash +if [ "${FAKE_PGREP_STATUS:-1}" = 0 ]; then + printf '%s\n' "${FAKE_PGREP_PIDS:-4242}" +fi +exit "${FAKE_PGREP_STATUS:-1}" +EOF + + cat > "$fixture/bin/notify-send" <<'EOF' +#!/usr/bin/env bash +printf '<%s>\n' "$@" >> "$RESULTS_DIR/notifications" +EOF + + cat > "$fixture/bin/proxy-client" <<'EOF' +#!/usr/bin/env bash +printf 'called\n' >> "$RESULTS_DIR/proxy-calls" +if [ "${FAKE_PROXY_FAIL:-0}" = 1 ]; then + echo 'тестовый прокси недоступен' >&2 + exit 1 +fi +[ "${1:-}" = env ] || exit 99 +cat <<'ENV' +HTTP_PROXY=http://proxy.test:1234 +HTTPS_PROXY=http://proxy.test:1234 +ALL_PROXY=socks5://proxy.test:1234 +NO_PROXY=proxy.test,localhost,127.0.0.1,::1,[::1],.example.test +http_proxy=http://proxy.test:1234 +https_proxy=http://proxy.test:1234 +all_proxy=socks5://proxy.test:1234 +no_proxy=proxy.test,localhost,127.0.0.1,::1,[::1],.example.test +ENV +EOF + + cat > "$fixture/bin/chatgpt" <<'EOF' +#!/usr/bin/env bash +printf '<%s>\n' "$@" > "$RESULTS_DIR/chatgpt-args" +printf 'HTTP_PROXY=%s\nALL_PROXY=%s\nNO_PROXY=%s\n' \ + "$HTTP_PROXY" "$ALL_PROXY" "$NO_PROXY" > "$RESULTS_DIR/chatgpt-env" +EOF + + chmod +x "$fixture/bin/pgrep" "$fixture/bin/notify-send" \ + "$fixture/bin/proxy-client" "$fixture/bin/chatgpt" +} + +write_process() { + local fixture="$1" pid="$2" + shift 2 + mkdir -p "$fixture/proc/$pid" + printf '%s\0' "$@" > "$fixture/proc/$pid/cmdline" +} + +run_launcher() { + local fixture="$1" + shift + HOME="$fixture/home" \ + PATH="$fixture/bin:/usr/bin:/bin" \ + RESULTS_DIR="$fixture/results" \ + PROXY_CLIENT_BIN="$fixture/bin/proxy-client" \ + PROXY_CLIENT_SHELL="$SHELL_MODULE" \ + CHATGPT_BIN="$fixture/bin/chatgpt" \ + PROC_ROOT="$fixture/proc" \ + "$LAUNCHER" "$@" +} + +# Успешный запуск передаёт и окружение, и явные параметры Chromium. +fixture="$TEST_ROOT/success" +setup_fixture "$fixture" +FAKE_PGREP_STATUS=1 run_launcher "$fixture" 'chatgpt://example/path' +assert_contains "$fixture/results/chatgpt-args" \ + '<--proxy-server=http://proxy.test:1234>' \ + 'не передан сервер Chromium' +assert_contains "$fixture/results/chatgpt-args" \ + '<--proxy-bypass-list=proxy.test;localhost;127.0.0.1;::1;[::1];*.example.test>' \ + 'неверно преобразован список обхода Chromium' +assert_contains "$fixture/results/chatgpt-args" '' \ + 'пользовательский аргумент потерян' +assert_contains "$fixture/results/chatgpt-env" \ + 'ALL_PROXY=socks5://proxy.test:1234' \ + 'proxy-окружение не унаследовано' +assert_not_exists "$fixture/results/notifications" \ + 'успешный запуск показал ошибку' +ok 'успешный запуск применяет proxy-client к Chromium и окружению' + +# Уже работающий через тот же прокси экземпляр получает повторный запрос: +# Electron поднимает его существующее окно вместо ошибки «уже запущен». +fixture="$TEST_ROOT/running-proxied" +setup_fixture "$fixture" +write_process "$fixture" 4242 \ + '/usr/lib/chatgpt/ChatGPT --proxy-server=http://proxy.test:1234 --proxy-bypass-list=proxy.test;localhost;127.0.0.1;::1;[::1];*.example.test' +FAKE_PGREP_STATUS=0 run_launcher "$fixture" 'chatgpt://existing' +assert_contains "$fixture/results/chatgpt-args" '' \ + 'повторный запрос не передан прокси-экземпляру' +assert_not_exists "$fixture/results/notifications" \ + 'работающий прокси-экземпляр ошибочно объявлен конфликтом' +ok 'повторный прокси-запуск открывает существующий экземпляр' + +# Прямой экземпляр блокируется с точной инструкцией: встроенный Restart +# сохранит прямой режим, поэтому приложение нужно завершить через системный лоток. +fixture="$TEST_ROOT/running-direct" +setup_fixture "$fixture" +write_process "$fixture" 4242 /usr/lib/chatgpt/ChatGPT +set +e +FAKE_PGREP_STATUS=0 run_launcher "$fixture" >/dev/null 2>&1 +status=$? +set -e +[ "$status" -eq 1 ] || fail "ожидался код 1 при прямом ChatGPT, получен $status" +assert_not_exists "$fixture/results/chatgpt-args" \ + 'прямой ChatGPT получил повторный запуск' +assert_contains "$fixture/results/notifications" 'без прокси' \ + 'уведомление не различает прямой экземпляр' +assert_contains "$fixture/results/notifications" 'системном лотке' \ + 'нет инструкции для полного завершения прямого экземпляра' +ok 'прямой экземпляр блокируется с точной инструкцией' + +# Недоступный прокси не приводит к прямому запуску. +fixture="$TEST_ROOT/unavailable" +setup_fixture "$fixture" +set +e +FAKE_PGREP_STATUS=1 FAKE_PROXY_FAIL=1 run_launcher "$fixture" >/dev/null 2>&1 +status=$? +set -e +[ "$status" -eq 1 ] || fail "ожидался код 1 при ошибке прокси, получен $status" +assert_not_exists "$fixture/results/chatgpt-args" \ + 'ChatGPT запущен напрямую после ошибки прокси' +assert_contains "$fixture/results/notifications" 'proxy-client status' \ + 'нет диагностической подсказки при ошибке прокси' +ok 'ошибка прокси запрещает прямой запуск' + +# Ярлык остаётся отдельным и не перехватывает штатные обработчики ChatGPT. +assert_contains "$DESKTOP_FILE" 'Name=ChatGPT Proxy' \ + 'неверное имя ярлыка' +assert_contains "$DESKTOP_FILE" 'Exec=start-chatgpt-proxy %U' \ + 'ярлык не вызывает управляемый запускатель' +if grep -q '^MimeType=' "$DESKTOP_FILE"; then + fail 'прокси-ярлык не должен перехватывать штатные MIME-обработчики' +fi +[ "$(basename "$DESKTOP_FILE")" = chatgpt-proxy.desktop ] \ + || fail 'прокси-ярлык подменяет системный chatgpt.desktop' +ok 'ChatGPT Proxy остаётся отдельным пользовательским ярлыком' + +if command -v desktop-file-validate >/dev/null 2>&1; then + desktop-file-validate "$DESKTOP_FILE" >/dev/null \ + || fail 'desktop-file-validate нашёл ошибку' + ok 'desktop-файл корректен' +else + echo 'skip - desktop-file-validate не установлен' +fi + +if command -v shellcheck >/dev/null 2>&1; then + shellcheck -s bash "$LAUNCHER" "$0" \ + || fail 'shellcheck нашёл проблемы' + ok 'shellcheck чист' +else + echo 'skip - shellcheck не установлен' +fi + +echo "PASS: $pass_count проверок пройдено" diff --git a/tests/test_install.sh b/tests/test_install.sh index 8873121..4a12913 100755 --- a/tests/test_install.sh +++ b/tests/test_install.sh @@ -36,9 +36,9 @@ setup_fixture() { # дерева. Локальные данные инструментов внутри пакетов (vscode/.vscode-server, # zed/.config/opencode и т.п.) весят гигабайты, переполняют /tmp и ломают # stow конфликтами, поэтому в тестовое дерево не попадает. - git -C "$REPO_ROOT" ls-files -z --cached -- \ + git -C "$REPO_ROOT" ls-files -z --cached --others --exclude-standard -- \ install.sh bash tmux vim zed kitty wezterm vscode agents opencode codex gemini \ - claude proxy-client scripts \ + claude proxy-client chatgpt scripts \ | tar -C "$REPO_ROOT" --null -T - -cf - \ | tar -xf - -C "$repo_root" @@ -102,6 +102,14 @@ test_fresh_install_uses_home_target() { [ -d "$home_dir/.local/bin" ] || fail "Expected ~/.local/bin directory" [ ! -L "$home_dir/.local/bin" ] || fail "Expected ~/.local/bin to stay a real directory" [ -x "$home_dir/.local/bin/proxy-client" ] || fail "Expected executable proxy-client CLI in HOME" + [ -x "$home_dir/.local/bin/start-chatgpt-proxy" ] || fail "Expected ChatGPT proxy launcher in HOME" + [ -f "$home_dir/.local/share/applications/chatgpt-proxy.desktop" ] \ + || fail "Expected ChatGPT Proxy desktop entry in HOME" + [ -d "$home_dir/.local/share" ] || fail "Expected ~/.local/share directory" + [ ! -L "$home_dir/.local/share" ] || fail "Expected ~/.local/share to stay a real directory" + [ -d "$home_dir/.local/share/applications" ] || fail "Expected applications directory" + [ ! -L "$home_dir/.local/share/applications" ] \ + || fail "Expected applications directory to stay real" [ -d "$home_dir/.config/proxy-client" ] || fail "Expected proxy-client config directory" [ ! -L "$home_dir/.config/proxy-client" ] || fail "Expected proxy-client config to stay outside the repository" [ -f "$home_dir/.config/proxy-client/shell.bash" ] || fail "Expected proxy-client shell module" diff --git a/tests/test_proxy_client_full.sh b/tests/test_proxy_client_full.sh index da2b6bf..0c11b45 100755 --- a/tests/test_proxy_client_full.sh +++ b/tests/test_proxy_client_full.sh @@ -13,6 +13,7 @@ export PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s tests bash tests/test_proxy_launchers.sh +bash tests/test_chatgpt_proxy_launcher.sh bash tests/test_proxy_client_install.sh bash tests/test_install.sh uv run --python 3.8 --no-project python -m unittest discover -s tests