feat(chatgpt): добавлен отдельный запуск через прокси
- Зачем: - приложение должно явно работать через прокси и сохранять настройку после обновлений. - Что: - добавлены отдельный ярлык ChatGPT Proxy и запускатель на основе proxy-client. - исключено смешение прямого и проксированного режимов существующего процесса. - установка, документация и автоматические проверки дополнены новым Stow-пакетом. - Проверка: - bash tests/test_chatgpt_proxy_launcher.sh. - bash tests/test_proxy_launchers.sh. - bash tests/test_install.sh.
This commit is contained in:
1 parent
039cf29b95
commit
cb600bcaa8
8 files changed
+462
-3
No files matched your search
@@ -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 |
|
||||
|
||||
Executable
+134
@@ -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" \
|
||||
"$@"
|
||||
@@ -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;
|
||||
@@ -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-пакета.
|
||||
+9
-1
@@ -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"
|
||||
|
||||
Executable
+193
@@ -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" '<chatgpt://example/path>' \
|
||||
'пользовательский аргумент потерян'
|
||||
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" '<chatgpt://existing>' \
|
||||
'повторный запрос не передан прокси-экземпляру'
|
||||
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 проверок пройдено"
|
||||
+10
-2
@@ -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"
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user