diff options
| author | William Boman <william@redwill.se> | 2023-05-18 17:06:38 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-18 17:06:38 +0200 |
| commit | 227f8a9aaae495f481c768f8346edfceaf6d2951 (patch) | |
| tree | 8b2dad3c6175fac3c02489690304f759f21b3649 /lua/mason-core | |
| parent | fix(ui): use vim.cmd("") for nvim-0.7.0 compatibility (#1307) (diff) | |
| download | mason-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.lua | 114 | ||||
| -rw-r--r-- | lua/mason-core/path.lua | 7 |
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() |
