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