Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 69 additions & 4 deletions hooks/backend_install.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ function PLUGIN:BackendInstall(ctx)
-- The delegated shiv task may itself run nested `mise install` for package
-- dependencies. On macOS mise 2026.6.0, repeated/concurrent nested installs
-- can race or fail while rebuilding runtime symlinks. Serialize this shared
-- delegated phase while keeping each package's SHIV_* install paths isolated.
-- delegated phase, but allow descendant shiv installs from the same package
-- install to reenter the lock.
local install_lock_path = shiv_path .. ".install.lock"
with_lock(install_lock_path, "VFOX_SHIV_INSTALL_LOCK_MAX_ATTEMPTS", "shiv package install", function()
with_lock(install_lock_path, "VFOX_SHIV_INSTALL_LOCK_MAX_ATTEMPTS", "shiv package install", function(lock_owner)
-- Sync remote sources.json into bundled shiv so it knows about new packages.
-- Keep this inside the install lock because every package install writes the
-- same bundled sources.json file.
Expand All @@ -64,7 +65,7 @@ function PLUGIN:BackendInstall(ctx)
local mise_bin = find_mise()
local quoted_mise = Shell.quote(mise_bin)
local quoted_shiv_path = Shell.quote(shiv_path)
local install_cmd = env_prefix .. shiv_mise_env() .. quoted_mise .. " -C " .. quoted_shiv_path ..
local install_cmd = env_prefix .. shiv_mise_env() .. install_lock_env(lock_owner) .. quoted_mise .. " -C " .. quoted_shiv_path ..
" exec --no-deps -- " .. quoted_mise .. " -C " .. quoted_shiv_path ..
" run --skip-tools -q install " .. Shell.quote(tool_spec)

Expand Down Expand Up @@ -220,11 +221,22 @@ function numeric_parts(version)
end

--- Run a callback under a directory lock, reclaiming dead-holder locks.
---
--- The shiv install task runs package-local `mise install` for dependencies.
--- Those dependency installs can include more shiv:* tools, so this lock must be
--- reentrant for descendants of the process that already holds it. The holder
--- writes an owner token into the lock directory and passes that token to nested
--- mise commands via VFOX_SHIV_INSTALL_LOCK_OWNER.
--- @param lock_path string
--- @param max_attempts_env string
--- @param lock_name string
--- @param callback function
function with_lock(lock_path, max_attempts_env, lock_name, callback)
local inherited_owner = inherited_lock_owner(lock_path)
if inherited_owner then
return callback(inherited_owner)
end

local cmd = require("cmd")
local parent_dir = lock_path:match("(.+)/[^/]+$")
if parent_dir then
Expand Down Expand Up @@ -254,14 +266,67 @@ function with_lock(lock_path, max_attempts_env, lock_name, callback)
)
end

local ok, result = pcall(callback)
local owner = new_lock_owner()
local owner_ok, owner_err = pcall(cmd.exec,
"printf '%s' " .. Shell.quote(owner) .. " > " .. Shell.quote(lock_owner_path(lock_path)))
if not owner_ok then
Lock.release(lock_path)
error("Failed to write " .. lock_name .. " lock owner: " .. Errors.clean_error(tostring(owner_err)))
end

local ok, result = pcall(function()
return callback(owner)
end)
Lock.release(lock_path)
if not ok then
error(result)
end
return result
end

--- Return the env prefix propagated to nested shiv dependency installs.
--- @param lock_owner string
--- @return string
function install_lock_env(lock_owner)
return "VFOX_SHIV_INSTALL_LOCK_OWNER=" .. Shell.quote(lock_owner) .. " MISE_JOBS=1 "
end

--- Return the inherited lock owner when it matches the current lock directory.
--- @param lock_path string
--- @return string|nil
function inherited_lock_owner(lock_path)
local owner = trim(os.getenv("VFOX_SHIV_INSTALL_LOCK_OWNER") or "")
if owner == "" then
return nil
end

local cmd = require("cmd")
local ok, current = pcall(cmd.exec, "cat " .. Shell.quote(lock_owner_path(lock_path)) .. " 2>/dev/null")
if ok and trim(current) == owner then
return owner
end
return nil
end

--- Path of the owner token inside a lock directory.
--- @param lock_path string
--- @return string
function lock_owner_path(lock_path)
return lock_path .. "/owner"
end

--- Create a best-effort unique owner token for reentrant install locking.
--- @return string
function new_lock_owner()
local cmd = require("cmd")
local ok, token = pcall(cmd.exec,
"printf '%s:%s:%s' \"$PPID\" \"$$\" \"$(date +%s%N 2>/dev/null || date +%s)\"")
if ok and token and trim(token) ~= "" then
return trim(token)
end
return tostring(os.time()) .. ":" .. tostring(math.random(1000000000))
end

--- Sync the bundled shiv's sources.json with the remote version.
--- Uses the same cached remote sources as backend_list_versions.
--- Falls back silently if fetch fails (bundled sources still work).
Expand Down
153 changes: 153 additions & 0 deletions test/lua/backend_install_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,159 @@ test("dependency install reports both inherited and scrubbed failures", function
end)
end)

test("install lock env propagates owner and serializes nested mise", function()
load_backend_install({ exec = function() return "" end })

local env = install_lock_env("owner-token")
assert_contains(env, "VFOX_SHIV_INSTALL_LOCK_OWNER='owner-token'")
assert_contains(env, "MISE_JOBS=1 ")
end)

test("with_lock passes owner token to callback", function()
local commands = {}
local cmd_module = {
exec = function(command)
table.insert(commands, command)
if command:find("^printf '%%s:%%s:%%s'") then
return "owner-token\n"
end
return ""
end,
}
load_backend_install(cmd_module)

local callback_owner
local result
with_env({ VFOX_SHIV_INSTALL_LOCK_OWNER = false }, function()
result = with_lock("/tmp/vfox-shiv-install.lock", "VFOX_TEST_LOCK_MAX_ATTEMPTS", "test install", function(owner)
callback_owner = owner
return "done"
end)
end)

assert_equal(result, "done")
assert_equal(callback_owner, "owner-token")
local log = table.concat(commands, "\n")
assert_contains(log, "mkdir '/tmp/vfox-shiv-install.lock' 2>/dev/null")
assert_contains(log, "printf '%s' 'owner-token' > '/tmp/vfox-shiv-install.lock/owner'")
assert_contains(log, "rm -rf '/tmp/vfox-shiv-install.lock'")
end)

test("with_lock is reentrant for matching inherited owner", function()
local commands = {}
local cmd_module = {
exec = function(command)
table.insert(commands, command)
if command:find("^cat '/tmp/vfox%-shiv%-install%.lock/owner'") then
return "owner-token\n"
end
error("unexpected command: " .. command)
end,
}
load_backend_install(cmd_module)

local callback_owner
local result
with_env({ VFOX_SHIV_INSTALL_LOCK_OWNER = "owner-token" }, function()
result = with_lock("/tmp/vfox-shiv-install.lock", "VFOX_TEST_LOCK_MAX_ATTEMPTS", "test install", function(owner)
callback_owner = owner
return "nested-done"
end)
end)

assert_equal(result, "nested-done")
assert_equal(callback_owner, "owner-token")
assert_equal(#commands, 1)
assert_contains(commands[1], "cat '/tmp/vfox-shiv-install.lock/owner' 2>/dev/null")
end)

test("with_lock allows nested callbacks from same owner", function()
local commands = {}
local cmd_module = {
exec = function(command)
table.insert(commands, command)
if command:find("^printf '%%s:%%s:%%s'") then
return "owner-token\n"
end
if command:find("^cat '/tmp/vfox%-shiv%-install%.lock/owner'") then
return "owner-token\n"
end
return ""
end,
}
load_backend_install(cmd_module)

local nested_owner
with_env({ VFOX_SHIV_INSTALL_LOCK_OWNER = false }, function()
with_lock("/tmp/vfox-shiv-install.lock", "VFOX_TEST_LOCK_MAX_ATTEMPTS", "test install", function(owner)
with_env({ VFOX_SHIV_INSTALL_LOCK_OWNER = owner }, function()
with_lock("/tmp/vfox-shiv-install.lock", "VFOX_TEST_LOCK_MAX_ATTEMPTS", "test install", function(owner_from_nested)
nested_owner = owner_from_nested
end)
end)
end)
end)

assert_equal(nested_owner, "owner-token")
local log = table.concat(commands, "\n")
assert_contains(log, "cat '/tmp/vfox-shiv-install.lock/owner' 2>/dev/null")
assert_contains(log, "rm -rf '/tmp/vfox-shiv-install.lock'")
end)

test("BackendInstall lets delegated dependency installs reenter lock", function()
local nested_owner
local saw_delegated_install = false
local commands = {}
local cmd_module = {
exec = function(command)
table.insert(commands, command)
if command == "command -v mise" then
return "/bin/mise\n"
end
if command:find("^git %-C '/tmp/shiv' describe") then
return "v0.3.2\n"
end
if command:find("^printf '%%s:%%s:%%s'") then
return "owner-token\n"
end
if command:find("^cat '/tmp/shiv%.install%.lock/owner'") then
return "owner-token\n"
end
if command:find("run %-%-skip%-tools %-q install 'emails@v0%.6%.2'") then
saw_delegated_install = true
assert_contains(command, "VFOX_SHIV_INSTALL_LOCK_OWNER='owner-token'")
assert_contains(command, "MISE_JOBS=1")
with_env({ VFOX_SHIV_INSTALL_LOCK_OWNER = "owner-token" }, function()
with_lock("/tmp/shiv.install.lock", "VFOX_TEST_LOCK_MAX_ATTEMPTS", "test install", function(owner)
nested_owner = owner
end)
end)
return ""
end
return ""
end,
}
local previous_file = package.loaded["file"]
package.loaded["file"] = {
exists = function(path)
return path == "/tmp/shiv/.git/HEAD" or path == "/tmp/shiv/.vfox-shiv-deps-ready"
end,
}
load_backend_install(cmd_module)

with_env({ VFOX_SHIV_PATH = "/tmp/shiv", VFOX_SHIV_INSTALL_LOCK_OWNER = false }, function()
PLUGIN:BackendInstall({ tool = "emails", version = "0.6.2", install_path = "/tmp/install" })
end)
package.loaded["file"] = previous_file

assert_truthy(saw_delegated_install)
assert_equal(nested_owner, "owner-token")
local log = table.concat(commands, "\n")
assert_contains(log, "printf '%s' 'owner-token' > '/tmp/shiv.install.lock/owner'")
assert_contains(log, "cat '/tmp/shiv.install.lock/owner' 2>/dev/null")
assert_contains(log, "rm -rf '/tmp/shiv.install.lock'")
end)

test("numeric minor install ref resolves to newest matching patch", function()
load_backend_install({ exec = function() return "" end })

Expand Down
Loading