From 3b860653ca0c698629d714f3af996822335debd1 Mon Sep 17 00:00:00 2001 From: Evgeni Chasnovski Date: Tue, 7 Oct 2025 22:24:23 +0300 Subject: [PATCH] fix(pack): handle lockfile in case of install errors #36064 Problem: If plugin was intended to install but there were errors (like if there is a typo in `src`), lockfile still includes its entry. This leads to all source of problems (like not correct `get()` output, not working `update()`, etc.). Solution: Explicitly account for plugins that were not installed. Alternative solution might be to write a safe `lock_set(plug, field, value)` wrapper (which sets field for a correct `plugins` entry in the lockfile Lua table) and use it after install to detect the change in `version`. However, this always requires an extra pass through plugins on every startup, which is suboptimal. Optimizing for the "happy path" should be a priority in `add()`. --- runtime/lua/vim/pack.lua | 5 +++++ test/functional/plugin/pack_spec.lua | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/runtime/lua/vim/pack.lua b/runtime/lua/vim/pack.lua index b7e396febe..532c85c5fe 100644 --- a/runtime/lua/vim/pack.lua +++ b/runtime/lua/vim/pack.lua @@ -789,6 +789,11 @@ function M.add(specs, opts) if #plugs_to_install > 0 then git_ensure_exec() install_list(plugs_to_install, opts.confirm) + for _, p in ipairs(plugs_to_install) do + if not p.info.installed then + plugin_lock.plugins[p.spec.name] = nil + end + end end if needs_lock_write then diff --git a/test/functional/plugin/pack_spec.lua b/test/functional/plugin/pack_spec.lua index c00c40791a..82f3f6c28e 100644 --- a/test/functional/plugin/pack_spec.lua +++ b/test/functional/plugin/pack_spec.lua @@ -541,6 +541,29 @@ describe('vim.pack', function() eq(ref_lockfile, get_lock_tbl()) end) + it('handles lockfile during install errors', function() + local repo_not_exist = 'file://' .. repo_get_path('does-not-exist') + pcall_err(exec_lua, function() + vim.pack.add({ + repo_not_exist, + { src = repos_src.basic, version = 'not-exist' }, + { src = repos_src.pluginerr, version = 'main' }, + }) + end) + + local pluginerr_hash = git_get_hash('main', 'pluginerr') + local ref_lockfile = { + -- Should be no entry for `repo_not_exist` + plugins = { + -- No `rev` because there was no relevant checkout + basic = { src = repos_src.basic, version = "'not-exist'" }, + -- Error during sourcing 'plugin/' should not affect lockfile + pluginerr = { rev = pluginerr_hash, src = repos_src.pluginerr, version = "'main'" }, + }, + } + eq(ref_lockfile, get_lock_tbl()) + end) + it('installs at proper version', function() local out = exec_lua(function() vim.pack.add({