feat(proxy-client): добавлен самостоятельный установщик
- Зачем: - пакет должен устанавливаться отдельно от dotfiles без ручной раскладки и опасных замен. - Что: - добавлен безопасный установщик со Stow и прямыми симлинками. - добавлены идемпотентная настройка Bash и защита машинных файлов. - обновлены инструкция и полный набор интеграционных проверок. - Проверка: - env XDG_STATE_HOME=<временный каталог> bash tests/test_proxy_client_full.sh.
This commit is contained in:
1 parent
43404d374f
commit
f679515a8f
5 files changed
+645
-12
No files matched your search
@@ -1 +1,4 @@
|
|||||||
^/README\.md$
|
^/README\.md$
|
||||||
|
^/install\.sh$
|
||||||
|
^/\.config/proxy-client/config$
|
||||||
|
^/\.config/proxy-client/default\.url$
|
||||||
+23
-12
@@ -32,20 +32,31 @@ source ~/.bash_custom
|
|||||||
|
|
||||||
### Только пакет `proxy-client`
|
### Только пакет `proxy-client`
|
||||||
|
|
||||||
```bash
|
Каталог можно скопировать отдельно от остальных dotfiles и запустить его
|
||||||
cd ~/dotfiles
|
собственный установщик из любого рабочего каталога:
|
||||||
mkdir -p ~/.local/bin ~/.config/proxy-client
|
|
||||||
stow proxy-client
|
|
||||||
source ~/.config/proxy-client/shell.bash
|
|
||||||
```
|
|
||||||
|
|
||||||
Если Bash-пакет dotfiles не установлен, добавьте подключение модуля в
|
|
||||||
`~/.bashrc`:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
printf '%s\n' 'source ~/.config/proxy-client/shell.bash' >> ~/.bashrc
|
~/src/proxy-client/install.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
При наличии Stow установщик использует его, иначе создаёт те же три симлинка
|
||||||
|
напрямую: CLI, Bash-модуль и каталог шаблонов. Исходный каталог после установки
|
||||||
|
нельзя перемещать: домашние ссылки зависят от его постоянного пути.
|
||||||
|
|
||||||
|
По умолчанию установщик добавляет в `~/.bashrc` помеченный блок для
|
||||||
|
`~/.local/bin` и Bash-модуля. Чтобы оставить настройки оболочки без изменений:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/src/proxy-client/install.sh --no-shell-setup
|
||||||
|
```
|
||||||
|
|
||||||
|
Установщик не создаёт машинный `config` и закрытый `default.url`, не
|
||||||
|
устанавливает Xray и не проверяет сеть. После установки примените изменения
|
||||||
|
через `source ~/.bashrc`; с `--no-shell-setup` подключите модуль напрямую:
|
||||||
|
`source ~/.config/proxy-client/shell.bash`. Затем выберите режим по разделу
|
||||||
|
[«Настройка режима»](#настройка-режима). Без машинного конфига продолжает
|
||||||
|
действовать режим Hiddify по умолчанию.
|
||||||
|
|
||||||
## Как выбирается прокси
|
## Как выбирается прокси
|
||||||
|
|
||||||
Режим определяется только машинным файлом
|
Режим определяется только машинным файлом
|
||||||
@@ -247,9 +258,9 @@ export PROXY_PREFLIGHT_TIMEOUT=5
|
|||||||
|
|
||||||
## Справочник
|
## Справочник
|
||||||
|
|
||||||
### Ручное подключение без Stow
|
### Ручное подключение без установщика
|
||||||
|
|
||||||
Можно создать ссылки на CLI, Bash-модуль и примеры либо скопировать те же файлы:
|
Если пакетный установщик запустить нельзя, создайте те же ссылки вручную:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir -p ~/.local/bin ~/.config/proxy-client
|
mkdir -p ~/.local/bin ~/.config/proxy-client
|
||||||
|
|||||||
Executable
+216
@@ -0,0 +1,216 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PACKAGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||||
|
STOW_BIN="${STOW_BIN:-stow}"
|
||||||
|
SETUP_SHELL=1
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Использование: ./install.sh [--no-shell-setup]
|
||||||
|
|
||||||
|
--no-shell-setup не изменять ~/.bashrc
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--no-shell-setup)
|
||||||
|
SETUP_SHELL=0
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Ошибка: неизвестный параметр: $1" >&2
|
||||||
|
usage >&2
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
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$'
|
||||||
|
)
|
||||||
|
|
||||||
|
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]}"
|
||||||
|
if [ -e "$target" ] || [ -L "$target" ]; then
|
||||||
|
if [ ! -L "$target" ] \
|
||||||
|
|| [ "$(readlink -f "$target" 2>/dev/null || true)" != "$source_path" ]; 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
|
||||||
|
printf ' %s\n' "${conflicts[@]}" >&2
|
||||||
|
echo "Разберите их вручную и повторите установку." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$HOME/.local/bin" "$HOME/.config/proxy-client"
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
"$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"
|
||||||
|
;;
|
||||||
|
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'
|
||||||
|
# <<< proxy-client <<<
|
||||||
|
EOF
|
||||||
|
elif [ "$SETUP_SHELL" -eq 0 ]; then
|
||||||
|
echo "Настройка Bash пропущена (--no-shell-setup)."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$SETUP_SHELL" -eq 1 ]; then
|
||||||
|
RELOAD_COMMAND='source ~/.bashrc'
|
||||||
|
else
|
||||||
|
RELOAD_COMMAND='source ~/.config/proxy-client/shell.bash'
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
proxy-client установлен.
|
||||||
|
Исходный каталог должен оставаться по постоянному пути:
|
||||||
|
$PACKAGE_DIR
|
||||||
|
Примените настройки в текущей сессии: $RELOAD_COMMAND
|
||||||
|
Настройка режима описана в $PACKAGE_DIR/README.md.
|
||||||
|
EOF
|
||||||
@@ -13,5 +13,6 @@ export PYTHONDONTWRITEBYTECODE=1
|
|||||||
|
|
||||||
python3 -m unittest discover -s tests
|
python3 -m unittest discover -s tests
|
||||||
bash tests/test_proxy_launchers.sh
|
bash tests/test_proxy_launchers.sh
|
||||||
|
bash tests/test_proxy_client_install.sh
|
||||||
bash tests/test_install.sh
|
bash tests/test_install.sh
|
||||||
uv run --python 3.8 --no-project python -m unittest discover -s tests
|
uv run --python 3.8 --no-project python -m unittest discover -s tests
|
||||||
Executable
+402
@@ -0,0 +1,402 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
TEST_TMP_ROOT="$(mktemp -d /tmp/proxy-client-install-test.XXXXXX)"
|
||||||
|
trap 'rm -rf "$TEST_TMP_ROOT"' EXIT
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
echo "FAIL: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_link_points_to() {
|
||||||
|
local link_path="$1" expected_path="$2" actual_path
|
||||||
|
[ -L "$link_path" ] || fail "Expected symlink: $link_path"
|
||||||
|
actual_path="$(readlink -f "$link_path")"
|
||||||
|
[ "$actual_path" = "$expected_path" ] \
|
||||||
|
|| fail "$link_path points to $actual_path, expected $expected_path"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_not_exists() {
|
||||||
|
local path="$1"
|
||||||
|
[ ! -e "$path" ] && [ ! -L "$path" ] || fail "Path should not exist: $path"
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_package() {
|
||||||
|
local fixture_root="$1" package_dir="$fixture_root/source with spaces/proxy-client"
|
||||||
|
mkdir -p \
|
||||||
|
"$package_dir/.local/bin" \
|
||||||
|
"$package_dir/.config/proxy-client"
|
||||||
|
cp -a \
|
||||||
|
"$REPO_ROOT/proxy-client/.local/bin/proxy-client" \
|
||||||
|
"$package_dir/.local/bin/proxy-client"
|
||||||
|
cp -a \
|
||||||
|
"$REPO_ROOT/proxy-client/.config/proxy-client/shell.bash" \
|
||||||
|
"$REPO_ROOT/proxy-client/.config/proxy-client/examples" \
|
||||||
|
"$package_dir/.config/proxy-client/"
|
||||||
|
cp -a \
|
||||||
|
"$REPO_ROOT/proxy-client/.stow-local-ignore" \
|
||||||
|
"$REPO_ROOT/proxy-client/README.md" \
|
||||||
|
"$REPO_ROOT/proxy-client/install.sh" \
|
||||||
|
"$package_dir/"
|
||||||
|
printf '%s\n' 'MACHINE_CONFIG_SENTINEL' > "$package_dir/.config/proxy-client/config"
|
||||||
|
printf '%s\n' 'PRIVATE_PROFILE_SENTINEL' > "$package_dir/.config/proxy-client/default.url"
|
||||||
|
printf '%s\n' "$package_dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_package_install() {
|
||||||
|
local package_dir="$1" home_dir="$2" stow_bin="$3"
|
||||||
|
shift 3
|
||||||
|
(
|
||||||
|
cd "$TEST_TMP_ROOT"
|
||||||
|
HOME="$home_dir" STOW_BIN="$stow_bin" \
|
||||||
|
"$package_dir/install.sh" "$@"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
test_installs_same_public_artifacts_without_stow() {
|
||||||
|
local fixture_root package_dir home_dir output
|
||||||
|
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
|
||||||
|
package_dir="$(setup_package "$fixture_root")"
|
||||||
|
home_dir="$fixture_root/home"
|
||||||
|
mkdir -p "$home_dir"
|
||||||
|
|
||||||
|
output="$(
|
||||||
|
run_package_install "$package_dir" "$home_dir" missing-stow --no-shell-setup
|
||||||
|
)"
|
||||||
|
|
||||||
|
printf '%s' "$output" | grep -Fq 'без Stow' \
|
||||||
|
|| fail "Expected direct-symlink method in output"
|
||||||
|
printf '%s' "$output" | grep -Fq \
|
||||||
|
'source ~/.config/proxy-client/shell.bash' \
|
||||||
|
|| fail "Expected direct shell-module hint with --no-shell-setup"
|
||||||
|
[ -d "$home_dir/.local/bin" ] && [ ! -L "$home_dir/.local/bin" ] \
|
||||||
|
|| fail "Expected ~/.local/bin to be a real directory"
|
||||||
|
[ -d "$home_dir/.config/proxy-client" ] \
|
||||||
|
&& [ ! -L "$home_dir/.config/proxy-client" ] \
|
||||||
|
|| fail "Expected proxy-client config home to be a real directory"
|
||||||
|
assert_link_points_to \
|
||||||
|
"$home_dir/.local/bin/proxy-client" \
|
||||||
|
"$package_dir/.local/bin/proxy-client"
|
||||||
|
assert_link_points_to \
|
||||||
|
"$home_dir/.config/proxy-client/shell.bash" \
|
||||||
|
"$package_dir/.config/proxy-client/shell.bash"
|
||||||
|
assert_link_points_to \
|
||||||
|
"$home_dir/.config/proxy-client/examples" \
|
||||||
|
"$package_dir/.config/proxy-client/examples"
|
||||||
|
assert_not_exists "$home_dir/.config/proxy-client/config"
|
||||||
|
assert_not_exists "$home_dir/.config/proxy-client/default.url"
|
||||||
|
assert_not_exists "$home_dir/README.md"
|
||||||
|
assert_not_exists "$home_dir/install.sh"
|
||||||
|
assert_not_exists "$home_dir/.bashrc"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_configures_bash_once_by_default() {
|
||||||
|
local fixture_root package_dir home_dir output marker_count path_count source_count
|
||||||
|
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
|
||||||
|
package_dir="$(setup_package "$fixture_root")"
|
||||||
|
home_dir="$fixture_root/home"
|
||||||
|
mkdir -p "$home_dir"
|
||||||
|
|
||||||
|
run_package_install "$package_dir" "$home_dir" missing-stow >/dev/null
|
||||||
|
output="$(run_package_install "$package_dir" "$home_dir" missing-stow)"
|
||||||
|
|
||||||
|
marker_count="$(grep -Fxc '# >>> proxy-client >>>' "$home_dir/.bashrc")"
|
||||||
|
path_count="$(grep -Fc 'export PATH="$HOME/.local/bin:$PATH"' "$home_dir/.bashrc")"
|
||||||
|
source_count="$(
|
||||||
|
grep -Fc \
|
||||||
|
'[ -f "$HOME/.config/proxy-client/shell.bash" ] && source "$HOME/.config/proxy-client/shell.bash"' \
|
||||||
|
"$home_dir/.bashrc"
|
||||||
|
)"
|
||||||
|
[ "$marker_count" -eq 1 ] || fail "Expected one managed Bash block"
|
||||||
|
[ "$path_count" -eq 1 ] || fail "Expected one ~/.local/bin PATH line"
|
||||||
|
[ "$source_count" -eq 1 ] || fail "Expected one shell module source line"
|
||||||
|
printf '%s' "$output" | grep -Fq 'source ~/.bashrc' \
|
||||||
|
|| fail "Expected current-session reload hint"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_installs_same_public_artifacts_with_stow() {
|
||||||
|
local fixture_root package_dir home_dir output
|
||||||
|
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
|
||||||
|
package_dir="$(setup_package "$fixture_root")"
|
||||||
|
home_dir="$fixture_root/home"
|
||||||
|
mkdir -p "$home_dir"
|
||||||
|
|
||||||
|
output="$(run_package_install "$package_dir" "$home_dir" stow --no-shell-setup)"
|
||||||
|
|
||||||
|
printf '%s' "$output" | grep -Fq 'с помощью Stow' \
|
||||||
|
|| fail "Expected Stow method in output"
|
||||||
|
[ -d "$home_dir/.local/bin" ] && [ ! -L "$home_dir/.local/bin" ] \
|
||||||
|
|| fail "Expected ~/.local/bin to be a real directory"
|
||||||
|
[ -d "$home_dir/.config/proxy-client" ] \
|
||||||
|
&& [ ! -L "$home_dir/.config/proxy-client" ] \
|
||||||
|
|| fail "Expected proxy-client config home to be a real directory"
|
||||||
|
assert_link_points_to \
|
||||||
|
"$home_dir/.local/bin/proxy-client" \
|
||||||
|
"$package_dir/.local/bin/proxy-client"
|
||||||
|
assert_link_points_to \
|
||||||
|
"$home_dir/.config/proxy-client/shell.bash" \
|
||||||
|
"$package_dir/.config/proxy-client/shell.bash"
|
||||||
|
assert_link_points_to \
|
||||||
|
"$home_dir/.config/proxy-client/examples" \
|
||||||
|
"$package_dir/.config/proxy-client/examples"
|
||||||
|
assert_not_exists "$home_dir/.config/proxy-client/config"
|
||||||
|
assert_not_exists "$home_dir/.config/proxy-client/default.url"
|
||||||
|
assert_not_exists "$home_dir/README.md"
|
||||||
|
assert_not_exists "$home_dir/install.sh"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_rejects_linked_parent_directories_before_changes() {
|
||||||
|
local fixture_root package_dir home_dir external_local external_config output
|
||||||
|
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
|
||||||
|
package_dir="$(setup_package "$fixture_root")"
|
||||||
|
home_dir="$fixture_root/home"
|
||||||
|
external_local="$fixture_root/external-local"
|
||||||
|
external_config="$fixture_root/external-config"
|
||||||
|
mkdir -p "$home_dir" "$external_local" "$external_config"
|
||||||
|
ln -s "$external_local" "$home_dir/.local"
|
||||||
|
ln -s "$external_config" "$home_dir/.config"
|
||||||
|
|
||||||
|
if output="$(
|
||||||
|
run_package_install "$package_dir" "$home_dir" missing-stow 2>&1
|
||||||
|
)"; then
|
||||||
|
fail "Expected linked parent directories to be rejected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s' "$output" | grep -Fq "$home_dir/.local" \
|
||||||
|
|| fail "Expected ~/.local conflict in output"
|
||||||
|
printf '%s' "$output" | grep -Fq "$home_dir/.config" \
|
||||||
|
|| fail "Expected ~/.config conflict in output"
|
||||||
|
assert_not_exists "$external_local/bin"
|
||||||
|
assert_not_exists "$external_config/proxy-client"
|
||||||
|
assert_not_exists "$home_dir/.bashrc"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_reports_all_target_conflicts_without_partial_install() {
|
||||||
|
local fixture_root package_dir home_dir other_target output
|
||||||
|
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
|
||||||
|
package_dir="$(setup_package "$fixture_root")"
|
||||||
|
home_dir="$fixture_root/home"
|
||||||
|
other_target="$fixture_root/other-target"
|
||||||
|
mkdir -p "$home_dir/.local/bin" "$home_dir/.config/proxy-client/shell.bash"
|
||||||
|
printf '%s\n' 'KEEP_CLI' > "$home_dir/.local/bin/proxy-client"
|
||||||
|
printf '%s\n' 'KEEP_TARGET' > "$other_target"
|
||||||
|
ln -s "$other_target" "$home_dir/.config/proxy-client/examples"
|
||||||
|
|
||||||
|
if output="$(
|
||||||
|
run_package_install "$package_dir" "$home_dir" missing-stow 2>&1
|
||||||
|
)"; then
|
||||||
|
fail "Expected all target conflicts to be rejected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s' "$output" | grep -Fq "$home_dir/.local/bin/proxy-client" \
|
||||||
|
|| fail "Expected CLI conflict in output"
|
||||||
|
printf '%s' "$output" | grep -Fq "$home_dir/.config/proxy-client/shell.bash" \
|
||||||
|
|| fail "Expected shell module conflict in output"
|
||||||
|
printf '%s' "$output" | grep -Fq "$home_dir/.config/proxy-client/examples" \
|
||||||
|
|| fail "Expected examples conflict in output"
|
||||||
|
grep -Fq 'KEEP_CLI' "$home_dir/.local/bin/proxy-client" \
|
||||||
|
|| fail "Expected conflicting CLI file to stay unchanged"
|
||||||
|
[ -d "$home_dir/.config/proxy-client/shell.bash" ] \
|
||||||
|
|| fail "Expected conflicting shell directory to stay unchanged"
|
||||||
|
assert_link_points_to "$home_dir/.config/proxy-client/examples" "$other_target"
|
||||||
|
assert_not_exists "$home_dir/.bashrc"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_rejects_broken_link_without_creating_other_targets() {
|
||||||
|
local fixture_root package_dir home_dir output
|
||||||
|
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
|
||||||
|
package_dir="$(setup_package "$fixture_root")"
|
||||||
|
home_dir="$fixture_root/home"
|
||||||
|
mkdir -p "$home_dir/.local/bin"
|
||||||
|
ln -s "$fixture_root/missing-cli" "$home_dir/.local/bin/proxy-client"
|
||||||
|
|
||||||
|
if output="$(
|
||||||
|
run_package_install "$package_dir" "$home_dir" missing-stow 2>&1
|
||||||
|
)"; then
|
||||||
|
fail "Expected broken target link to be rejected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s' "$output" | grep -Fq "$home_dir/.local/bin/proxy-client" \
|
||||||
|
|| fail "Expected broken CLI link in output"
|
||||||
|
[ -L "$home_dir/.local/bin/proxy-client" ] \
|
||||||
|
|| fail "Expected broken CLI link to stay unchanged"
|
||||||
|
assert_not_exists "$home_dir/.config"
|
||||||
|
assert_not_exists "$home_dir/.bashrc"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_preserves_machine_files_with_both_methods() {
|
||||||
|
local stow_bin fixture_root package_dir home_dir
|
||||||
|
for stow_bin in missing-stow stow; do
|
||||||
|
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
|
||||||
|
package_dir="$(setup_package "$fixture_root")"
|
||||||
|
home_dir="$fixture_root/home"
|
||||||
|
mkdir -p "$home_dir/.config/proxy-client"
|
||||||
|
printf '%s\n' 'HOME_CONFIG_SENTINEL' \
|
||||||
|
> "$home_dir/.config/proxy-client/config"
|
||||||
|
printf '%s\n' 'HOME_PROFILE_SENTINEL' \
|
||||||
|
> "$home_dir/.config/proxy-client/default.url"
|
||||||
|
|
||||||
|
run_package_install "$package_dir" "$home_dir" "$stow_bin" \
|
||||||
|
--no-shell-setup >/dev/null
|
||||||
|
|
||||||
|
[ ! -L "$home_dir/.config/proxy-client/config" ] \
|
||||||
|
|| fail "Machine config must not become a symlink"
|
||||||
|
[ ! -L "$home_dir/.config/proxy-client/default.url" ] \
|
||||||
|
|| fail "Private profile must not become a symlink"
|
||||||
|
grep -Fq 'HOME_CONFIG_SENTINEL' "$home_dir/.config/proxy-client/config" \
|
||||||
|
|| fail "Machine config changed"
|
||||||
|
grep -Fq 'HOME_PROFILE_SENTINEL' "$home_dir/.config/proxy-client/default.url" \
|
||||||
|
|| fail "Private profile changed"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
test_accepts_existing_links_when_method_changes() {
|
||||||
|
local fixture_root package_dir home_dir
|
||||||
|
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
|
||||||
|
package_dir="$(setup_package "$fixture_root")"
|
||||||
|
home_dir="$fixture_root/home"
|
||||||
|
mkdir -p "$home_dir"
|
||||||
|
|
||||||
|
run_package_install "$package_dir" "$home_dir" missing-stow \
|
||||||
|
--no-shell-setup >/dev/null
|
||||||
|
run_package_install "$package_dir" "$home_dir" stow \
|
||||||
|
--no-shell-setup >/dev/null
|
||||||
|
|
||||||
|
assert_link_points_to \
|
||||||
|
"$home_dir/.local/bin/proxy-client" \
|
||||||
|
"$package_dir/.local/bin/proxy-client"
|
||||||
|
assert_link_points_to \
|
||||||
|
"$home_dir/.config/proxy-client/shell.bash" \
|
||||||
|
"$package_dir/.config/proxy-client/shell.bash"
|
||||||
|
assert_link_points_to \
|
||||||
|
"$home_dir/.config/proxy-client/examples" \
|
||||||
|
"$package_dir/.config/proxy-client/examples"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_stow_completes_partial_correct_installation() {
|
||||||
|
local fixture_root package_dir home_dir
|
||||||
|
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
|
||||||
|
package_dir="$(setup_package "$fixture_root")"
|
||||||
|
home_dir="$fixture_root/home"
|
||||||
|
mkdir -p "$home_dir"
|
||||||
|
|
||||||
|
run_package_install "$package_dir" "$home_dir" missing-stow \
|
||||||
|
--no-shell-setup >/dev/null
|
||||||
|
rm "$home_dir/.config/proxy-client/shell.bash"
|
||||||
|
|
||||||
|
run_package_install "$package_dir" "$home_dir" stow \
|
||||||
|
--no-shell-setup >/dev/null
|
||||||
|
|
||||||
|
assert_link_points_to \
|
||||||
|
"$home_dir/.local/bin/proxy-client" \
|
||||||
|
"$package_dir/.local/bin/proxy-client"
|
||||||
|
assert_link_points_to \
|
||||||
|
"$home_dir/.config/proxy-client/shell.bash" \
|
||||||
|
"$package_dir/.config/proxy-client/shell.bash"
|
||||||
|
assert_link_points_to \
|
||||||
|
"$home_dir/.config/proxy-client/examples" \
|
||||||
|
"$package_dir/.config/proxy-client/examples"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_does_not_duplicate_full_dotfiles_shell_setup() {
|
||||||
|
local fixture_root package_dir home_dir source_count
|
||||||
|
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
|
||||||
|
package_dir="$(setup_package "$fixture_root")"
|
||||||
|
home_dir="$fixture_root/home"
|
||||||
|
mkdir -p "$home_dir"
|
||||||
|
printf '%s\n' \
|
||||||
|
'[ -f ~/.config/proxy-client/shell.bash ] && source ~/.config/proxy-client/shell.bash' \
|
||||||
|
> "$home_dir/.bash_custom"
|
||||||
|
printf '%s\n' '[ -f ~/.bash_custom ] && source ~/.bash_custom' \
|
||||||
|
> "$home_dir/.bashrc"
|
||||||
|
|
||||||
|
run_package_install "$package_dir" "$home_dir" missing-stow >/dev/null
|
||||||
|
|
||||||
|
grep -Fq '# >>> proxy-client >>>' "$home_dir/.bashrc" \
|
||||||
|
|| fail "Expected managed PATH block with full dotfiles"
|
||||||
|
grep -Fq 'export PATH="$HOME/.local/bin:$PATH"' "$home_dir/.bashrc" \
|
||||||
|
|| fail "Expected ~/.local/bin setup with full dotfiles"
|
||||||
|
source_count="$(grep -Fc 'proxy-client/shell.bash' "$home_dir/.bashrc" || true)"
|
||||||
|
[ "$source_count" -eq 0 ] \
|
||||||
|
|| fail "Standalone block duplicated full dotfiles module source"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_rejects_damaged_managed_bash_block_before_changes() {
|
||||||
|
local fixture_root package_dir home_dir output
|
||||||
|
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
|
||||||
|
package_dir="$(setup_package "$fixture_root")"
|
||||||
|
home_dir="$fixture_root/home"
|
||||||
|
mkdir -p "$home_dir"
|
||||||
|
printf '%s\n' '# >>> proxy-client >>>' > "$home_dir/.bashrc"
|
||||||
|
|
||||||
|
if output="$(
|
||||||
|
run_package_install "$package_dir" "$home_dir" missing-stow 2>&1
|
||||||
|
)"; then
|
||||||
|
fail "Expected damaged managed Bash block to be rejected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s' "$output" | grep -Fq "$home_dir/.bashrc" \
|
||||||
|
|| fail "Expected ~/.bashrc conflict in output"
|
||||||
|
[ "$(wc -l < "$home_dir/.bashrc")" -eq 1 ] \
|
||||||
|
|| fail "Expected damaged Bash block to stay unchanged"
|
||||||
|
assert_not_exists "$home_dir/.local"
|
||||||
|
assert_not_exists "$home_dir/.config"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_does_not_duplicate_existing_local_bin_path() {
|
||||||
|
local fixture_root package_dir home_dir path_count source_count
|
||||||
|
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
|
||||||
|
package_dir="$(setup_package "$fixture_root")"
|
||||||
|
home_dir="$fixture_root/home"
|
||||||
|
mkdir -p "$home_dir"
|
||||||
|
printf '%s\n' 'export PATH="$HOME/.local/bin:$PATH"' > "$home_dir/.bashrc"
|
||||||
|
|
||||||
|
run_package_install "$package_dir" "$home_dir" missing-stow >/dev/null
|
||||||
|
|
||||||
|
path_count="$(grep -Fc 'export PATH="$HOME/.local/bin:$PATH"' "$home_dir/.bashrc")"
|
||||||
|
source_count="$(grep -Fc 'proxy-client/shell.bash' "$home_dir/.bashrc")"
|
||||||
|
[ "$path_count" -eq 1 ] || fail "Existing ~/.local/bin PATH line was duplicated"
|
||||||
|
[ "$source_count" -eq 1 ] || fail "Expected shell module source in managed block"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_rejects_incomplete_source_before_changes() {
|
||||||
|
local fixture_root package_dir home_dir output
|
||||||
|
fixture_root="$(mktemp -d "$TEST_TMP_ROOT/fixture.XXXXXX")"
|
||||||
|
package_dir="$(setup_package "$fixture_root")"
|
||||||
|
home_dir="$fixture_root/home"
|
||||||
|
mkdir -p "$home_dir"
|
||||||
|
rm "$package_dir/.config/proxy-client/shell.bash"
|
||||||
|
|
||||||
|
if output="$(
|
||||||
|
run_package_install "$package_dir" "$home_dir" missing-stow 2>&1
|
||||||
|
)"; then
|
||||||
|
fail "Expected incomplete source package to be rejected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s' "$output" | grep -Fq 'исходного пакета' \
|
||||||
|
|| fail "Expected source package error"
|
||||||
|
assert_not_exists "$home_dir/.local"
|
||||||
|
assert_not_exists "$home_dir/.config"
|
||||||
|
assert_not_exists "$home_dir/.bashrc"
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
test_installs_same_public_artifacts_without_stow
|
||||||
|
test_configures_bash_once_by_default
|
||||||
|
test_installs_same_public_artifacts_with_stow
|
||||||
|
test_rejects_linked_parent_directories_before_changes
|
||||||
|
test_reports_all_target_conflicts_without_partial_install
|
||||||
|
test_rejects_broken_link_without_creating_other_targets
|
||||||
|
test_preserves_machine_files_with_both_methods
|
||||||
|
test_accepts_existing_links_when_method_changes
|
||||||
|
test_stow_completes_partial_correct_installation
|
||||||
|
test_does_not_duplicate_full_dotfiles_shell_setup
|
||||||
|
test_rejects_damaged_managed_bash_block_before_changes
|
||||||
|
test_does_not_duplicate_existing_local_bin_path
|
||||||
|
test_rejects_incomplete_source_before_changes
|
||||||
|
echo "All standalone proxy-client install tests passed"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Reference in new issue
Block a user