Files
unkin-agent 460dc2c5c8
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/lint Pipeline was successful
Install plugins from a branch
Many upstream plugins publish no tags at all, so a tag-only installer cannot
manage them.

- Add a branch spec field, mutually exclusive with tag and validated the same way
- Build archive/refs/heads/<branch> URLs for branch specs
- Refetch a branch only on :ArtiPackUpdate or a forced sync, never from cache
- Record the branch, archive digest and fetch time in the lockfile
- Report a branch plugin as tracked and fetched, never as up to date
2026-09-12 00:08:18 +10:00

126 lines
3.7 KiB
Lua

local t = require('tests.harness')
local fs = require('arti-pack.fs')
local function tmpdir()
local dir = vim.fn.tempname()
vim.fn.mkdir(dir, 'p')
return dir
end
local function write(path, contents)
local fd = assert(io.open(path, 'wb'))
fd:write(contents)
fd:close()
return path
end
t.describe('fs.archive_root', function()
t.it('strips the single wrapper directory a tag archive unpacks to', function()
local dir = tmpdir()
vim.fn.mkdir(dir .. '/plenary.nvim-0.1.4/lua', 'p')
t.eq(dir .. '/plenary.nvim-0.1.4', fs.archive_root(dir))
end)
t.it('keeps the directory when the archive unpacked flat', function()
local dir = tmpdir()
vim.fn.mkdir(dir .. '/lua', 'p')
write(dir .. '/README.md', 'hi')
t.eq(dir, fs.archive_root(dir))
end)
t.it('keeps the directory when its only entry is a file', function()
local dir = tmpdir()
write(dir .. '/init.lua', 'return {}')
t.eq(dir, fs.archive_root(dir))
end)
end)
t.describe('fs.verify_archive', function()
t.it('accepts a zip magic header', function()
local path = write(tmpdir() .. '/a.zip', 'PK\3\4rest of the file')
t.truthy(fs.verify_archive(path, 'zip'))
end)
t.it('accepts a gzip magic header', function()
local path = write(tmpdir() .. '/a.tar.gz', '\31\139\8\0padding')
t.truthy(fs.verify_archive(path, 'tar.gz'))
end)
t.it('rejects an empty download', function()
local path = write(tmpdir() .. '/a.zip', '')
local ok, err = fs.verify_archive(path, 'zip')
t.falsy(ok)
t.matches('empty', err)
end)
t.it('rejects an html error page served in place of a zip', function()
local path = write(tmpdir() .. '/a.zip', '<!DOCTYPE html><html>404</html>')
local ok, err = fs.verify_archive(path, 'zip')
t.falsy(ok)
t.matches('not a zip', err)
end)
t.it('rejects a zip when a tar.gz was expected', function()
local path = write(tmpdir() .. '/a.tar.gz', 'PK\3\4rest')
local ok, err = fs.verify_archive(path, 'tar.gz')
t.falsy(ok)
t.matches('not a tar%.gz', err)
end)
t.it('rejects a missing file', function()
local ok, err = fs.verify_archive(tmpdir() .. '/absent.zip', 'zip')
t.falsy(ok)
t.matches('empty', err)
end)
end)
t.describe('fs.digest', function()
t.it('identifies file contents, not the file name', function()
local dir = tmpdir()
local a = write(dir .. '/a.zip', 'PK\3\4payload')
local b = write(dir .. '/b.zip', 'PK\3\4payload')
t.eq(fs.digest(a), fs.digest(b))
t.eq(64, #fs.digest(a), 'a sha256 hex digest')
end)
t.it('changes when the bytes change', function()
local dir = tmpdir()
local before = fs.digest(write(dir .. '/a.zip', 'PK\3\4one'))
local after = fs.digest(write(dir .. '/a.zip', 'PK\3\4two'))
t.truthy(before ~= after, 'a refetched archive with new contents digests differently')
end)
t.it('returns nothing for a missing file', function()
t.eq(nil, fs.digest(tmpdir() .. '/absent.zip'))
end)
end)
t.describe('fs.extract', function()
t.it('reports a missing archive instead of raising', function()
local ok, err = fs.extract(tmpdir() .. '/absent.zip', tmpdir(), 'zip')
t.falsy(ok)
t.truthy(err)
end)
t.it('reports an empty archive', function()
local path = write(tmpdir() .. '/a.zip', '')
local ok, err = fs.extract(path, tmpdir() .. '/dest', 'zip')
t.falsy(ok)
t.matches('empty', err)
end)
end)
t.describe('fs.rm_rf', function()
t.it('removes a populated directory tree', function()
local dir = tmpdir()
vim.fn.mkdir(dir .. '/a/b', 'p')
write(dir .. '/a/b/c.txt', 'x')
fs.rm_rf(dir .. '/a')
t.falsy(fs.exists(dir .. '/a'))
end)
t.it('is a no-op on a path that is not there', function()
fs.rm_rf(tmpdir() .. '/never-existed')
end)
end)