fix(install): исправлено владение каталогом .config
Зачем: - GNU Stow сворачивал весь ~/.config в пакет zed и помещал рабочие настройки в репозиторий. Что: - добавлена ограниченная миграция старой ссылки с возвратом данных в домашний каталог; - предотвращено повторное сворачивание ~/.config на свежей установке; - добавлены проверки сохранности профиля, повторного запуска и чужих ссылок. Проверка: - bash tests/test_install.sh - bash -n install.sh tests/test_install.sh - git diff --check
This commit is contained in:
1 parent
fd8210db71
commit
e5323284a8
3 files changed
+231
-48
No files matched your search
@@ -1,62 +1,54 @@
|
||||
# Handoff: headless-xray-proxy — задача 05 готова к ручной приёмке
|
||||
# Handoff: владение `~/.config` исправлено, задача 05 готова к приёмке
|
||||
|
||||
Дата: 2026-07-21 07:43 EDT. Заменяет прежние handoff задачи 05.
|
||||
Обновлён: 2026-07-21 08:18 EDT.
|
||||
|
||||
## Состояние
|
||||
## Состояние задачи 05
|
||||
|
||||
Автоматическая часть `headless-xray-proxy` зафиксирована коммитом `fd8210d`.
|
||||
Тикет `.scratch/headless-xray-proxy/issues/05-package-delivery-and-acceptance.md`
|
||||
переведён в `ready-for-human`. Автоматическая часть выполнена; продуктовый CLI
|
||||
менять не потребовалось.
|
||||
находится в `ready-for-human`; результаты тестов и команды ручной приёмки уже
|
||||
записаны там. Дальше можно продолжать ручную приёмку по этому тикету.
|
||||
|
||||
## Что изменено
|
||||
## Исправленный дефект stow
|
||||
|
||||
- Корневой `README.md` описывает готовый управляемый Xray и отдельное
|
||||
подключение через `stow xray-proxy`.
|
||||
- `xray-proxy/README.md` явно фиксирует отсутствие systemd, автозапуска и
|
||||
автоматического перехода между прокси.
|
||||
- `tests/test_xray_proxy_full.sh` запускает полный набор под системным Python и
|
||||
CPython 3.8 через `uv`, а также Bash-тесты и проверку установки.
|
||||
- Поддельный Xray в `tests/test_xray_proxy_managed.py` сохраняет отдельный
|
||||
исполняемый файл и получает доступ к библиотекам исходного Python. Это
|
||||
устраняет ложные падения переносимой сборки `uv`, не ломая тест удалённого
|
||||
исполняемого файла.
|
||||
- `tests/test_install.sh` создаёт тестовые деревья только из отслеживаемых
|
||||
Git-файлов внутри одного временного каталога и удаляет его при выходе.
|
||||
Корневая причина была в сворачивании GNU Stow: в пустом домашнем каталоге пакет
|
||||
`zed` создавал общую ссылку `~/.config -> ~/dotfiles/zed/.config`. После этого
|
||||
рабочие настройки приложений и закрытый профиль Xray физически попадали внутрь
|
||||
репозитория.
|
||||
|
||||
## Проверка
|
||||
`install.sh` теперь:
|
||||
|
||||
`bash tests/test_xray_proxy_full.sh` завершился с кодом 0:
|
||||
- заранее создаёт настоящий каталог `~/.config`, поэтому свежая установка
|
||||
создаёт только ссылку `~/.config/zed`;
|
||||
- распознаёт старую ссылку лишь при точном совпадении с
|
||||
`$DOTFILES_DIR/zed/.config`;
|
||||
- оставляет управляемый каталог `zed` в пакете, а остальные верхнеуровневые
|
||||
элементы переносит в настоящий `~/.config`;
|
||||
- до первого переноса проверяет, что смог полностью перечислить данные;
|
||||
- при штатной ошибке переноса возвращает уже перемещённые элементы назад;
|
||||
- не раскрывает произвольные ссылки `~/.config`.
|
||||
|
||||
- системный Python 3.14: 79 тестов, `OK`;
|
||||
- Bash-интеграция: 8 проверок, `PASS`;
|
||||
- установка: `All install tests passed`, остатков `dotfiles-install-test.*` в
|
||||
`/tmp` нет;
|
||||
- CPython 3.8.20 через `uv`: 79 тестов, `OK`.
|
||||
В `tests/test_install.sh` добавлены проверки свежей установки, миграции с
|
||||
вымышленным секретом, сохранения прав `600`, отсутствия секрета в выводе,
|
||||
повторного запуска и неприкосновенности произвольной ссылки.
|
||||
|
||||
Двухосевое ревью относительно `92cf339`:
|
||||
## Проверка и живое состояние
|
||||
|
||||
- Standards: 3 документационных замечания исправлены, повторная проверка PASS;
|
||||
- Spec: 2 документационных замечания исправлены, повторная проверка PASS;
|
||||
- запахов кода и незапрошенного расширения области не найдено.
|
||||
Полный набор `bash tests/test_xray_proxy_full.sh` прошёл на системном Python и
|
||||
Python 3.8 через `uv`: по 79 Python-тестов, 8 проверок командной оболочки и тесты
|
||||
установщика. Синтаксис сценариев командной оболочки и `git diff --check` также
|
||||
прошли.
|
||||
|
||||
## Что осталось пользователю
|
||||
Миграция применена к текущему домашнему каталогу:
|
||||
|
||||
Выполнить ручные команды из раздела `## Comments` тикета 05:
|
||||
- `~/.config` — настоящий каталог;
|
||||
- `~/.config/zed` указывает на пакет Zed;
|
||||
- в `zed/.config` остался только каталог `zed`;
|
||||
- рабочие каталоги приложений находятся в `~/.config`;
|
||||
- `~/.config/xray-proxy/default.url` — обычный файл вне репозитория, сохранил
|
||||
права `600` и одну непустую строку. Содержимое профиля не выводилось.
|
||||
|
||||
1. На доступной Ubuntu с Python 3.10 — установка, жизненный цикл,
|
||||
`xray run -test`, HTTP и SOCKS через один локальный вход.
|
||||
2. На РЕД ОС, Astra Linux или Alt Linux по доступности — тот же сценарий после
|
||||
проверки версии Python.
|
||||
## Текущее состояние Git
|
||||
|
||||
В отчёт не копировать VLESS-ссылку, UUID, ключи, `shortId`, `path` и
|
||||
сгенерированный JSON. Достаточно версии системы/Python, кодов возврата и итога
|
||||
HTTP/SOCKS. Недоступную систему отметить как неподтверждённую.
|
||||
|
||||
## После ручного результата
|
||||
|
||||
1. Отметить только фактически выполненные пункты тикета 05.
|
||||
2. Обновить статус исходного дизайна и заявления о поддержке.
|
||||
3. Обновить или удалить этот handoff.
|
||||
4. По навыку `implement` выполнить итоговую проверку и conventional commit.
|
||||
|
||||
До ручной приёмки коммита задачи 05 нет.
|
||||
Не включать в коммиты посторонние неотслеживаемые
|
||||
`agents/.agents/.skill-lock.json` и `scripts/setup-xray-proxy.sh`.
|
||||
+77
@@ -169,6 +169,77 @@ migrate_claude_stow_symlink() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Миграция: раскрыть старую stow-ссылку ~/.config -> zed/.config.
|
||||
# В пакете zed остаётся только управляемый каталог zed; остальные настройки
|
||||
# возвращаются в домашний каталог и больше не попадают в репозиторий.
|
||||
migrate_zed_config_stow_symlink() {
|
||||
local config_path="$HOME/.config"
|
||||
local zed_config="$DOTFILES_DIR/zed/.config"
|
||||
local config_real=""
|
||||
local zed_config_real=""
|
||||
local replacement=""
|
||||
local items_file=""
|
||||
local item=""
|
||||
local base=""
|
||||
local moved_items=()
|
||||
|
||||
[ -L "$config_path" ] || return 0
|
||||
|
||||
config_real="$(readlink -f "$config_path" 2>/dev/null || true)"
|
||||
zed_config_real="$(readlink -f "$zed_config" 2>/dev/null || true)"
|
||||
if [ -z "$zed_config_real" ] || [ "$config_real" != "$zed_config_real" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
info "Миграция: ~/.config больше не должен принадлежать пакету zed..."
|
||||
replacement="$(mktemp -d "$HOME/.config.migrate.XXXXXX")"
|
||||
chmod --reference="$zed_config_real" "$replacement"
|
||||
|
||||
items_file="$(mktemp "$HOME/.config.migrate-items.XXXXXX")"
|
||||
if ! find "$zed_config_real" -mindepth 1 -maxdepth 1 ! -name zed -print0 > "$items_file"; then
|
||||
error "Не удалось получить полный список старого ~/.config"
|
||||
rm -f -- "$items_file"
|
||||
rmdir "$replacement" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
|
||||
while IFS= read -r -d '' item; do
|
||||
base="${item##*/}"
|
||||
if ! mv -- "$item" "$replacement/$base"; then
|
||||
error "Не удалось перенести ~/.config/$base"
|
||||
for base in "${moved_items[@]}"; do
|
||||
mv -- "$replacement/$base" "$zed_config_real/$base" 2>/dev/null || true
|
||||
done
|
||||
rm -f -- "$items_file"
|
||||
rmdir "$replacement" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
moved_items+=("$base")
|
||||
done < "$items_file"
|
||||
rm -f -- "$items_file"
|
||||
|
||||
if ! rm -- "$config_path"; then
|
||||
error "Не удалось удалить старую ссылку ~/.config"
|
||||
for base in "${moved_items[@]}"; do
|
||||
mv -- "$replacement/$base" "$zed_config_real/$base" 2>/dev/null || true
|
||||
done
|
||||
rmdir "$replacement" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! mv -- "$replacement" "$config_path"; then
|
||||
error "Не удалось создать новый каталог ~/.config"
|
||||
ln -s "$zed_config_real" "$config_path" 2>/dev/null || true
|
||||
for base in "${moved_items[@]}"; do
|
||||
mv -- "$replacement/$base" "$zed_config_real/$base" 2>/dev/null || true
|
||||
done
|
||||
rmdir "$replacement" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
|
||||
info "Настройки перенесены из пакета zed в ~/.config"
|
||||
}
|
||||
|
||||
# Настроить boot command для hproxy.local в /etc/wsl.conf.
|
||||
setup_wsl_hproxy() {
|
||||
[ -n "${WSL_DISTRO_NAME:-}" ] || return 0
|
||||
@@ -244,6 +315,12 @@ for pkg in "${PACKAGES[@]}"; do
|
||||
fi
|
||||
done
|
||||
|
||||
migrate_zed_config_stow_symlink
|
||||
|
||||
if [ ! -L "$HOME/.config" ]; then
|
||||
mkdir -p "$HOME/.config"
|
||||
fi
|
||||
|
||||
if [ "${#AVAILABLE_PACKAGES[@]}" -gt 0 ]; then
|
||||
stow_package "${AVAILABLE_PACKAGES[@]}"
|
||||
fi
|
||||
|
||||
+115
-1
@@ -94,7 +94,7 @@ test_fresh_install_uses_home_target() {
|
||||
home_dir="$fixture/home"
|
||||
mkdir -p "$home_dir"
|
||||
|
||||
run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >/tmp/test-install.out
|
||||
run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out"
|
||||
|
||||
[ -L "$home_dir/.bash_custom" ] || fail "Expected .bash_custom symlink in HOME"
|
||||
[ -x "$home_dir/.local/bin/xray-proxy" ] || fail "Expected executable xray-proxy CLI in HOME"
|
||||
@@ -105,6 +105,116 @@ test_fresh_install_uses_home_target() {
|
||||
assert_not_exists "$fixture/.bash_custom"
|
||||
}
|
||||
|
||||
test_fresh_install_does_not_fold_config_into_zed() {
|
||||
local fixture repo_root home_dir target expected
|
||||
fixture="$(setup_fixture)"
|
||||
repo_root="$fixture/repo"
|
||||
home_dir="$fixture/home"
|
||||
mkdir -p "$home_dir"
|
||||
|
||||
# Неполный клон: zed остаётся единственным stow-пакетом с каталогом .config.
|
||||
mv "$repo_root/xray-proxy" "$fixture/xray-proxy-disabled"
|
||||
|
||||
run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out"
|
||||
|
||||
[ -d "$home_dir/.config" ] || fail "Expected ~/.config directory"
|
||||
[ ! -L "$home_dir/.config" ] || fail "Expected ~/.config to stay a real directory"
|
||||
[ -L "$home_dir/.config/zed" ] || fail "Expected ~/.config/zed symlink"
|
||||
target="$(readlink -f "$home_dir/.config/zed")"
|
||||
expected="$(readlink -f "$repo_root/zed/.config/zed")"
|
||||
[ "$target" = "$expected" ] || fail "~/.config/zed points to $target, expected $expected"
|
||||
}
|
||||
|
||||
test_migrates_legacy_zed_config_symlink() {
|
||||
local fixture repo_root home_dir legacy_config output mode
|
||||
fixture="$(setup_fixture)"
|
||||
repo_root="$fixture/repo"
|
||||
home_dir="$fixture/home"
|
||||
legacy_config="$repo_root/zed/.config"
|
||||
mkdir -p "$home_dir" "$legacy_config/btop" "$legacy_config/xray-proxy"
|
||||
printf '%s\n' 'btop settings' > "$legacy_config/btop/btop.conf"
|
||||
printf '%s\n' 'VLESS_PROFILE_SENTINEL' > "$legacy_config/xray-proxy/default.url"
|
||||
chmod 600 "$legacy_config/xray-proxy/default.url"
|
||||
ln -s "$legacy_config" "$home_dir/.config"
|
||||
|
||||
if ! output="$(
|
||||
run_install "$repo_root" "$home_dir" \
|
||||
env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never
|
||||
)"; then
|
||||
fail "Expected legacy ~/.config migration to complete"
|
||||
fi
|
||||
|
||||
[ -d "$home_dir/.config" ] || fail "Expected migrated ~/.config directory"
|
||||
[ ! -L "$home_dir/.config" ] || fail "Expected migrated ~/.config to stop being a symlink"
|
||||
[ -L "$home_dir/.config/zed" ] || fail "Expected managed ~/.config/zed symlink"
|
||||
[ -d "$home_dir/.config/btop" ] || fail "Expected btop config to be migrated"
|
||||
[ ! -L "$home_dir/.config/btop" ] || fail "Expected btop config outside the repository"
|
||||
[ -f "$home_dir/.config/xray-proxy/default.url" ] || fail "Expected profile to be migrated"
|
||||
[ ! -L "$home_dir/.config/xray-proxy/default.url" ] || fail "Expected profile outside the repository"
|
||||
mode="$(stat -c '%a' "$home_dir/.config/xray-proxy/default.url")"
|
||||
[ "$mode" = "600" ] || fail "Expected profile mode 600, got $mode"
|
||||
assert_file_contains "$home_dir/.config/xray-proxy/default.url" "VLESS_PROFILE_SENTINEL"
|
||||
assert_not_exists "$legacy_config/btop"
|
||||
assert_not_exists "$legacy_config/xray-proxy"
|
||||
if printf '%s' "$output" | grep -Fq 'VLESS_PROFILE_SENTINEL'; then
|
||||
fail "Secret profile leaked into install output"
|
||||
fi
|
||||
|
||||
run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out"
|
||||
assert_file_contains "$home_dir/.config/xray-proxy/default.url" "VLESS_PROFILE_SENTINEL"
|
||||
}
|
||||
|
||||
test_stops_legacy_migration_when_listing_fails() {
|
||||
local fixture repo_root home_dir legacy_config target expected
|
||||
fixture="$(setup_fixture)"
|
||||
repo_root="$fixture/repo"
|
||||
home_dir="$fixture/home"
|
||||
legacy_config="$repo_root/zed/.config"
|
||||
mkdir -p "$home_dir" "$legacy_config/xray-proxy"
|
||||
printf '%s\n' 'VLESS_PROFILE_SENTINEL' > "$legacy_config/xray-proxy/default.url"
|
||||
ln -s "$legacy_config" "$home_dir/.config"
|
||||
|
||||
cat > "$fixture/bin/find" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
exit 7
|
||||
EOF
|
||||
chmod +x "$fixture/bin/find"
|
||||
|
||||
if run_install "$repo_root" "$home_dir" \
|
||||
env PATH="$fixture/bin:$PATH" WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never \
|
||||
>"$fixture/install.out" 2>&1; then
|
||||
fail "Expected migration to stop when legacy config listing fails"
|
||||
fi
|
||||
|
||||
[ -L "$home_dir/.config" ] || fail "Expected legacy ~/.config symlink after listing failure"
|
||||
target="$(readlink -f "$home_dir/.config")"
|
||||
expected="$(readlink -f "$legacy_config")"
|
||||
[ "$target" = "$expected" ] || fail "Legacy ~/.config target changed after listing failure"
|
||||
assert_file_contains "$legacy_config/xray-proxy/default.url" "VLESS_PROFILE_SENTINEL"
|
||||
}
|
||||
|
||||
test_does_not_migrate_unrelated_config_symlink() {
|
||||
local fixture repo_root home_dir unrelated_config target expected
|
||||
fixture="$(setup_fixture)"
|
||||
repo_root="$fixture/repo"
|
||||
home_dir="$fixture/home"
|
||||
unrelated_config="$fixture/unrelated-config"
|
||||
mkdir -p "$home_dir" "$unrelated_config"
|
||||
printf '%s\n' 'keep me' > "$unrelated_config/local.conf"
|
||||
ln -s "$unrelated_config" "$home_dir/.config"
|
||||
|
||||
# Stow вправе сообщить о конфликте, но установщик не должен раскрывать
|
||||
# произвольную ссылку и переносить чужие данные.
|
||||
run_install "$repo_root" "$home_dir" \
|
||||
env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out" 2>&1 || true
|
||||
|
||||
[ -L "$home_dir/.config" ] || fail "Expected unrelated ~/.config symlink to be preserved"
|
||||
target="$(readlink -f "$home_dir/.config")"
|
||||
expected="$(readlink -f "$unrelated_config")"
|
||||
[ "$target" = "$expected" ] || fail "Unrelated ~/.config symlink target changed"
|
||||
assert_file_contains "$unrelated_config/local.conf" "keep me"
|
||||
}
|
||||
|
||||
test_settings_local_is_symlinked_to_repo() {
|
||||
local fixture repo_root home_dir
|
||||
fixture="$(setup_fixture)"
|
||||
@@ -385,6 +495,10 @@ main() {
|
||||
command -v stow >/dev/null 2>&1 || fail "stow is required for tests"
|
||||
|
||||
test_fresh_install_uses_home_target
|
||||
test_fresh_install_does_not_fold_config_into_zed
|
||||
test_migrates_legacy_zed_config_symlink
|
||||
test_stops_legacy_migration_when_listing_fails
|
||||
test_does_not_migrate_unrelated_config_symlink
|
||||
test_settings_local_is_symlinked_to_repo
|
||||
test_settings_local_backup_on_existing_file
|
||||
test_settings_local_dir_is_skipped
|
||||
|
||||
Reference in new issue
Block a user