35b9df9eba
## Why
Cloning plugins from git ties the editor to GitHub uptime and moving branches; archives at a pinned version from our own registry are reproducible and reachable offline.
## How
- Add a registry resolver interface `(spec, registry) -> {url, headers, filename, sha256?}` with builtin `template` and `artifactapi` kinds, a per-plugin registry override and a `url` escape hatch.
- Take auth tokens only from an env var or a `token_cmd`; reject a literal token at setup.
- Download with curl, verify an optional sha256, extract to a staging dir and rename into `pack/vimpack/{start,opt}/<name>`, restoring the previous tree if the swap fails.
- Track installed name/version/sha256 in `stdpath('data')/vimpack/manifest.json` and skip plugins already at the wanted version.
- Add `:VimpackInstall`, `:VimpackUpdate`, `:VimpackClean` and `:VimpackStatus`, running downloads through `vim.system` under a concurrency cap.
- Hash with a pure-Lua SHA-256; `vim.fn.sha256()` rejects binary archives with E976.
- Add a dependency-free `tests/minimal.lua` harness and 57 specs covering resolution, auth, manifest, planning, checksum mismatch, orphan detection and end-to-end install.
- Add `.woodpecker/{test,lint,pre-commit}.yaml`, luacheck/stylua config and pre-commit hooks.
82 lines
3.3 KiB
Lua
82 lines
3.3 KiB
Lua
-- SHA-256 over LuaJIT bit ops. vim.fn.sha256() rejects binary input (E976:
|
|
-- Using a Blob as a String), and archives are binary, so vimpack hashes here.
|
|
local bit = require('bit')
|
|
|
|
local band, bxor, bnot = bit.band, bit.bxor, bit.bnot
|
|
local rshift, rol, tobit, tohex = bit.rshift, bit.rol, bit.tobit, bit.tohex
|
|
local byte, rep, sub = string.byte, string.rep, string.sub
|
|
|
|
-- stylua: ignore
|
|
local K = {
|
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
}
|
|
for i = 1, 64 do
|
|
K[i] = tobit(K[i])
|
|
end
|
|
|
|
local function pad(msg)
|
|
local len = #msg
|
|
local bits = len * 8
|
|
local pad_len = (55 - len) % 64
|
|
local tail = {}
|
|
-- 64-bit big-endian bit length; the top word stays 0 below 512 MiB.
|
|
local hi = math.floor(bits / 4294967296)
|
|
local lo = bits % 4294967296
|
|
for shift = 24, 0, -8 do
|
|
tail[#tail + 1] = string.char(band(rshift(tobit(hi), shift), 0xff))
|
|
end
|
|
for shift = 24, 0, -8 do
|
|
tail[#tail + 1] = string.char(band(rshift(tobit(lo), shift), 0xff))
|
|
end
|
|
return msg .. '\128' .. rep('\0', pad_len) .. table.concat(tail)
|
|
end
|
|
|
|
--- Hex digest of an arbitrary (binary-safe) string.
|
|
local function sha256(msg)
|
|
local h0, h1, h2, h3 = tobit(0x6a09e667), tobit(0xbb67ae85), tobit(0x3c6ef372), tobit(0xa54ff53a)
|
|
local h4, h5, h6, h7 = tobit(0x510e527f), tobit(0x9b05688c), tobit(0x1f83d9ab), tobit(0x5be0cd19)
|
|
|
|
local data = pad(msg)
|
|
local w = {}
|
|
|
|
for offset = 1, #data, 64 do
|
|
local chunk = sub(data, offset, offset + 63)
|
|
for i = 1, 16 do
|
|
local b1, b2, b3, b4 = byte(chunk, i * 4 - 3, i * 4)
|
|
w[i] = tobit(b1 * 16777216 + b2 * 65536 + b3 * 256 + b4)
|
|
end
|
|
for i = 17, 64 do
|
|
local x, y = w[i - 15], w[i - 2]
|
|
local s0 = bxor(rol(x, 25), rol(x, 14), rshift(x, 3))
|
|
local s1 = bxor(rol(y, 15), rol(y, 13), rshift(y, 10))
|
|
w[i] = tobit(w[i - 16] + s0 + w[i - 7] + s1)
|
|
end
|
|
|
|
local a, b, c, d, e, f, g, h = h0, h1, h2, h3, h4, h5, h6, h7
|
|
for i = 1, 64 do
|
|
local S1 = bxor(rol(e, 26), rol(e, 21), rol(e, 7))
|
|
local ch = bxor(band(e, f), band(bnot(e), g))
|
|
local t1 = tobit(h + S1 + ch + K[i] + w[i])
|
|
local S0 = bxor(rol(a, 30), rol(a, 19), rol(a, 10))
|
|
local maj = bxor(band(a, b), band(a, c), band(b, c))
|
|
local t2 = tobit(S0 + maj)
|
|
h, g, f, e = g, f, e, tobit(d + t1)
|
|
d, c, b, a = c, b, a, tobit(t1 + t2)
|
|
end
|
|
|
|
h0, h1, h2, h3 = tobit(h0 + a), tobit(h1 + b), tobit(h2 + c), tobit(h3 + d)
|
|
h4, h5, h6, h7 = tobit(h4 + e), tobit(h5 + f), tobit(h6 + g), tobit(h7 + h)
|
|
end
|
|
|
|
return tohex(h0) .. tohex(h1) .. tohex(h2) .. tohex(h3) .. tohex(h4) .. tohex(h5) .. tohex(h6) .. tohex(h7)
|
|
end
|
|
|
|
return { hash = sha256 }
|