aboutsummaryrefslogtreecommitdiffstats
path: root/lua/mason-core
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2023-05-18 17:06:38 +0200
committerGitHub <noreply@github.com>2023-05-18 17:06:38 +0200
commit227f8a9aaae495f481c768f8346edfceaf6d2951 (patch)
tree8b2dad3c6175fac3c02489690304f759f21b3649 /lua/mason-core
parentfix(ui): use vim.cmd("") for nvim-0.7.0 compatibility (#1307) (diff)
downloadmason-227f8a9aaae495f481c768f8346edfceaf6d2951.tar
mason-227f8a9aaae495f481c768f8346edfceaf6d2951.tar.gz
mason-227f8a9aaae495f481c768f8346edfceaf6d2951.tar.bz2
mason-227f8a9aaae495f481c768f8346edfceaf6d2951.tar.lz
mason-227f8a9aaae495f481c768f8346edfceaf6d2951.tar.xz
mason-227f8a9aaae495f481c768f8346edfceaf6d2951.tar.zst
mason-227f8a9aaae495f481c768f8346edfceaf6d2951.zip
feat(installer): lock package installation (#1290)
Diffstat (limited to 'lua/mason-core')
-rw-r--r--lua/mason-core/installer/init.lua114
-rw-r--r--lua/mason-core/path.lua7
2 files changed, 78 insertions, 43 deletions
diff --git a/lua/mason-core/installer/init.lua b/lua/mason-core/installer/init.lua
index 059f30bd..994ab847 100644
--- a/lua/mason-core/installer/init.lua
+++ b/lua/mason-core/installer/init.lua
@@ -16,7 +16,7 @@ local sem = Semaphore.new(settings.current.max_concurrent_installers)
local M = {}
---@async
-local function create_prefix_dirs()
+function M.create_prefix_dirs()
return Result.try(function(try)
for _, p in ipairs {
path.install_prefix(),
@@ -53,10 +53,28 @@ function M.context()
end
---@async
+---@param ctx InstallContext
+local function lock_package(ctx)
+ log.debug("Attempting to lock package", ctx.package)
+ local lockfile = path.package_lock(ctx.package.name)
+ if not ctx.opts.force and fs.async.file_exists(lockfile) then
+ log.error("Lockfile already exists.", ctx.package)
+ return Result.failure(
+ ("Lockfile exists, installation is already running in another process (pid: %s). Run with :MasonInstall --force to bypass."):format(
+ fs.sync.read_file(lockfile)
+ )
+ )
+ end
+ a.scheduler()
+ fs.async.write_file(lockfile, vim.fn.getpid())
+ log.debug("Wrote lockfile", ctx.package)
+ return Result.success(lockfile)
+end
+
+---@async
---@param context InstallContext
function M.prepare_installer(context)
return Result.try(function(try)
- try(create_prefix_dirs())
local package_build_prefix = path.package_build_prefix(context.package.name)
if fs.async.dir_exists(package_build_prefix) then
try(Result.pcall(fs.async.rmrf, package_build_prefix))
@@ -165,35 +183,66 @@ function M.execute(handle, opts)
log.fmt_info("Executing installer for %s %s", pkg, opts)
- return Result.try(function(try)
- -- 1. prepare directories and initialize cwd
- local installer = try(M.prepare_installer(context))
+ return M.create_prefix_dirs()
+ :and_then(function()
+ return lock_package(context)
+ end)
+ :and_then(function(lockfile)
+ local release_lock = _.partial(pcall, fs.async.unlink, lockfile)
+ return Result.try(function(try)
+ -- 1. prepare directories and initialize cwd
+ local installer = try(M.prepare_installer(context))
- -- 2. execute installer
- try(run_installer(context, installer))
+ -- 2. execute installer
+ try(run_installer(context, installer))
- -- 3. promote temporary installation dir
- try(Result.pcall(function()
- context:promote_cwd()
- end))
+ -- 3. promote temporary installation dir
+ try(Result.pcall(function()
+ context:promote_cwd()
+ end))
- -- 4. link package
- try(linker.link(context))
+ -- 4. link package
+ try(linker.link(context))
- -- 5. build & write receipt
- ---@type InstallReceipt
- local receipt = try(build_receipt(context))
- try(Result.pcall(function()
- receipt:write(context.cwd:get())
- end))
- end)
+ -- 5. build & write receipt
+ ---@type InstallReceipt
+ local receipt = try(build_receipt(context))
+ try(Result.pcall(function()
+ receipt:write(context.cwd:get())
+ end))
+ end)
+ :on_success(function()
+ release_lock()
+ if opts.debug then
+ context.fs:write_file("mason-debug.log", table.concat(tailed_output, ""))
+ end
+ end)
+ :on_failure(function()
+ release_lock()
+ if not opts.debug then
+ -- clean up installation dir
+ pcall(function()
+ fs.async.rmrf(context.cwd:get())
+ end)
+ else
+ context.fs:write_file("mason-debug.log", table.concat(tailed_output, ""))
+ context.stdio_sink.stdout(
+ ("[debug] Installation directory retained at %q.\n"):format(context.cwd:get())
+ )
+ end
+
+ -- unlink linked executables (in the occasion an error occurs after linking)
+ build_receipt(context):on_success(function(receipt)
+ linker.unlink(context.package, receipt):on_failure(function(err)
+ log.error("Failed to unlink failed installation", err)
+ end)
+ end)
+ end)
+ end)
:on_success(function()
permit:forget()
handle:close()
log.fmt_info("Installation succeeded for %s", pkg)
- if opts.debug then
- context.fs:write_file("mason-debug.log", table.concat(tailed_output, ""))
- end
end)
:on_failure(function(failure)
permit:forget()
@@ -201,25 +250,6 @@ function M.execute(handle, opts)
context.stdio_sink.stderr(tostring(failure))
context.stdio_sink.stderr "\n"
- if not opts.debug then
- -- clean up installation dir
- pcall(function()
- fs.async.rmrf(context.cwd:get())
- end)
- else
- context.fs:write_file("mason-debug.log", table.concat(tailed_output, ""))
- context.stdio_sink.stdout(
- ("[debug] Installation directory retained at %q.\n"):format(context.cwd:get())
- )
- end
-
- -- unlink linked executables (in the occasion an error occurs after linking)
- build_receipt(context):on_success(function(receipt)
- linker.unlink(context.package, receipt):on_failure(function(err)
- log.error("Failed to unlink failed installation", err)
- end)
- end)
-
if not handle:is_closed() and not handle.is_terminated then
handle:close()
end
diff --git a/lua/mason-core/path.lua b/lua/mason-core/path.lua
index 1e0038be..3d7c8668 100644
--- a/lua/mason-core/path.lua
+++ b/lua/mason-core/path.lua
@@ -55,7 +55,12 @@ end
---@param name string?
function M.package_build_prefix(name)
- return M.concat { M.install_prefix ".packages", name }
+ return M.concat { M.install_prefix "staging", name }
+end
+
+---@param name string
+function M.package_lock(name)
+ return M.package_build_prefix(("%s.lock"):format(name))
end
function M.registry_prefix()