ca55c9dd9f
- Reject archive members that are absolute, escape via .., are special files, or link outside the destination, before anything is written - Stage under the pack root so the swap is a same-filesystem rename, with an EXDEV copy fallback; propagate registry auth errors and validate dir_name
182 lines
6.4 KiB
Lua
182 lines
6.4 KiB
Lua
-- End-to-end install over file:// URLs: real curl, real tar, real swap.
|
|
local checksum = require('vimpack.checksum')
|
|
local config = require('vimpack.config')
|
|
local fsmod = require('vimpack.fs')
|
|
local installer = require('vimpack.installer')
|
|
local manifest = require('vimpack.manifest')
|
|
local plan = require('vimpack.plan')
|
|
|
|
local function make_tarball(root, version, body)
|
|
local src = vim.fs.joinpath(root, 'src', 'demo.nvim-' .. version, 'lua')
|
|
vim.fn.mkdir(src, 'p')
|
|
vim.fn.writefile({ body }, vim.fs.joinpath(src, 'demo.lua'))
|
|
local archive = vim.fs.joinpath(root, 'demo-' .. version .. '.tar.gz')
|
|
vim.fn.system({ 'tar', '-czf', archive, '-C', vim.fs.joinpath(root, 'src'), 'demo.nvim-' .. version })
|
|
return archive
|
|
end
|
|
|
|
local function install(cfg, data, pack_root)
|
|
local built = plan.build(cfg, data, fsmod.scan(pack_root))
|
|
local items = vim.list_extend(vim.deepcopy(built.install), built.update)
|
|
local results
|
|
installer.run(items, {
|
|
manifest = data,
|
|
pack_root = pack_root,
|
|
timeout = 30000,
|
|
concurrency = 2,
|
|
}, function(r)
|
|
results = r
|
|
end)
|
|
vim.wait(30000, function()
|
|
return results ~= nil
|
|
end)
|
|
return results, items
|
|
end
|
|
|
|
describe('install end to end', function()
|
|
it('downloads, extracts, swaps and records the manifest', function()
|
|
local root = tmpdir()
|
|
local pack_root = vim.fs.joinpath(root, 'pack')
|
|
local archive = make_tarball(root, '1.0.0', 'return 1')
|
|
local cfg = assert(config.normalize({
|
|
plugins = { { name = 'demo.nvim', version = '1.0.0', url = 'file://' .. archive } },
|
|
}))
|
|
local data = manifest.empty()
|
|
|
|
local results = install(cfg, data, pack_root)
|
|
assert_eq(1, #results)
|
|
assert_true(results[1].ok, tostring(results[1].err))
|
|
|
|
local installed = vim.fs.joinpath(pack_root, 'start', 'demo.nvim', 'lua', 'demo.lua')
|
|
assert_true(fsmod.exists(installed), 'expected ' .. installed)
|
|
assert_eq({ 'return 1' }, vim.fn.readfile(installed))
|
|
|
|
local entry = manifest.get(data, 'demo.nvim')
|
|
assert_eq('1.0.0', entry.version)
|
|
assert_eq(assert(checksum.sha256_file(archive)), entry.sha256)
|
|
|
|
-- Second pass is a no-op: nothing left to install.
|
|
local _, items = install(cfg, data, pack_root)
|
|
assert_eq(0, #items)
|
|
end)
|
|
|
|
it('replaces the old tree on a version bump', function()
|
|
local root = tmpdir()
|
|
local pack_root = vim.fs.joinpath(root, 'pack')
|
|
local data = manifest.empty()
|
|
|
|
local v1 = make_tarball(root, '1.0.0', 'return 1')
|
|
local cfg1 = assert(config.normalize({
|
|
plugins = { { name = 'demo.nvim', version = '1.0.0', url = 'file://' .. v1 } },
|
|
}))
|
|
assert_true(install(cfg1, data, pack_root)[1].ok)
|
|
|
|
local v2 = make_tarball(root, '2.0.0', 'return 2')
|
|
local cfg2 = assert(config.normalize({
|
|
plugins = { { name = 'demo.nvim', version = '2.0.0', url = 'file://' .. v2 } },
|
|
}))
|
|
local results = install(cfg2, data, pack_root)
|
|
assert_true(results[1].ok, tostring(results[1].err))
|
|
assert_eq({ 'return 2' }, vim.fn.readfile(vim.fs.joinpath(pack_root, 'start', 'demo.nvim', 'lua', 'demo.lua')))
|
|
assert_eq('2.0.0', manifest.get(data, 'demo.nvim').version)
|
|
end)
|
|
|
|
it('aborts on a checksum mismatch and keeps the previous install', function()
|
|
local root = tmpdir()
|
|
local pack_root = vim.fs.joinpath(root, 'pack')
|
|
local data = manifest.empty()
|
|
|
|
local v1 = make_tarball(root, '1.0.0', 'return 1')
|
|
assert_true(install(
|
|
assert(config.normalize({
|
|
plugins = { { name = 'demo.nvim', version = '1.0.0', url = 'file://' .. v1 } },
|
|
})),
|
|
data,
|
|
pack_root
|
|
)[1].ok)
|
|
|
|
local v2 = make_tarball(root, '2.0.0', 'return 2')
|
|
local results = install(
|
|
assert(config.normalize({
|
|
plugins = {
|
|
{ name = 'demo.nvim', version = '2.0.0', url = 'file://' .. v2, sha256 = string.rep('a', 64) },
|
|
},
|
|
})),
|
|
data,
|
|
pack_root
|
|
)
|
|
assert_true(not results[1].ok)
|
|
assert_match('checksum mismatch', results[1].err)
|
|
assert_eq({ 'return 1' }, vim.fn.readfile(vim.fs.joinpath(pack_root, 'start', 'demo.nvim', 'lua', 'demo.lua')))
|
|
assert_eq('1.0.0', manifest.get(data, 'demo.nvim').version)
|
|
end)
|
|
|
|
it('names the url when the download fails', function()
|
|
local root = tmpdir()
|
|
local missing = 'file://' .. vim.fs.joinpath(root, 'nope.tar.gz')
|
|
local results = install(
|
|
assert(config.normalize({
|
|
plugins = { { name = 'demo.nvim', version = '1.0.0', url = missing } },
|
|
})),
|
|
manifest.empty(),
|
|
vim.fs.joinpath(root, 'pack')
|
|
)
|
|
assert_true(not results[1].ok)
|
|
assert_match('download failed', results[1].err)
|
|
assert_match(missing, results[1].err)
|
|
end)
|
|
|
|
it('installs with the cache on a different filesystem from the pack root', function()
|
|
local root = tmpdir()
|
|
local pack_root = vim.fs.joinpath(root, 'pack')
|
|
local other = otherfs_dir(root)
|
|
if not other then
|
|
return
|
|
end
|
|
|
|
local real_cache_dir = fsmod.cache_dir
|
|
fsmod.cache_dir = function()
|
|
return vim.fs.joinpath(other, 'vimpack')
|
|
end
|
|
|
|
local archive = make_tarball(root, '1.0.0', 'return 1')
|
|
local cfg = assert(config.normalize({
|
|
plugins = { { name = 'demo.nvim', version = '1.0.0', url = 'file://' .. archive } },
|
|
}))
|
|
local data = manifest.empty()
|
|
local ok, results = pcall(install, cfg, data, pack_root)
|
|
fsmod.cache_dir = real_cache_dir
|
|
fsmod.rm_rf(other)
|
|
|
|
assert_true(ok, tostring(results))
|
|
assert_true(results[1].ok, tostring(results[1].err))
|
|
assert_eq({ 'return 1' }, vim.fn.readfile(vim.fs.joinpath(pack_root, 'start', 'demo.nvim', 'lua', 'demo.lua')))
|
|
end)
|
|
|
|
it('moves a plugin from start to opt', function()
|
|
local root = tmpdir()
|
|
local pack_root = vim.fs.joinpath(root, 'pack')
|
|
local data = manifest.empty()
|
|
local archive = make_tarball(root, '1.0.0', 'return 1')
|
|
|
|
assert_true(install(
|
|
assert(config.normalize({
|
|
plugins = { { name = 'demo.nvim', version = '1.0.0', url = 'file://' .. archive } },
|
|
})),
|
|
data,
|
|
pack_root
|
|
)[1].ok)
|
|
|
|
local results = install(
|
|
assert(config.normalize({
|
|
plugins = { { name = 'demo.nvim', version = '1.0.0', url = 'file://' .. archive, opt = true } },
|
|
})),
|
|
data,
|
|
pack_root
|
|
)
|
|
assert_true(results[1].ok, tostring(results[1].err))
|
|
assert_true(fsmod.exists(vim.fs.joinpath(pack_root, 'opt', 'demo.nvim', 'lua', 'demo.lua')))
|
|
assert_true(not fsmod.exists(vim.fs.joinpath(pack_root, 'start', 'demo.nvim')))
|
|
end)
|
|
end)
|