fix(claude): settings.json копируется вместо симлинка через stow
- Зачем:
- stow tree-folding создавал симлинк ~/.claude → dotfiles/claude/.claude/,
из-за чего Claude Code модифицировал settings.json прямо в репозитории,
а рантайм-данные (sessions, credentials) попадали в каталог dotfiles.
- Что:
- claude убран из stow-пакетов, установка через ручную секцию в install.sh.
- settings.json копируется в ~/.claude/ (оригинал защищён chmod 444).
- добавлена автомиграция: симлинк → директория с переносом рантайм-данных.
- грязный settings.json в репо автоматически сбрасывается через git checkout.
- добавлен .stow-local-ignore для защиты от случайного stow claude.
- check.sh дополнен проверками: ~/.claude — директория, settings.json — копия.
- Проверка:
- bash install.sh (на машине с симлинком — мигрирует, иначе — идемпотентно).
- bash agents/.agents/skills/dotfiles-check/check.sh.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
769ef95096
commit
e00611015a
3 files changed
+79
-10
No files matched your search
@@ -28,6 +28,8 @@ echo "════════════════════════
|
|||||||
for pkg_dir in "$DOTFILES_DIR"/*/; do
|
for pkg_dir in "$DOTFILES_DIR"/*/; do
|
||||||
pkg=$(basename "$pkg_dir")
|
pkg=$(basename "$pkg_dir")
|
||||||
[[ "$pkg" == .* ]] && continue
|
[[ "$pkg" == .* ]] && continue
|
||||||
|
# claude управляется вручную (копирование settings.json), не через stow
|
||||||
|
[[ "$pkg" == "claude" ]] && continue
|
||||||
|
|
||||||
# Точки входа — элементы первого уровня внутри пакета
|
# Точки входа — элементы первого уровня внутри пакета
|
||||||
entries=()
|
entries=()
|
||||||
@@ -99,7 +101,25 @@ if [ -f "$install_script" ]; then
|
|||||||
echo ""
|
echo ""
|
||||||
echo "Доп. проверки (install.sh):"
|
echo "Доп. проверки (install.sh):"
|
||||||
|
|
||||||
# chmod 444 на settings.json
|
# Claude Code: ~/.claude должна быть директорией, не симлинком
|
||||||
|
if [ -L "$HOME/.claude" ]; then
|
||||||
|
err "~/.claude — симлинк (должна быть директория). Запустите install.sh"
|
||||||
|
elif [ -d "$HOME/.claude" ]; then
|
||||||
|
pass "~/.claude — директория (не симлинк)"
|
||||||
|
else
|
||||||
|
err "~/.claude — не найден"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Claude Code: settings.json должен быть копией, не симлинком
|
||||||
|
if [ -L "$HOME/.claude/settings.json" ]; then
|
||||||
|
err "~/.claude/settings.json — симлинк (должна быть копия)"
|
||||||
|
elif [ -f "$HOME/.claude/settings.json" ]; then
|
||||||
|
pass "~/.claude/settings.json — файл (копия)"
|
||||||
|
else
|
||||||
|
err "~/.claude/settings.json — не найден"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# chmod 444 на settings.json в репозитории
|
||||||
if [ -f "$DOTFILES_DIR/claude/.claude/settings.json" ]; then
|
if [ -f "$DOTFILES_DIR/claude/.claude/settings.json" ]; then
|
||||||
perms=$(stat -c %a "$DOTFILES_DIR/claude/.claude/settings.json" 2>/dev/null || true)
|
perms=$(stat -c %a "$DOTFILES_DIR/claude/.claude/settings.json" 2>/dev/null || true)
|
||||||
if [ "$perms" = "444" ]; then
|
if [ "$perms" = "444" ]; then
|
||||||
@@ -116,6 +136,13 @@ if [ -f "$install_script" ]; then
|
|||||||
skip "~/.claude/settings.local.json — не найден (создайте через install.sh)"
|
skip "~/.claude/settings.local.json — не найден (создайте через install.sh)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# skills симлинк
|
||||||
|
if [ -L "$HOME/.claude/skills" ]; then
|
||||||
|
pass "~/.claude/skills — симлинк"
|
||||||
|
else
|
||||||
|
skip "~/.claude/skills — не найден"
|
||||||
|
fi
|
||||||
|
|
||||||
# bashrc
|
# bashrc
|
||||||
if grep -q 'bash_custom' "$HOME/.bashrc" 2>/dev/null; then
|
if grep -q 'bash_custom' "$HOME/.bashrc" 2>/dev/null; then
|
||||||
pass "~/.bashrc содержит подключение bash_custom"
|
pass "~/.bashrc содержит подключение bash_custom"
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Claude Code управляется вручную через install.sh (копирование, не симлинк).
|
||||||
|
# Этот файл не даёт stow случайно создать симлинк ~/.claude.
|
||||||
|
\.claude
|
||||||
+48
-9
@@ -20,7 +20,7 @@ if ! command -v stow &>/dev/null; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Stow пакеты ---
|
# --- Stow пакеты ---
|
||||||
PACKAGES=(bash tmux vim zed vscode agents opencode codex claude gemini)
|
PACKAGES=(bash tmux vim zed vscode agents opencode codex gemini)
|
||||||
|
|
||||||
info "Применяю stow пакеты..."
|
info "Применяю stow пакеты..."
|
||||||
cd "$DOTFILES_DIR"
|
cd "$DOTFILES_DIR"
|
||||||
@@ -32,7 +32,53 @@ for pkg in "${PACKAGES[@]}"; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# --- Claude: settings.local.json ---
|
# --- Claude Code: установка без stow ---
|
||||||
|
# settings.json КОПИРУЕТСЯ (не линкуется), чтобы Claude Code не модифицировал
|
||||||
|
# файл в репозитории. Локальные настройки — в settings.local.json.
|
||||||
|
|
||||||
|
# Миграция: если ~/.claude — stow-симлинк, раскрываем в реальную директорию
|
||||||
|
if [ -L "$HOME/.claude" ]; then
|
||||||
|
info "Миграция: ~/.claude — симлинк, преобразую в директорию..."
|
||||||
|
CLAUDE_STOW_DIR=$(readlink -f "$HOME/.claude")
|
||||||
|
rm "$HOME/.claude"
|
||||||
|
mkdir -p "$HOME/.claude"
|
||||||
|
# Переносим рантайм-данные из stow-директории
|
||||||
|
for item in "$CLAUDE_STOW_DIR"/* "$CLAUDE_STOW_DIR"/.[!.]*; do
|
||||||
|
[ -e "$item" ] || continue
|
||||||
|
base=$(basename "$item")
|
||||||
|
# Пропускаем git-tracked файлы — они останутся в репозитории
|
||||||
|
case "$base" in .gitignore|settings.json|skills) continue ;; esac
|
||||||
|
mv "$item" "$HOME/.claude/" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
info "Рантайм-данные перенесены в ~/.claude/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$HOME/.claude"
|
||||||
|
|
||||||
|
# Skills (симлинк)
|
||||||
|
SKILLS_SRC="$DOTFILES_DIR/agents/.agents/skills"
|
||||||
|
if [ -d "$SKILLS_SRC" ]; then
|
||||||
|
ln -sfn "$SKILLS_SRC" "$HOME/.claude/skills"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# settings.json — копия из репозитория (не симлинк!)
|
||||||
|
CLAUDE_SETTINGS_SRC="$DOTFILES_DIR/claude/.claude/settings.json"
|
||||||
|
CLAUDE_SETTINGS_DST="$HOME/.claude/settings.json"
|
||||||
|
if [ -f "$CLAUDE_SETTINGS_SRC" ]; then
|
||||||
|
# Если settings.json в репо был изменён (например, Claude Code писал через симлинк) — сбрасываем
|
||||||
|
if cd "$DOTFILES_DIR" && ! git diff --quiet claude/.claude/settings.json 2>/dev/null; then
|
||||||
|
warn "claude/.claude/settings.json имеет локальные изменения — сбрасываю"
|
||||||
|
git checkout claude/.claude/settings.json
|
||||||
|
fi
|
||||||
|
# Защищаем оригинал в репозитории
|
||||||
|
chmod 444 "$CLAUDE_SETTINGS_SRC"
|
||||||
|
# Копируем в ~/.claude/ (перезаписывает при повторном запуске)
|
||||||
|
cp -f "$CLAUDE_SETTINGS_SRC" "$CLAUDE_SETTINGS_DST"
|
||||||
|
chmod 644 "$CLAUDE_SETTINGS_DST"
|
||||||
|
info "settings.json скопирован (оригинал защищён chmod 444)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# settings.local.json — локальные переопределения (не в git)
|
||||||
CLAUDE_LOCAL="$HOME/.claude/settings.local.json"
|
CLAUDE_LOCAL="$HOME/.claude/settings.local.json"
|
||||||
if [ ! -f "$CLAUDE_LOCAL" ]; then
|
if [ ! -f "$CLAUDE_LOCAL" ]; then
|
||||||
info "Создаю $CLAUDE_LOCAL (локальные настройки Claude)..."
|
info "Создаю $CLAUDE_LOCAL (локальные настройки Claude)..."
|
||||||
@@ -46,13 +92,6 @@ else
|
|||||||
warn "$CLAUDE_LOCAL уже существует, пропускаю"
|
warn "$CLAUDE_LOCAL уже существует, пропускаю"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Claude: защита settings.json от перезаписи ---
|
|
||||||
CLAUDE_SETTINGS="$DOTFILES_DIR/claude/.claude/settings.json"
|
|
||||||
if [ -f "$CLAUDE_SETTINGS" ]; then
|
|
||||||
chmod 444 "$CLAUDE_SETTINGS"
|
|
||||||
info "settings.json защищён от записи (chmod 444)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Bash: подключение в .bashrc ---
|
# --- Bash: подключение в .bashrc ---
|
||||||
BASHRC="$HOME/.bashrc"
|
BASHRC="$HOME/.bashrc"
|
||||||
BASHRC_LINE='[ -f ~/.bash_custom ] && source ~/.bash_custom'
|
BASHRC_LINE='[ -f ~/.bash_custom ] && source ~/.bash_custom'
|
||||||
|
|||||||
Reference in new issue
Block a user