diff --git a/tests/neovim.bats b/tests/neovim.bats index 347cb3c..708e3fb 100644 --- a/tests/neovim.bats +++ b/tests/neovim.bats @@ -7,12 +7,13 @@ load test_helper setup() { common_setup - export XDG_DATA_HOME="$HOME/.local/share" + export XDG_DATA_HOME="$HOME/.local/share" XDG_STATE_HOME="$HOME/.local/state" LAZY="$XDG_DATA_HOME/nvim/lazy" + BACKUPS="$XDG_STATE_HOME/coredesktop/nvim-plugins" mkdir -p "$LAZY" export GIT_AUTHOR_NAME=test GIT_AUTHOR_EMAIL=test@example.com export GIT_COMMITTER_NAME=test GIT_COMMITTER_EMAIL=test@example.com - stub nvim 'echo "nvim ran" >> "$BATS_TEST_TMPDIR/nvim.log"' + stub nvim } # plugin NAME: a plugin checkout with one committed file @@ -28,28 +29,44 @@ update_neovim() { run_update } -@test "a plugin with local changes is stashed so lazy.nvim can update it" { +@test "a plugin whose files don't match its checkout is set aside to be installed again" { plugin LuaSnip echo changed > "$LAZY/LuaSnip/README.md" - echo build output > "$LAZY/LuaSnip/untracked.so" + mkdir "$LAZY/LuaSnip/data" + echo leftover > "$LAZY/LuaSnip/data/new.md" run update_neovim [ "$status" -eq 0 ] - [[ "$output" == *"LuaSnip had local changes"* ]] - [ -z "$(git -C "$LAZY/LuaSnip" ls-files -d -m)" ] - [ "$(cat "$LAZY/LuaSnip/README.md")" = original ] - # The changes can be got back, and untracked build output is left alone - git -C "$LAZY/LuaSnip" stash list | grep -q "Stashed by CoreDesktop's update" - [ -f "$LAZY/LuaSnip/untracked.so" ] - grep -q "nvim ran" "$BATS_TEST_TMPDIR/nvim.log" + [[ "$output" == *"LuaSnip's files didn't match its version"* ]] + [ ! -e "$LAZY/LuaSnip" ] + # The old copy, edits and all, is kept + copies=("$BACKUPS"/LuaSnip-*) + [ "${#copies[@]}" -eq 1 ] + [ "$(cat "${copies[0]}/README.md")" = changed ] + [ -f "${copies[0]}/data/new.md" ] + called nvim --headless "+Lazy! sync" +qa } -@test "plugins without local changes are left as they are" { +@test "a plugin an earlier update only stashed is set aside too" { + plugin LuaSnip + echo changed > "$LAZY/LuaSnip/README.md" + git -C "$LAZY/LuaSnip" stash push -q -m "Stashed by CoreDesktop's update" + + run update_neovim + [ "$status" -eq 0 ] + [ ! -e "$LAZY/LuaSnip" ] + [ -d "$(echo "$BACKUPS"/LuaSnip-*)" ] +} + +@test "plugins that match their checkout are left as they are" { plugin lazy.nvim echo build output > "$LAZY/lazy.nvim/untracked.so" + echo changed > "$LAZY/lazy.nvim/README.md" + git -C "$LAZY/lazy.nvim" stash push -q -m "the user's own stash" run update_neovim [ "$status" -eq 0 ] - [[ "$output" != *"local changes"* ]] - [ -z "$(git -C "$LAZY/lazy.nvim" stash list)" ] + [[ "$output" != *"didn't match"* ]] + [ -f "$LAZY/lazy.nvim/untracked.so" ] + [ ! -e "$BACKUPS" ] } diff --git a/updates.d/46-neovim.sh b/updates.d/46-neovim.sh index d59966d..f95b9ae 100755 --- a/updates.d/46-neovim.sh +++ b/updates.d/46-neovim.sh @@ -2,28 +2,36 @@ # Only the plugins are updated; the kickstart.nvim config itself is the user's to edit. LAZY_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/nvim/lazy" +PLUGIN_BACKUPS="${XDG_STATE_HOME:-$HOME/.local/state}/coredesktop/nvim-plugins" is_installed() { command -v nvim &>/dev/null && [ -d "$LAZY_DIR/lazy.nvim" ] } # lazy.nvim won't update a plugin whose tracked files differ from its checkout -# (it checks with `git ls-files -d -m`), which an interrupted update can leave -# behind. Stash those changes, so the plugin updates and they can still be -# recovered with `git stash pop` in its folder, and put its submodules back. -restore_plugins() { - local dir +# (it checks with `git ls-files -d -m`). An interrupted update can leave a +# plugin like that, along with leftovers git won't overwrite, such as new files +# and half-cloned submodules (LuaSnip, 2026-10-20). Move such a plugin out of +# the way, so `Lazy! sync` installs it afresh; any edits made to it are in the +# copy. So is a plugin an earlier version of this step only stashed. +set_aside_broken_plugins() { + local dir name dest for dir in "$LAZY_DIR"/*/; do + dir="${dir%/}" [ -d "$dir/.git" ] || continue - [ -n "$(git -C "$dir" ls-files -d -m 2> /dev/null)" ] || continue - echo " $(basename "$dir") had local changes; stashing them so it can update." - git -C "$dir" stash push -q -m "Stashed by CoreDesktop's update" \ - && git -C "$dir" submodule update -q --init --recursive --force + if [ -z "$(git -C "$dir" ls-files -d -m 2> /dev/null)" ] \ + && ! git -C "$dir" stash list 2> /dev/null | grep -q "Stashed by CoreDesktop's update"; then + continue + fi + name="$(basename "$dir")" + dest="$PLUGIN_BACKUPS/$name-$(date +%Y-%m-%d-%H%M%S)" + mkdir -p "$PLUGIN_BACKUPS" && mv "$dir" "$dest" \ + && echo " $name's files didn't match its version, so it will be installed again. The old copy is in $dest" done } run_update() { echo "Updating Neovim plugins..." - restore_plugins + set_aside_broken_plugins nvim --headless "+Lazy! sync" +qa }