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.
173 lines
7.4 KiB
Plaintext
173 lines
7.4 KiB
Plaintext
*vimpack.txt* Install Neovim plugins from archives on an artifact registry
|
|
|
|
==============================================================================
|
|
CONTENTS *vimpack-contents*
|
|
|
|
1. Introduction ............................. |vimpack-introduction|
|
|
2. Setup .................................... |vimpack-setup|
|
|
3. Plugin spec .............................. |vimpack-spec|
|
|
4. Registries ............................... |vimpack-registries|
|
|
5. Auth ..................................... |vimpack-auth|
|
|
6. Commands ................................. |vimpack-commands|
|
|
7. Layout ................................... |vimpack-layout|
|
|
8. Lua API .................................. |vimpack-api|
|
|
|
|
==============================================================================
|
|
1. INTRODUCTION *vimpack-introduction*
|
|
|
|
vimpack installs Neovim plugins from tarballs or zips served by an artifact
|
|
registry, at an explicit version, rather than by cloning git repositories.
|
|
Downloads are checksum-verified and swapped into place atomically, so a failed
|
|
install never leaves a half-written plugin behind.
|
|
|
|
Requirements: Neovim 0.10+, `curl`, and `tar` (plus `unzip` for .zip
|
|
archives).
|
|
|
|
==============================================================================
|
|
2. SETUP *vimpack-setup*
|
|
|
|
>lua
|
|
require('vimpack').setup({
|
|
registry = {
|
|
kind = 'artifactapi',
|
|
base_url = 'https://artifactapi.k8s.syd1.au.unkin.net',
|
|
repo = 'nvim-plugins',
|
|
auth = { env = 'ARTIFACTAPI_TOKEN' },
|
|
},
|
|
plugins = {
|
|
{ name = 'telescope.nvim', version = '0.1.8' },
|
|
{ name = 'vim-fugitive', version = '3.7', opt = true },
|
|
},
|
|
concurrency = 4,
|
|
timeout = 120000,
|
|
})
|
|
<
|
|
`setup()` validates the spec and returns the normalised config, or nil plus an
|
|
error message. Nothing is downloaded until |:VimpackInstall|.
|
|
|
|
Top-level options ~
|
|
|
|
registry default registry for every plugin (table, optional)
|
|
plugins list of plugin specs (list, default {})
|
|
concurrency maximum simultaneous downloads (number, default 4)
|
|
timeout per-download timeout in milliseconds (number, default 120000)
|
|
|
|
==============================================================================
|
|
3. PLUGIN SPEC *vimpack-spec*
|
|
|
|
name Plugin name. Required. Also the directory name under
|
|
pack/vimpack/{start,opt}/ unless dir_name overrides it.
|
|
version Required unless `url` is given. Passed to the resolver.
|
|
sha256 Expected digest of the archive. A mismatch aborts the install
|
|
and reports both the expected and the actual digest.
|
|
opt When true the plugin lands in opt/ for |:packadd|.
|
|
url Fully explicit archive URL; registry resolution is skipped.
|
|
ext Archive extension, default "tar.gz".
|
|
registry Per-plugin registry, overriding the top-level one.
|
|
dir_name On-disk directory name.
|
|
|
|
==============================================================================
|
|
4. REGISTRIES *vimpack-registries*
|
|
|
|
A registry maps (name, version) to a download URL. `kind` selects the
|
|
resolver; the default kind is "template".
|
|
|
|
template ~
|
|
|
|
Substitutes ${name}, ${version}, ${ext}, ${slug}, ${repo} and ${base_url} into
|
|
`url_template`. An unrecognised placeholder is reported as an error.
|
|
`filename_template` optionally names the cached archive.
|
|
>lua
|
|
registry = {
|
|
kind = 'template',
|
|
url_template = 'https://artifactory.example.com/artifactory/nvim-generic/'
|
|
.. '${name}/${version}/${name}-${version}.${ext}',
|
|
}
|
|
<
|
|
artifactapi ~
|
|
|
|
Builds ${base_url}/api/v2/remotes/${repo}/files/${path}. `path_template`
|
|
defaults to "${name}/${name}-${version}.${ext}".
|
|
>lua
|
|
registry = {
|
|
kind = 'artifactapi',
|
|
base_url = 'https://artifactapi.k8s.syd1.au.unkin.net',
|
|
repo = 'nvim-plugins',
|
|
}
|
|
<
|
|
custom ~
|
|
|
|
A resolver is `(spec, registry) -> resolved | nil, err`, where `resolved` is
|
|
`{ url, headers, filename, sha256? }`.
|
|
>lua
|
|
require('vimpack').register_resolver('mine', function(spec, registry)
|
|
return { url = registry.base_url .. '/' .. spec.name .. '-'
|
|
.. spec.version .. '.tar.gz', headers = {} }
|
|
end)
|
|
<
|
|
==============================================================================
|
|
5. AUTH *vimpack-auth*
|
|
|
|
Tokens are never stored in the config. `registry.auth` accepts:
|
|
|
|
env name of an environment variable holding the token
|
|
token_cmd command (list or shell string) that prints the token
|
|
header header name, default "Authorization"
|
|
prefix value prefix, default "Bearer " for Authorization
|
|
|
|
A literal `token` key is rejected at |vimpack-setup| time.
|
|
>lua
|
|
auth = { header = 'X-JFrog-Art-Api', prefix = '',
|
|
token_cmd = { 'pass', 'show', 'artifactory/api-key' } }
|
|
<
|
|
==============================================================================
|
|
6. COMMANDS *vimpack-commands*
|
|
|
|
*:VimpackInstall*
|
|
:VimpackInstall [{name}...]
|
|
Install plugins that are missing or whose installed version, digest or
|
|
start/opt placement has drifted from the spec. Up-to-date plugins are
|
|
skipped. With names, restrict the work to those plugins.
|
|
|
|
*:VimpackUpdate*
|
|
:VimpackUpdate [{name}...]
|
|
Re-fetch plugins whether or not the manifest says they are current.
|
|
|
|
*:VimpackClean*
|
|
:VimpackClean
|
|
Remove installed plugins and manifest entries that are no longer in the
|
|
spec.
|
|
|
|
*:VimpackStatus*
|
|
:VimpackStatus
|
|
Print wanted versus installed version per plugin, with a state of "ok",
|
|
"missing", "drift" or "orphan".
|
|
|
|
==============================================================================
|
|
7. LAYOUT *vimpack-layout*
|
|
|
|
stdpath('cache')/vimpack/archives/ downloaded archives
|
|
stdpath('cache')/vimpack/stage/ extraction staging area
|
|
stdpath('data')/vimpack/manifest.json installed name/version/sha256
|
|
stdpath('data')/site/pack/vimpack/start/<name>
|
|
stdpath('data')/site/pack/vimpack/opt/<name>
|
|
|
|
An install extracts to the staging area, hoists a single wrapper directory if
|
|
the archive has one, then renames into place after parking any previous tree
|
|
aside. If the rename fails the previous tree is restored.
|
|
|
|
Lazy loading is out of scope: `opt = true` only chooses the opt/ bucket.
|
|
|
|
==============================================================================
|
|
8. LUA API *vimpack-api*
|
|
|
|
require('vimpack').setup(opts) configure; returns cfg or nil,err
|
|
require('vimpack').install(names?) same as |:VimpackInstall|
|
|
require('vimpack').update(names?) same as |:VimpackUpdate|
|
|
require('vimpack').clean() same as |:VimpackClean|
|
|
require('vimpack').status() same as |:VimpackStatus|
|
|
require('vimpack').status_rows() status as a list of tables
|
|
require('vimpack').register_resolver(n, fn) add a registry resolver
|
|
|
|
vim:tw=78:ts=8:ft=help:norl:
|