*arti-pack.txt*   Install Neovim plugins from an artifact store

==============================================================================
CONTENTS                                                   *arti-pack-contents*

  1. Introduction ......................... |arti-pack-introduction|
  2. Setup ................................ |arti-pack-setup|
  3. Plugin specs ......................... |arti-pack-specs|
  4. Branch tracking ...................... |arti-pack-branches|
  5. Commands ............................. |arti-pack-commands|
  6. Layout ............................... |arti-pack-layout|

==============================================================================
1. INTRODUCTION                                        *arti-pack-introduction*

arti-pack installs plugins from upstream source archives served over HTTP by an
artifact repository — a tag for anything that publishes one, a branch for
anything that does not — and unpacks them into Neovim's native package path.
See |packages| for how Neovim loads what arti-pack writes.

Any artifact repository that serves upstream tag and branch archives at a
stable URL works — a JFrog Artifactory remote GitHub repository, or any caching
proxy with the same path shape. Nothing is cloned and the editor never reaches
GitHub itself.

Requires curl. Zip and tar.gz 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.

==============================================================================
2. SETUP                                                      *arti-pack-setup*

                                                            *arti-pack.setup()*
>lua
    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 },
        { 'hrsh7th/cmp-path', branch = 'main' },
      },
    })
<
Options:

  registry     Required, no default. Archive URL prefix placed in front of
               "owner/repo", pointing at your own artifact repository. It
               must be an http:// or https:// URL; surrounding whitespace
               is trimmed and anything else is rejected at setup, so a
               stray space never reaches the download. Until it is set,
               |:ArtiPackSync| refuses to run and says which option to
               set. |:ArtiPackClean| and |:ArtiPackList| never resolve a
               URL and work without it, as do plugin specs carrying an
               explicit "url".
  plugins      List of plugin specs. See |arti-pack-specs|.
  auto_sync    Sync missing and changed plugins on startup. Default true.
  concurrency  Plugins downloaded in parallel. Default 4.
  timeout      Per-download timeout in seconds. Default 120.

==============================================================================
3. PLUGIN SPECS                                               *arti-pack-specs*

Each entry is a table whose first element is "owner/repo".

  tag     Upstream tag, used verbatim in the archive path.
  branch  Upstream branch to track, used verbatim in the archive path.
          See |arti-pack-branches|.
  opt     Install under opt/ for |:packadd| instead of start/. Default false.
  as      Directory name to install as. Defaults to the repo name.
  ext     "zip" or "tar.gz". Default "zip".
  url     Full archive URL, bypassing the registry. Trimmed; it may not
          contain whitespace.

Exactly one of "tag" and "branch" is required. A spec that sets both, or
neither, is rejected with an error naming the plugin, and nothing is installed
for it. Both are one path segment of the archive URL, so both are trimmed of
surrounding whitespace and rejected if they are "." or "..", or contain inner
whitespace, "/", "\", or any of "#", "?", "%", "[", "]", "{", "}" — each of
those would make curl fetch something other than the ref you named. A branch
name containing "/" therefore needs an explicit "url".

The archive URL is built as: >
    <registry>/<owner>/<repo>/archive/refs/tags/<tag>.<ext>
    <registry>/<owner>/<repo>/archive/refs/heads/<branch>.<ext>
<
==============================================================================
4. BRANCH TRACKING                                         *arti-pack-branches*

Plenty of upstreams publish no tags at all, so a "branch" spec installs from
the branch archive instead. A tag is immutable; a branch is not, so a branch
plugin is refetched only when asked for:

  |:ArtiPackSync|    installs a branch plugin that is missing, that moved
                     between start/ and opt/, or whose "branch" changed, and
                     otherwise leaves it alone. It reports how many branch
                     plugins it did not recheck. Startup cost and load on the
                     artifact store stay as they are for tags, and the editor
                     does not change under you on a restart.
  |:ArtiPackUpdate|  refetches every branch plugin, whether or not upstream
                     moved. Nothing else goes looking for new branch content.
  |:ArtiPackSync|!   still reinstalls everything.

Branch archives are never served from the local archive cache, so an update
always sees what the store has now.

The lockfile records the branch name, a digest of the archive that was actually
installed, and when it was fetched — never a version, since a branch is not
one. |:ArtiPackList| reports that rather than guessing: >
    hrsh7th/cmp-path     @main    start  tracking branch main, fetched
                                         2026-09-08 10:14 (7db8f64a)
<
The digest is a content identity, not a file checksum: Neovim's sha256()
refuses binary input, so it hashes the archive's base64 form. It will not
match sha256sum on the archive.

A branch plugin is never reported as up to date, because nothing on disk can
know whether upstream has moved. An update that brings back the same digest is
reported as unchanged, not as an update.

==============================================================================
5. COMMANDS                                                *arti-pack-commands*

                                                                *:ArtiPackSync*
:ArtiPackSync     Install missing plugins and apply changed refs.
:ArtiPackSync!    Reinstall every configured plugin, ignoring recorded state.

                                                              *:ArtiPackUpdate*
:ArtiPackUpdate   Refetch every branch-tracking plugin. See
                  |arti-pack-branches|.

                                                               *:ArtiPackClean*
:ArtiPackClean    Remove installed plugins that are no longer in the spec.

                                                                *:ArtiPackList*
:ArtiPackList     Show every configured plugin and its state.

==============================================================================
6. LAYOUT                                                    *arti-pack-layout*

  stdpath('data')/site/pack/arti-pack/start/<name>   loaded at startup
  stdpath('data')/site/pack/arti-pack/opt/<name>     loaded by |:packadd|
  stdpath('data')/arti-pack/lock.json                recorded installs
  stdpath('cache')/arti-pack                         downloaded archives

Downloads are checked for a zip or gzip magic header before being trusted, and
the single "<repo>-<ref>" directory that source archives wrap their contents in
is stripped on the way in. A plugin that fails is reported and skipped; the
rest of the run continues.

vim:tw=78:ts=8:noet:ft=help:norl:
