fix(install): устранены конфликты stow в WSL и opencode
- Зачем: - нужно было убрать падение install.sh на абсолютных WSL-симлинках и существующей runtime-директории opencode. - Что: - убран `--restow` из вызова stow, чтобы install не падал на ссылках `~/.aws` и `~/.azure`. - настройка `~/.config/opencode` переведена на ручную установку с backup существующего `opencode.json` и сохранением runtime-файлов. - в tests/test_install.sh добавлены сценарии для WSL-симлинков и существующей директории opencode. - Проверка: - bash -n install.sh - bash -n tests/test_install.sh - ./tests/test_install.sh
This commit is contained in:
1 parent
edeb8da3c2
commit
22d38f80e5
2 files changed
+111
-3
No files matched your search
+70
-3
@@ -19,7 +19,7 @@ warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
|||||||
error() { echo -e "${RED}[✗]${NC} $*"; }
|
error() { echo -e "${RED}[✗]${NC} $*"; }
|
||||||
|
|
||||||
stow_package() {
|
stow_package() {
|
||||||
"$STOW_BIN" --target="$HOME" -v --restow "$@" 2>&1 | sed "s/^/ /"
|
"$STOW_BIN" --target="$HOME" -v "$@" 2>&1 | sed "s/^/ /"
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_symlink() {
|
ensure_symlink() {
|
||||||
@@ -56,6 +56,53 @@ ensure_symlink() {
|
|||||||
ln -s "$src" "$dst"
|
ln -s "$src" "$dst"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backup_existing_path() {
|
||||||
|
local path="$1"
|
||||||
|
local label="$2"
|
||||||
|
local backup_path
|
||||||
|
|
||||||
|
backup_path="${path}.bak.$(date +%Y%m%d%H%M%S)"
|
||||||
|
if [ -e "$backup_path" ] || [ -L "$backup_path" ]; then
|
||||||
|
backup_path="${backup_path}.$$"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mv "$path" "$backup_path"
|
||||||
|
warn "$label уже существовал, сохранил резервную копию: $backup_path"
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_managed_file() {
|
||||||
|
local src="$1"
|
||||||
|
local dst="$2"
|
||||||
|
local label="$3"
|
||||||
|
local src_real=""
|
||||||
|
local dst_real=""
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$dst")"
|
||||||
|
src_real="$(readlink -f "$src" 2>/dev/null || printf '%s' "$src")"
|
||||||
|
|
||||||
|
if [ -L "$dst" ]; then
|
||||||
|
dst_real="$(readlink -f "$dst" 2>/dev/null || true)"
|
||||||
|
if [ "$dst_real" = "$src_real" ]; then
|
||||||
|
rm "$dst"
|
||||||
|
else
|
||||||
|
backup_existing_path "$dst" "$label"
|
||||||
|
fi
|
||||||
|
elif [ -f "$dst" ]; then
|
||||||
|
if cmp -s "$src" "$dst"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
backup_existing_path "$dst" "$label"
|
||||||
|
elif [ -d "$dst" ]; then
|
||||||
|
warn "$dst уже существует как директория, пропускаю замену"
|
||||||
|
return 0
|
||||||
|
elif [ -e "$dst" ]; then
|
||||||
|
warn "$dst уже существует и не является обычным файлом, пропускаю замену"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp -f "$src" "$dst"
|
||||||
|
}
|
||||||
|
|
||||||
is_interactive() {
|
is_interactive() {
|
||||||
case "$INSTALL_INTERACTIVE_MODE" in
|
case "$INSTALL_INTERACTIVE_MODE" in
|
||||||
always) return 0 ;;
|
always) return 0 ;;
|
||||||
@@ -75,7 +122,7 @@ if ! command -v "$STOW_BIN" &>/dev/null; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Stow пакеты ---
|
# --- Stow пакеты ---
|
||||||
PACKAGES=(bash tmux vim zed vscode agents opencode codex gemini)
|
PACKAGES=(bash tmux vim zed vscode agents codex gemini)
|
||||||
AVAILABLE_PACKAGES=()
|
AVAILABLE_PACKAGES=()
|
||||||
|
|
||||||
info "Применяю stow пакеты..."
|
info "Применяю stow пакеты..."
|
||||||
@@ -92,6 +139,27 @@ if [ "${#AVAILABLE_PACKAGES[@]}" -gt 0 ]; then
|
|||||||
stow_package "${AVAILABLE_PACKAGES[@]}"
|
stow_package "${AVAILABLE_PACKAGES[@]}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
SKILLS_SRC="$DOTFILES_DIR/agents/.agents/skills"
|
||||||
|
|
||||||
|
# --- Opencode: установка без stow ---
|
||||||
|
OPENCODE_DIR="$HOME/.config/opencode"
|
||||||
|
OPENCODE_SRC_DIR="$DOTFILES_DIR/opencode/.config/opencode"
|
||||||
|
OPENCODE_JSON_SRC="$OPENCODE_SRC_DIR/opencode.json"
|
||||||
|
|
||||||
|
mkdir -p "$OPENCODE_DIR"
|
||||||
|
|
||||||
|
if [ -d "$OPENCODE_SRC_DIR/agents" ]; then
|
||||||
|
ensure_symlink "$OPENCODE_SRC_DIR/agents" "$OPENCODE_DIR/agents"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$SKILLS_SRC" ]; then
|
||||||
|
ensure_symlink "$SKILLS_SRC" "$OPENCODE_DIR/skills"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$OPENCODE_JSON_SRC" ]; then
|
||||||
|
copy_managed_file "$OPENCODE_JSON_SRC" "$OPENCODE_DIR/opencode.json" "opencode.json"
|
||||||
|
fi
|
||||||
|
|
||||||
# --- Claude Code: установка без stow ---
|
# --- Claude Code: установка без stow ---
|
||||||
# settings.json КОПИРУЕТСЯ (не линкуется), чтобы Claude Code не модифицировал
|
# settings.json КОПИРУЕТСЯ (не линкуется), чтобы Claude Code не модифицировал
|
||||||
# файл в репозитории. Локальные настройки — в settings.local.json.
|
# файл в репозитории. Локальные настройки — в settings.local.json.
|
||||||
@@ -120,7 +188,6 @@ fi
|
|||||||
mkdir -p "$HOME/.claude"
|
mkdir -p "$HOME/.claude"
|
||||||
|
|
||||||
# Skills (симлинк)
|
# Skills (симлинк)
|
||||||
SKILLS_SRC="$DOTFILES_DIR/agents/.agents/skills"
|
|
||||||
if [ -d "$SKILLS_SRC" ]; then
|
if [ -d "$SKILLS_SRC" ]; then
|
||||||
ensure_symlink "$SKILLS_SRC" "$HOME/.claude/skills"
|
ensure_symlink "$SKILLS_SRC" "$HOME/.claude/skills"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -162,6 +162,29 @@ test_preserves_nonempty_skills_directory() {
|
|||||||
[ -f "$home_dir/.claude/skills/local.txt" ] || fail "Expected local skill data to be preserved"
|
[ -f "$home_dir/.claude/skills/local.txt" ] || fail "Expected local skill data to be preserved"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_opencode_runtime_dir_is_preserved_and_config_is_managed() {
|
||||||
|
local fixture repo_root home_dir backup_path
|
||||||
|
fixture="$(setup_fixture)"
|
||||||
|
repo_root="$fixture/repo"
|
||||||
|
home_dir="$fixture/home"
|
||||||
|
mkdir -p "$home_dir/.config/opencode"
|
||||||
|
echo runtime > "$home_dir/.config/opencode/package.json"
|
||||||
|
cat > "$home_dir/.config/opencode/opencode.json" <<'EOF'
|
||||||
|
{"source":"local"}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >/tmp/test-install.out
|
||||||
|
|
||||||
|
[ -f "$home_dir/.config/opencode/package.json" ] || fail "Expected opencode runtime files to be preserved"
|
||||||
|
[ -L "$home_dir/.config/opencode/agents" ] || fail "Expected ~/.config/opencode/agents symlink"
|
||||||
|
[ -L "$home_dir/.config/opencode/skills" ] || fail "Expected ~/.config/opencode/skills symlink"
|
||||||
|
assert_file_contains "$home_dir/.config/opencode/opencode.json" '"$schema": "https://opencode.ai/config.json"'
|
||||||
|
|
||||||
|
backup_path="$(find "$home_dir/.config/opencode" -maxdepth 1 -name 'opencode.json.bak.*' -print -quit)"
|
||||||
|
[ -n "$backup_path" ] || fail "Expected backup for existing opencode.json"
|
||||||
|
assert_file_contains "$backup_path" '"source":"local"'
|
||||||
|
}
|
||||||
|
|
||||||
test_broken_claude_symlink_does_not_fail() {
|
test_broken_claude_symlink_does_not_fail() {
|
||||||
local fixture repo_root home_dir
|
local fixture repo_root home_dir
|
||||||
fixture="$(setup_fixture)"
|
fixture="$(setup_fixture)"
|
||||||
@@ -176,6 +199,22 @@ test_broken_claude_symlink_does_not_fail() {
|
|||||||
[ ! -L "$home_dir/.claude" ] || fail "Expected ~/.claude to stop being a symlink"
|
[ ! -L "$home_dir/.claude" ] || fail "Expected ~/.claude to stop being a symlink"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_absolute_symlinks_in_home_do_not_break_install() {
|
||||||
|
local fixture repo_root home_dir
|
||||||
|
fixture="$(setup_fixture)"
|
||||||
|
repo_root="$fixture/repo"
|
||||||
|
home_dir="$fixture/home"
|
||||||
|
mkdir -p "$home_dir" "$fixture/windows/.aws" "$fixture/windows/.azure"
|
||||||
|
ln -s "$fixture/windows/.aws" "$home_dir/.aws"
|
||||||
|
ln -s "$fixture/windows/.azure" "$home_dir/.azure"
|
||||||
|
|
||||||
|
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 >/tmp/test-install.out
|
||||||
|
|
||||||
|
[ -L "$home_dir/.aws" ] || fail "Expected ~/.aws symlink to survive install"
|
||||||
|
[ -L "$home_dir/.azure" ] || fail "Expected ~/.azure symlink to survive install"
|
||||||
|
}
|
||||||
|
|
||||||
test_wsl_noninteractive_skips_file_changes() {
|
test_wsl_noninteractive_skips_file_changes() {
|
||||||
local fixture repo_root home_dir wsl_conf output
|
local fixture repo_root home_dir wsl_conf output
|
||||||
fixture="$(setup_fixture)"
|
fixture="$(setup_fixture)"
|
||||||
@@ -246,7 +285,9 @@ main() {
|
|||||||
test_rerun_does_not_duplicate_bashrc_line
|
test_rerun_does_not_duplicate_bashrc_line
|
||||||
test_repairs_nested_skills_directory_bug
|
test_repairs_nested_skills_directory_bug
|
||||||
test_preserves_nonempty_skills_directory
|
test_preserves_nonempty_skills_directory
|
||||||
|
test_opencode_runtime_dir_is_preserved_and_config_is_managed
|
||||||
test_broken_claude_symlink_does_not_fail
|
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_skips_file_changes
|
||||||
test_wsl_interactive_adds_boot_command
|
test_wsl_interactive_adds_boot_command
|
||||||
test_wsl_existing_boot_command_is_preserved
|
test_wsl_existing_boot_command_is_preserved
|
||||||
|
|||||||
Reference in new issue
Block a user