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$
|
||||
^/install\.sh$
|
||||
^/\.config/proxy-client/config$
|
||||
^/\.config/proxy-client/default\.url$
|
||||
+23
-12
@@ -32,20 +32,31 @@ source ~/.bash_custom
|
||||
|
||||
### Только пакет `proxy-client`
|
||||
|
||||
```bash
|
||||
cd ~/dotfiles
|
||||
mkdir -p ~/.local/bin ~/.config/proxy-client
|
||||
stow proxy-client
|
||||
source ~/.config/proxy-client/shell.bash
|
||||
```
|
||||
|
||||
Если Bash-пакет dotfiles не установлен, добавьте подключение модуля в
|
||||
`~/.bashrc`:
|
||||
Каталог можно скопировать отдельно от остальных dotfiles и запустить его
|
||||
собственный установщик из любого рабочего каталога:
|
||||
|
||||
```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
|
||||
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
|
||||
bash tests/test_proxy_launchers.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
|
||||
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