unkin-agent e61e950d30
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Trim whitespace out of the registry and spec urls
A registry with a stray space or newline passed validation and was
concatenated verbatim into the archive URL, so curl saw the malformed
address instead of the user seeing the required-registry error. Trim the
value before matching and reject any whitespace left inside it; apply the
same to a spec's explicit url.
2026-09-09 22:56:18 +10:00
2026-09-08 23:22:15 +10:00

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, and unzip (or tar when using ext = 'tar.gz')

Install

arti-pack manages itself least awkwardly as a plain clone into the package path:

git clone https://git.unkin.net/unkin/arti-pack \
  "${XDG_DATA_HOME:-$HOME/.local/share}/nvim/site/pack/arti-pack/start/arti-pack"

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
S
Description
Neovim plugin manager that installs plugins from tagged archives in an HTTP artifact repository into nvim's native package path
Readme 256 KiB
Languages
Lua 97.7%
Makefile 2.3%