From 898db1098df03941792f4641d8dced7a9c8a7300 Mon Sep 17 00:00:00 2001 From: Dmitrii Date: Sun, 22 Mar 2026 19:42:50 +0300 Subject: [PATCH] =?UTF-8?q?refactor(wsl):=20=D1=83=D0=BD=D0=B8=D1=84=D0=B8?= =?UTF-8?q?=D1=86=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B0=20=D0=BB=D0=BE?= =?UTF-8?q?=D0=B3=D0=B8=D0=BA=D0=B0=20hproxy.local=20=D0=B8=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BC=D0=B8=D0=B3?= =?UTF-8?q?=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20legacy=20boot=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Зачем: - setup-hproxy.sh удалял запись из hosts до проверки gateway — при ошибке hosts ломался. - update_hproxy() дублировала логику скрипта с багами и не тестировалась. - legacy inline boot command в wsl.conf не мигрировался автоматически. - Что: - setup-hproxy.sh: безопасный порядок (gateway → hosts), set -e, экранированная точка в sed. - update_hproxy() заменена на thin wrapper с проверкой wsl.conf и ошибок sudo. - install.sh: интерактивная замена legacy boot command, детекция существующих [boot] command, принимает русскую «у» наравне с «y». - README.md: описаны три сценария (WSL NAT, WSL mirrored — host-side resolution, native Linux). - добавлены тесты: existing boot command preserved, legacy command migration hint. - Проверка: - bash tests/test_install.sh - ./install.sh (интерактивно, ответить «y» на миграцию legacy command) Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 12 ++++++-- bash/.bash_functions | 52 +++++++++---------------------- install.sh | 68 ++++++++++++++++++++++++++--------------- scripts/setup-hproxy.sh | 12 ++++++-- tests/test_install.sh | 53 ++++++++++++++++++++++++++++++++ 5 files changed, 131 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index eaece36..6608631 100644 --- a/README.md +++ b/README.md @@ -113,13 +113,21 @@ EOF После изменения `wsl.conf` — `wsl --shutdown`. Обновить без перезапуска: `sudo scripts/setup-hproxy.sh` или `update_hproxy`. -**WSL Mirror mode** (`networkingMode=mirrored`) — IP не меняется, достаточно одной записи: +**WSL mirrored mode** (`networkingMode=mirrored`) — имя резолвится из WSL через DNS/hosts хоста. Достаточно записи на стороне Windows: ``` # C:\Windows\System32\drivers\etc\hosts 192.168.1.100 hproxy.local ``` +Проверка: `getent hosts hproxy.local` (запись в Linux `/etc/hosts` не обязательна). + +**Native Linux** — прокси на localhost, нужна запись в `/etc/hosts`: + +```bash +echo '127.0.0.1 hproxy.local' | sudo tee -a /etc/hosts +``` + **VS Code Server** — `vscode/.vscode-server/server-env-setup` автоматически подхватывает `hproxy.local` и выставляет все proxy-переменные (HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, NO_PROXY и lowercase-варианты). Если `hproxy.local` не резолвится — прокси не включается, запуск не блокируется. **Windows pwsh** — `proxy_on` / `proxy_off` / `myip` работают аналогично bash-версиям. @@ -161,7 +169,7 @@ EOF | `proxy_on` | Включает HTTP/HTTPS/SOCKS5 прокси через `hproxy.local:12334` | | `proxy_off` | Очищает proxy-переменные | | `myip` | Показывает текущий внешний IP | -| `update_hproxy` | Обновляет `hproxy.local` в `/etc/hosts` (WSL NAT mode) | +| `update_hproxy` | Обновляет `hproxy.local` в `/etc/hosts` через `setup-hproxy.sh` (WSL NAT) | ### PowerShell-функции (`~/.config/powershell/profile.ps1`) diff --git a/bash/.bash_functions b/bash/.bash_functions index 3f466a6..017da79 100644 --- a/bash/.bash_functions +++ b/bash/.bash_functions @@ -3,46 +3,24 @@ # export HTTP_PROXY="http://127.0.0.1:10809" # export HTTPS_PROXY="http://127.0.0.1:10809" -# --- WSL Host Update --- +# --- WSL Host Update (только WSL NAT) --- update_hproxy() { - echo "🔄 Обновление IP-адреса для hproxy.local..." - - local host_ip="" - local wslconfig=$(ls /mnt/c/Users/*/.wslconfig 2>/dev/null | head -n 1) - local win_hosts="/mnt/c/Windows/System32/drivers/etc/hosts" - - # Проверяем наличие параметра networkingMode=mirror в конфиге Windows - if [ -f "$wslconfig" ] && grep -qiE '^[[:space:]]*networkingMode[[:space:]]*=[[:space:]]*mirror' "$wslconfig"; then - echo "🔍 Обнаружен режим Mirror." - - # Читаем hosts из Windows - if [ -f "$win_hosts" ] && grep -qiw "hproxy.local" "$win_hosts"; then - # Извлекаем IP (первый столбец) и чистим от виндовых символов возврата каретки (\r) - host_ip=$(grep -iw "hproxy.local" "$win_hosts" | awk '{print $1}' | tr -d '\r' | head -n 1) - echo "📂 Взят IP из Windows hosts: $host_ip" - else - # Фолбэк на случай, если записи в Windows нет - host_ip="127.0.0.1" - echo "⚠️ Запись hproxy.local в Windows hosts не найдена, используем дефолтный $host_ip" - fi - else - echo "🔍 Обнаружен режим NAT." - - # Формируем IP на лету из маршрута по умолчанию - host_ip=$(ip route show default | awk '{print $3}') - echo "🌐 Вычислен IP шлюза WSL: $host_ip" - fi - - if [ -z "$host_ip" ]; then - echo "❌ Ошибка: Не удалось определить IP-адрес." + local script + script="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/../scripts/setup-hproxy.sh" + if [ ! -f "$script" ]; then + echo "❌ Скрипт не найден: $script" return 1 fi - - # Обновляем /etc/hosts в Ubuntu - sudo sed -i '/hproxy.local/d' /etc/hosts - echo "$host_ip hproxy.local" | sudo tee -a /etc/hosts > /dev/null - - echo "✅ Готово! hproxy.local теперь указывает на $host_ip" + if ! awk '/^\[boot\]$/{b=1;next} /^\[/{b=0} b && /^[[:space:]]*command[[:space:]]*=/ && /setup-hproxy\.sh/' \ + "${WSL_CONF_PATH:-/etc/wsl.conf}" 2>/dev/null | grep -q .; then + echo "⚠️ Boot command не настроен — update_hproxy работает только для WSL NAT" + return 1 + fi + if ! sudo "$script"; then + echo "❌ Ошибка обновления hproxy.local" + return 1 + fi + echo "✅ hproxy.local -> $(getent hosts hproxy.local 2>/dev/null | awk '{print $1}')" } # Удобные функции-переключатели для терминала diff --git a/install.sh b/install.sh index 1e9f263..0a5b2a7 100755 --- a/install.sh +++ b/install.sh @@ -115,6 +115,15 @@ is_interactive() { esac } +wsl_conf_has_boot_command() { + awk ' + /^\[boot\]$/ { in_boot=1; next } + /^\[/ { in_boot=0 } + in_boot && /^[[:space:]]*command[[:space:]]*=/ { found=1 } + END { exit !found } + ' "$1" 2>/dev/null +} + # --- Проверка stow --- if ! command -v "$STOW_BIN" &>/dev/null; then error "stow не найден. Установите: sudo apt install stow" @@ -241,40 +250,49 @@ fi if [ -n "${WSL_DISTRO_NAME:-}" ]; then HPROXY_SCRIPT="$DOTFILES_DIR/scripts/setup-hproxy.sh" WSL_BOOT_COMMAND="command = \"$HPROXY_SCRIPT\"" + LEGACY_WSL_BOOT_COMMAND="command = \"ip route show default | awk '{print \$3 \\\" hproxy.local\\\"}' >> /etc/hosts\"" if grep -Fq "$WSL_BOOT_COMMAND" "$WSL_CONF_PATH" 2>/dev/null; then warn "/etc/wsl.conf уже содержит boot command для setup-hproxy.sh, пропускаю" - elif ! is_interactive; then - warn "WSL обнаружен, но stdin не интерактивный. Пропускаю настройку /etc/wsl.conf" - info "Добавьте вручную: $WSL_BOOT_COMMAND" - else - if awk ' - /^\[boot\]$/ { in_boot=1; next } - /^\[/ { in_boot=0 } - in_boot && /^[[:space:]]*command[[:space:]]*=/ { found=1 } - END { exit !found } - ' "$WSL_CONF_PATH" 2>/dev/null; then - warn "/etc/wsl.conf уже содержит [boot] command. Добавьте setup-hproxy.sh вручную" - info "Нужная строка: $WSL_BOOT_COMMAND" - else - echo "" - info "Обнаружен WSL. Скрипт $HPROXY_SCRIPT может автоматически" - info "прописывать hproxy.local в /etc/hosts при каждом старте WSL (NAT mode)." - echo -ne "${YELLOW}[?]${NC} Добавить boot command в /etc/wsl.conf? [y/N] " + elif grep -Fq "$LEGACY_WSL_BOOT_COMMAND" "$WSL_CONF_PATH" 2>/dev/null; then + warn "/etc/wsl.conf содержит legacy boot command для hproxy.local" + info "Рекомендуется заменить на: $WSL_BOOT_COMMAND" + if is_interactive; then + echo -ne "${YELLOW}[?]${NC} Заменить legacy command на setup-hproxy.sh? [y/N] " answer="" if ! read -r answer; then answer="" fi - if [[ "$answer" =~ ^[Yy]$ ]]; then - # Добавляем [boot] секцию, если её нет - if ! grep -q '^\[boot\]' "$WSL_CONF_PATH" 2>/dev/null; then - printf '\n[boot]\n' | "$SUDO_BIN" tee -a "$WSL_CONF_PATH" > /dev/null - fi + if [[ "$answer" =~ ^[YyУу]$ ]]; then + "$SUDO_BIN" sed -i '/ip route.*hproxy\.local/d' "$WSL_CONF_PATH" "$SUDO_BIN" sed -i "/^\[boot\]/a command = \"$HPROXY_SCRIPT\"" "$WSL_CONF_PATH" - info "Добавлено в /etc/wsl.conf. Перезапустите WSL: wsl --shutdown" - else - warn "Пропущено. Можно настроить вручную — см. README.md" + info "Заменено. Перезапустите WSL: wsl --shutdown" fi fi + elif wsl_conf_has_boot_command "$WSL_CONF_PATH"; then + warn "/etc/wsl.conf уже содержит [boot] command. Добавьте setup-hproxy.sh вручную" + info "Нужная строка: $WSL_BOOT_COMMAND" + elif ! is_interactive; then + warn "WSL обнаружен, но stdin не интерактивный. Пропускаю настройку /etc/wsl.conf" + info "Добавьте вручную: $WSL_BOOT_COMMAND" + else + echo "" + info "Обнаружен WSL. Скрипт $HPROXY_SCRIPT может автоматически" + info "прописывать hproxy.local в /etc/hosts при каждом старте WSL (NAT mode)." + echo -ne "${YELLOW}[?]${NC} Добавить boot command в /etc/wsl.conf? [y/N] " + answer="" + if ! read -r answer; then + answer="" + fi + if [[ "$answer" =~ ^[YyУу]$ ]]; then + # Добавляем [boot] секцию, если её нет + if ! grep -q '^\[boot\]' "$WSL_CONF_PATH" 2>/dev/null; then + printf '\n[boot]\n' | "$SUDO_BIN" tee -a "$WSL_CONF_PATH" > /dev/null + fi + "$SUDO_BIN" sed -i "/^\[boot\]/a command = \"$HPROXY_SCRIPT\"" "$WSL_CONF_PATH" + info "Добавлено в /etc/wsl.conf. Перезапустите WSL: wsl --shutdown" + else + warn "Пропущено. Можно настроить вручную — см. README.md" + fi fi fi diff --git a/scripts/setup-hproxy.sh b/scripts/setup-hproxy.sh index a07566f..2ec29be 100755 --- a/scripts/setup-hproxy.sh +++ b/scripts/setup-hproxy.sh @@ -8,5 +8,13 @@ # [boot] # command = "/path/to/setup-hproxy.sh" -sed -i '/hproxy.local/d' /etc/hosts -ip route show default | awk '{print $3 " hproxy.local"}' >> /etc/hosts +set -e + +gateway=$(ip route show default | awk '{print $3}') +if [ -z "$gateway" ]; then + echo "setup-hproxy: no default gateway" >&2 + exit 1 +fi + +sed -i '/hproxy\.local/d' /etc/hosts +printf '%s hproxy.local\n' "$gateway" >> /etc/hosts diff --git a/tests/test_install.sh b/tests/test_install.sh index fdad563..b7971cd 100755 --- a/tests/test_install.sh +++ b/tests/test_install.sh @@ -236,6 +236,57 @@ test_wsl_noninteractive_skips_file_changes() { printf '%s' "$output" | grep -Fq 'Добавьте вручную:' || fail "Expected manual WSL hint" } +test_wsl_noninteractive_existing_boot_command_is_preserved() { + local fixture repo_root home_dir wsl_conf output + fixture="$(setup_fixture)" + repo_root="$fixture/repo" + home_dir="$fixture/home" + wsl_conf="$fixture/wsl.conf" + mkdir -p "$home_dir" + cat > "$wsl_conf" <<'EOF' +[boot] +command = "/usr/local/bin/existing.sh" +EOF + + output="$( + run_install "$repo_root" "$home_dir" \ + env WSL_DISTRO_NAME=Ubuntu INSTALL_INTERACTIVE_MODE=never WSL_CONF_PATH="$wsl_conf" + )" + + assert_file_contains "$wsl_conf" 'command = "/usr/local/bin/existing.sh"' + if grep -Fq 'setup-hproxy.sh' "$wsl_conf"; then + fail "Expected existing boot command to stay untouched" + fi + printf '%s' "$output" | grep -Fq 'уже содержит [boot] command' || fail "Expected warning about existing boot command" + if printf '%s' "$output" | grep -Fq 'Добавьте вручную:'; then + fail "Did not expect manual add hint when [boot] command already exists" + fi +} + +test_wsl_legacy_boot_command_suggests_migration() { + local fixture repo_root home_dir wsl_conf output command_line + fixture="$(setup_fixture)" + repo_root="$fixture/repo" + home_dir="$fixture/home" + wsl_conf="$fixture/wsl.conf" + mkdir -p "$home_dir" + cat > "$wsl_conf" <<'EOF' +[boot] +command = "ip route show default | awk '{print $3 \" hproxy.local\"}' >> /etc/hosts" +EOF + command_line="command = \"$repo_root/scripts/setup-hproxy.sh\"" + + output="$( + run_install "$repo_root" "$home_dir" \ + env WSL_DISTRO_NAME=Ubuntu INSTALL_INTERACTIVE_MODE=never WSL_CONF_PATH="$wsl_conf" + )" + + assert_file_contains "$wsl_conf" 'command = "ip route show default | awk '\''{print $3 \" hproxy.local\"}'\'' >> /etc/hosts"' + printf '%s' "$output" | grep -Fq 'legacy boot command' || fail "Expected legacy boot command warning" + printf '%s' "$output" | grep -Fq 'Рекомендуется заменить' || fail "Expected migration hint to replace legacy command" + printf '%s' "$output" | grep -Fq "$command_line" || fail "Expected suggested setup-hproxy.sh command" +} + test_wsl_interactive_adds_boot_command() { local fixture repo_root home_dir wsl_conf command_line fixture="$(setup_fixture)" @@ -289,6 +340,8 @@ main() { test_broken_claude_symlink_does_not_fail test_absolute_symlinks_in_home_do_not_break_install test_wsl_noninteractive_skips_file_changes + test_wsl_noninteractive_existing_boot_command_is_preserved + test_wsl_legacy_boot_command_suggests_migration test_wsl_interactive_adds_boot_command test_wsl_existing_boot_command_is_preserved