refactor(proxy-client): упростить установщик пакета и shell-тесты

- install.sh ставит три прямые ссылки без Stow; чужие файлы не трогает
  и перечисляет все конфликты сразу; ~/.local и ~/.config могут быть
  ссылками; блок в ~/.bashrc дописывается, если нет маркера.
- proxy_on читает вывод proxy-client env без временного файла.
- Тесты Bash-функций работают через поддельный PROXY_CLIENT_BIN.
- test_proxy_client_full.sh запускает только тесты proxy-client,
  прогон под Python 3.8 — если есть uv.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
ddadminandClaude Opus 5.5 committed 2026-09-28 09:28:07 -04:00
1 parent 502f828c6f
commit 06ca4ab0c6
5 files changed
+281 -698

No files matched your search

+10 -32
View File
@@ -5,21 +5,17 @@ _proxy_cli() {
"${PROXY_CLIENT_BIN:-proxy-client}" "$@"
}
_proxy_apply_env_file() {
local output_file="$1" line key value
# Применяет вывод "proxy-client env" целиком или не применяет ничего.
_proxy_apply_env() {
local line key
local -A proxy_values=()
local -a allowed_keys=(
HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY
http_proxy https_proxy all_proxy no_proxy
)
while IFS= read -r line || [ -n "$line" ]; do
while IFS= read -r line; do
if [[ "$line" != *=* ]]; then
echo "Ошибка: proxy-client env вернул строку без '='" >&2
return 1
fi
key="${line%%=*}"
value="${line#*=}"
case "$key" in
HTTP_PROXY|HTTPS_PROXY|ALL_PROXY|NO_PROXY|http_proxy|https_proxy|all_proxy|no_proxy) ;;
*)
@@ -27,38 +23,20 @@ _proxy_apply_env_file() {
return 1
;;
esac
proxy_values["$key"]="$value"
done < "$output_file"
proxy_values["$key"]="${line#*=}"
done <<< "$1"
for key in "${allowed_keys[@]}"; do
if [[ -n "${proxy_values[$key]+set}" ]]; then
printf -v "$key" '%s' "${proxy_values[$key]}"
export "$key"
fi
for key in "${!proxy_values[@]}"; do
export "$key=${proxy_values[$key]}"
done
}
proxy_on() {
local output_file old_umask
old_umask="$(umask)"
umask 077
if ! output_file="$(mktemp "${TMPDIR:-/tmp}/proxy-client-env.XXXXXX")"; then
umask "$old_umask"
echo "Ошибка: не удалось создать временный файл для proxy-env" >&2
return 1
fi
umask "$old_umask"
if ! _proxy_cli env > "$output_file"; then
rm -f "$output_file"
local output
if ! output="$(_proxy_cli env)" || ! _proxy_apply_env "$output"; then
echo "Ошибка: прокси не включён" >&2
return 1
fi
if ! _proxy_apply_env_file "$output_file"; then
rm -f "$output_file"
echo "Ошибка: прокси не включён" >&2
return 1
fi
rm -f "$output_file"
echo "Прокси включён"
}
+33 -156
View File
@@ -1,8 +1,9 @@
#!/usr/bin/env bash
# Ставит proxy-client в домашний каталог: три ссылки на этот каталог и
# (по умолчанию) блок в ~/.bashrc. Повторный запуск ничего не меняет.
set -euo pipefail
PACKAGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
STOW_BIN="${STOW_BIN:-stow}"
SETUP_SHELL=1
usage() {
@@ -15,9 +16,7 @@ EOF
while [ "$#" -gt 0 ]; do
case "$1" in
--no-shell-setup)
SETUP_SHELL=0
;;
--no-shell-setup) SETUP_SHELL=0 ;;
-h|--help)
usage
exit 0
@@ -31,180 +30,58 @@ while [ "$#" -gt 0 ]; do
shift
done
SOURCES=(
"$PACKAGE_DIR/.local/bin/proxy-client"
"$PACKAGE_DIR/.config/proxy-client/shell.bash"
"$PACKAGE_DIR/.config/proxy-client/examples"
)
TARGETS=(
"$HOME/.local/bin/proxy-client"
"$HOME/.config/proxy-client/shell.bash"
"$HOME/.config/proxy-client/examples"
)
STOW_IGNORE_PATTERNS=(
'^\.local/bin/proxy-client$'
'^\.config/proxy-client/shell\.bash$'
'^\.config/proxy-client/examples$'
# Машинные config и default.url сюда не входят: они живут только в $HOME.
LINKS=(
.local/bin/proxy-client
.config/proxy-client/shell.bash
.config/proxy-client/examples
)
source_errors=()
[ -x "${SOURCES[0]}" ] || source_errors+=("${SOURCES[0]}")
[ -f "${SOURCES[1]}" ] || source_errors+=("${SOURCES[1]}")
[ -d "${SOURCES[2]}" ] || source_errors+=("${SOURCES[2]}")
if [ "${#source_errors[@]}" -gt 0 ]; then
echo "Ошибка: отсутствуют обязательные файлы исходного пакета:" >&2
printf ' %s\n' "${source_errors[@]}" >&2
exit 1
fi
"${SOURCES[0]}" --help >/dev/null
bash -n "${SOURCES[1]}"
SHELL_BLOCK_START='# >>> proxy-client >>>'
SHELL_BLOCK_END='# <<< proxy-client <<<'
SHELL_SETUP_NEEDED=0
BASH_CUSTOM_HAS_MODULE=0
SHELL_PATH_NEEDED=1
SHELL_SOURCE_NEEDED=1
SHELL_BLOCK_CONFLICT=0
if [ "$SETUP_SHELL" -eq 1 ]; then
if [ -f "$HOME/.bash_custom" ] \
&& grep -Fq 'proxy-client/shell.bash' "$HOME/.bash_custom"; then
BASH_CUSTOM_HAS_MODULE=1
SHELL_SOURCE_NEEDED=0
fi
bashrc_start_count=0
bashrc_end_count=0
if [ -f "$HOME/.bashrc" ]; then
bashrc_start_count="$(
grep -Fxc "$SHELL_BLOCK_START" "$HOME/.bashrc" || true
)"
bashrc_end_count="$(
grep -Fxc "$SHELL_BLOCK_END" "$HOME/.bashrc" || true
)"
if grep -Fq 'export PATH="$HOME/.local/bin:$PATH"' "$HOME/.bashrc"; then
SHELL_PATH_NEEDED=0
fi
fi
if [ "$bashrc_start_count" -eq 0 ] && [ "$bashrc_end_count" -eq 0 ]; then
if [ "$SHELL_PATH_NEEDED" -eq 1 ] \
|| [ "$SHELL_SOURCE_NEEDED" -eq 1 ]; then
SHELL_SETUP_NEEDED=1
fi
elif [ "$bashrc_start_count" -eq 1 ] \
&& [ "$bashrc_end_count" -eq 1 ] \
&& [ "$SHELL_PATH_NEEDED" -eq 0 ] \
&& { [ "$BASH_CUSTOM_HAS_MODULE" -eq 1 ] \
|| grep -Fq 'proxy-client/shell.bash' "$HOME/.bashrc"; }; then
:
else
SHELL_BLOCK_CONFLICT=1
fi
fi
# Существующая ссылка на этот же файл пакета — не конфликт, всё остальное
# (включая битую ссылку) не трогаем.
conflicts=()
missing_targets=0
stow_ignore_args=()
PARENT_DIRS=(
"$HOME/.local"
"$HOME/.local/bin"
"$HOME/.config"
"$HOME/.config/proxy-client"
)
for parent_dir in "${PARENT_DIRS[@]}"; do
if { [ -e "$parent_dir" ] || [ -L "$parent_dir" ]; } \
&& { [ ! -d "$parent_dir" ] || [ -L "$parent_dir" ]; }; then
conflicts+=("$parent_dir")
fi
done
for index in "${!TARGETS[@]}"; do
target="${TARGETS[$index]}"
source_path="${SOURCES[$index]}"
for link in "${LINKS[@]}"; do
target="$HOME/$link"
if [ -e "$target" ] || [ -L "$target" ]; then
if [ ! -L "$target" ] \
|| [ "$(readlink -f "$target" 2>/dev/null || true)" != "$source_path" ]; then
if [ "$(readlink -f "$target" 2>/dev/null)" != "$PACKAGE_DIR/$link" ]; then
conflicts+=("$target")
else
stow_ignore_args+=("--ignore=${STOW_IGNORE_PATTERNS[$index]}")
fi
else
missing_targets=$((missing_targets + 1))
fi
done
if [ "$SHELL_SETUP_NEEDED" -eq 1 ] \
&& { [ -e "$HOME/.bashrc" ] || [ -L "$HOME/.bashrc" ]; } \
&& [ ! -f "$HOME/.bashrc" ]; then
conflicts+=("$HOME/.bashrc")
fi
if [ "$SHELL_BLOCK_CONFLICT" -eq 1 ]; then
conflicts+=("$HOME/.bashrc")
fi
if [ "${#conflicts[@]}" -gt 0 ]; then
echo "Ошибка: найдены конфликтующие пути:" >&2
echo "Ошибка: пути уже заняты, установка не выполнена:" >&2
printf ' %s\n' "${conflicts[@]}" >&2
echo "Разберите их вручную и повторите установку." >&2
echo "Удалите или переименуйте их и повторите установку." >&2
exit 1
fi
mkdir -p "$HOME/.local/bin" "$HOME/.config/proxy-client"
for link in "${LINKS[@]}"; do
[ -e "$HOME/$link" ] || ln -s "$PACKAGE_DIR/$link" "$HOME/$link"
done
if command -v "$STOW_BIN" >/dev/null 2>&1; then
echo "Установка с помощью Stow."
if [ "$missing_targets" -gt 0 ]; then
"$STOW_BIN" --dir="$(dirname "$PACKAGE_DIR")" --target="$HOME" \
"${stow_ignore_args[@]}" \
"$(basename "$PACKAGE_DIR")"
fi
SHELL_BLOCK_START='# >>> proxy-client >>>'
if [ "$SETUP_SHELL" -eq 0 ]; then
echo "Настройка Bash пропущена (--no-shell-setup)."
# Команда для пользователя: $HOME и $PATH раскроются в его сессии.
# shellcheck disable=SC2016
RELOAD_COMMAND='export PATH="$HOME/.local/bin:$PATH"; source ~/.config/proxy-client/shell.bash'
else
echo "Установка без Stow: создаю символические ссылки напрямую."
for index in "${!TARGETS[@]}"; do
target="${TARGETS[$index]}"
source_path="${SOURCES[$index]}"
if [ ! -L "$target" ]; then
ln -s "$source_path" "$target"
fi
done
fi
if ! grep -Fqx "$SHELL_BLOCK_START" "$HOME/.bashrc" 2>/dev/null; then
cat >> "$HOME/.bashrc" <<EOF
"$HOME/.local/bin/proxy-client" --help >/dev/null
bash -n "$HOME/.config/proxy-client/shell.bash"
if [ "$SHELL_SETUP_NEEDED" -eq 1 ]; then
cat >> "$HOME/.bashrc" <<'EOF'
# >>> proxy-client >>>
EOF
if [ "$SHELL_PATH_NEEDED" -eq 1 ]; then
cat >> "$HOME/.bashrc" <<'EOF'
case ":$PATH:" in
*":$HOME/.local/bin:"*) ;;
*)
export PATH="$HOME/.local/bin:$PATH"
;;
$SHELL_BLOCK_START
case ":\$PATH:" in
*":\$HOME/.local/bin:"*) ;;
*) export PATH="\$HOME/.local/bin:\$PATH" ;;
esac
EOF
fi
if [ "$SHELL_SOURCE_NEEDED" -eq 1 ]; then
cat >> "$HOME/.bashrc" <<'EOF'
[ -f "$HOME/.config/proxy-client/shell.bash" ] && source "$HOME/.config/proxy-client/shell.bash"
EOF
fi
cat >> "$HOME/.bashrc" <<'EOF'
[ -f "\$HOME/.config/proxy-client/shell.bash" ] && source "\$HOME/.config/proxy-client/shell.bash"
# <<< proxy-client <<<
EOF
elif [ "$SETUP_SHELL" -eq 0 ]; then
echo "Настройка Bash пропущена (--no-shell-setup)."
fi
if [ "$SETUP_SHELL" -eq 1 ]; then
echo "В ~/.bashrc добавлен блок proxy-client."
fi
RELOAD_COMMAND='source ~/.bashrc'
else
RELOAD_COMMAND='export PATH="$HOME/.local/bin:$PATH"; source ~/.config/proxy-client/shell.bash'
fi
cat <<EOF