A record whose name is cut short already reported; the raise needed a zero-length name and a directory offset pointing into the trailing comment. Build that case instead.
arti-pack
A Neovim plugin manager that installs plugins from an artifact repository
instead of git. It downloads tagged upstream source archives over HTTP and
unpacks them into Neovim's native package path — see :h packages.
It works with any HTTP artifact repository that serves an upstream tag archive at a stable URL — a JFrog Artifactory remote GitHub repository, or any caching proxy with the same path shape. Nothing is cloned, no git binary is needed, and the editor never has to reach GitHub: every plugin arrives as a cached, immutable, versioned artifact from the same store your builds already pull from.
Requirements
- Neovim 0.10+
curl
Archives are unpacked in pure Lua, so no unzip or tar binary is needed.
Entry paths are validated and every entry's CRC32 is checked before it lands on
disk.
Install
Each tag is published as a source zip under the same tag-archive path shape arti-pack expects, so it installs the same way as everything it manages — mirror it into your own artifact repository and fetch it from there:
curl -fsSL -o /tmp/arti-pack.zip \
https://artifacts.example.com/artifactory/nvim-plugins/unkin/arti-pack/archive/refs/tags/v0.1.0.zip
mkdir -p "${XDG_DATA_HOME:-$HOME/.local/share}/nvim/site/pack/arti-pack/start"
unzip -q /tmp/arti-pack.zip -d /tmp/arti-pack
mv /tmp/arti-pack/arti-pack-0.1.0 \
"${XDG_DATA_HOME:-$HOME/.local/share}/nvim/site/pack/arti-pack/start/arti-pack"
Once installed it can carry itself, since url bypasses the registry:
{
'unkin/arti-pack',
tag = 'v0.1.0',
url = 'https://artifacts.example.com/artifactory/nvim-plugins/unkin/arti-pack/archive/refs/tags/v0.1.0.zip',
}
Usage
require('arti-pack').setup({
registry = 'https://artifacts.example.com/artifactory/github-remote',
plugins = {
{ 'nvim-lua/plenary.nvim', tag = 'v0.1.4' },
{ 'folke/which-key.nvim', tag = 'v3.13.3', opt = true },
{ 'folke/tokyonight.nvim', tag = 'v4.10.0', as = 'tokyonight' },
{ 'tpope/vim-fugitive', tag = 'v3.7', ext = 'tar.gz' },
},
})
registry is required and has no default: it is the URL prefix your own
artifact repository serves upstream tag archives under, so there is no sensible
value to ship. It must be an http:// or https:// URL; surrounding
whitespace is trimmed and anything else is rejected. Until you set it,
:ArtiPackSync refuses to run and says so:
[arti-pack] registry is not configured; set it in require('arti-pack').setup{ registry = 'https://artifacts.example.com/artifactory/github-remote' }
:ArtiPackClean and :ArtiPackList never resolve a URL, so they work without
it, as do plugins that carry an explicit url.
Every plugin needs an explicit tag — that is the point. Plugins install to
stdpath('data')/site/pack/arti-pack/start/<name>, or .../opt/<name> when
opt = true.
Spec fields
| Field | Default | Meaning |
|---|---|---|
[1] |
required | owner/repo |
tag |
required | Upstream tag, used verbatim in the archive path |
opt |
false |
Install under opt/ for :packadd instead of start/ |
as |
repo name | Directory name to install as |
ext |
'zip' |
zip or tar.gz |
url |
— | Full archive URL, bypassing the registry (no whitespace) |
Options
| Option | Default | Meaning |
|---|---|---|
registry |
required | Archive URL prefix placed in front of owner/repo |
auto_sync |
true |
Sync missing and changed plugins on startup |
concurrency |
4 |
Plugins downloaded in parallel |
timeout |
120 |
Per-download timeout in seconds |
Commands
| Command | Effect |
|---|---|
:ArtiPackSync |
Install missing plugins and update ones whose tag changed |
:ArtiPackSync! |
Reinstall everything, ignoring recorded state |
:ArtiPackClean |
Remove installed plugins no longer in the spec |
:ArtiPackList |
Show every configured plugin and its state |
How it resolves an archive
<registry>/<owner>/<repo>/archive/refs/tags/<tag>.<ext>
which for the example above is
https://artifacts.example.com/artifactory/github-remote/nvim-lua/plenary.nvim/archive/refs/tags/v0.1.4.zip
That is GitHub's own tag-archive path shape, so a remote repository proxying
github.com resolves it unchanged.
Archives are cached under stdpath('cache')/arti-pack, checked for a real zip
or gzip magic header before being trusted, and unpacked into a staging
directory. Tag archives wrap their contents in a single <repo>-<version>
directory, which is stripped on the way in. Installed tags are recorded in
stdpath('data')/arti-pack/lock.json, so a sync with nothing to do costs
nothing. Helptags are regenerated per plugin after install.
A failed plugin is reported and skipped; the rest of the run continues.
Tests
make test