Add arti-pack plugin manager scaffold #1
Reference in New Issue
Block a user
Delete Branch "benvin/scaffold"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Neovim plugin managers clone from GitHub, putting the editor on the public network and pinning nothing but a moving branch. arti-pack fetches tagged source archives from any HTTP artifact repository instead, so every plugin arrives as an immutable, cached artifact.
Review findings — diff matches the PR body (all five bullets accounted for, nothing unaccounted extra), body follows conventions, and this is a coherent atomic scaffold (fine to keep as one PR for a new repo). Tests for spec/fs/lock/installer are genuinely good (path-traversal cases, corrupt-archive rejection, cache reuse, concurrency, partial-failure isolation).
Must fix
lua/arti-pack/init.lua:122—M.clean()callslocal specs = spec_mod.parse_all(M.config.plugins), capturing only the first return value.spec_mod.parse_allreturnsspecs, errors(see correct usage at line 95 and 157). Any spec entry that fails to parse (bad tag, invalid ext, malformed source, etc.) is silently dropped fromspecs/wanted, so:ArtiPackCleantreats that plugin's already-installed directory as orphaned andfs.rm_rfs it, and strips its lock entry — even though it's still listed in the user's config, just with a typo elsewhere. This directly contradicts the documented behavior ("Remove installed plugins no longer in the spec", README.md:70) and is exactly the "deletes the wrong directory" failure mode a plugin manager can't afford. Fix: captureerrorstoo, and refuse to clean (or at least warn loudly) whenparse_allreturned any errors, rather than treating unparseable-but-configured plugins as orphaned.lua/arti-pack/init.lua(sync/clean/list/setup) has zero test coverage — there's noinit_spec.luaamong the added specs (fs_spec, installer_spec, lock_spec, spec_spec only). This is the module owning the destructiveclean()path and directory-move logic, and a single test asserting "an unparseable spec entry doesn't get its installed directory removed by :ArtiPackClean" would have caught finding #1.Nice to have
All three
.woodpecker/*.yamlsetserviceAccountName: defaultinstead of a dedicated arti-pack SA, with no accompanying argocd-apps PR. Low risk since none of these jobs touch secrets or deploy, but it's a stated org convention.lock.lua:36Lock:save()writes the JSON file in place (no temp+rename). An interrupted nvim process during save can leave a truncated lock.json;load()already tolerates that by resetting to{}, so the failure mode is "acts like first run" rather than data loss, but a temp+fs.renamewrite would be more robust andfs.renamealready exists in this codebase.Archive extraction (
unzip -o,tar -xzf) relies entirely on the extraction tool's own defenses against path traversal / absolute-path entries in a crafted archive. Source is the estate's own artifactapi GitHub proxy (semi-trusted), so severity is low, but worth a one-line note on the trust assumption.Reviewed the two changes since the last clean pass (de-branding, required registry, commit
c75a866). The registry contract's core logic is genuinely solid — verified all entry points (setup,sync,clean,list, and a command invoked beforesetup()), and confirmed by actually deleting the guard and runningmake test: 5 tests fail concretely (spec_spec.lua:105/114,init_spec.lua:159/203/215), so these aren't guard-less rubber stamps.M.url's trailing-slash stripping is byte-for-byte unchanged from beforec75a866. De-branding is clean: noartifactapi/k8s.syd1.auleft inlua/,doc/,README.md, ortests/; the one remaininggit.unkin.nethit (README.md:24, thegit cloneinstall command) is this repo's own real, public clone URL, not a private-deployment assumption — fine to keep, same rationale as the.woodpecker/carve-out.Must fix
lua/arti-pack/spec.lua:73-81(M.check_registry) — the http(s) check only inspects the character immediately after://; it never trims the value, so a registry with leading/trailing whitespace (a stray newline from a file read, a copy-pasted trailing space) sails through validation and gets baked verbatim into the resolved URL instead of being rejected. Reproduced directly:That malformed URL goes straight to curl (
installer.lua:75), producing a raw curl error instead of the intended clear "registry is not configured" message — exactly the failure mode this commit set out to close. Fix:registry = trim(registry)(the file already definestrim()for owner/tag) before matching incheck_registry.PR body doesn't describe the final state. It still only lists the original 5-bullet scaffold; it says nothing about the registry now being a required, validated option (the change this PR revision is centered on) or about the docs being rewritten vendor-neutral, and doesn't mention the two bugfix commits (clean aborts on parse errors, lockfile saved via temp+rename) that also landed on this branch. Please rewrite the body to reflect what's actually in the diff today.
Nice to have (carried over, unaddressed, low risk — not blocking)
.woodpecker/*.yamlstill useserviceAccountName: defaultwith no dedicated SA / argocd-apps PR.unzip/tar's own path-traversal defenses against a crafted archive; worth a one-line trust-assumption note.Reviewed
e61e950against the round-2 finding. Verified independently (not from the diff/commit message): builtspec.check_registry/spec.urlby hand for leading space, trailing space, trailing newline, embedded newline, embedded space, tab (leading and embedded), CR, whitespace-only, and "scheme followed only by whitespace" — every case either trims to a clean URL or is rejected with the "not an http(s) URL" error; none reach curl malformed. Also revertedlua/arti-pack/spec.luatoc75a866with the new tests in place:make testgives 6 concrete failures, then 0 with the fix restored, confirming they're real regression tests. The whitespace hole from round 2 is genuinely closed, andregistry/url/tag/owner+reponow share the same trim-outer-reject-inner rule, documented accurately in README.md and doc/arti-pack.txt.Must fix
3190b30clean aborts when a spec fails to parse,11ce1felockfile saved via temp+rename). A reader of the PR page today gets no idea any of that shipped. Please rewrite it to reflect what's actually in the diff now.Nice to have
tests/spec_spec.lua:176-182('rejects a whitespace-only registry') is non-load-bearing: whitespace-only strings (' ','\n', etc.) were already rejected by the pre-fixcheck_registrytoo, since they never matched the leading'^https?://'anchor with or without a trim step. Confirmed by revertingspec.luatoc75a866: this specific test still passes while the other 5 new tests fail. It doesn't hurt anything, but it isn't testing the trim fix — worth either removing or replacing with a case that only fails without the trim (e.g.'https://x 'alone, already covered elsewhere, or noting it's a belt-and-suspenders check).lua/arti-pack/spec.lua:49-52(as/name viabad_name, line 12-14) doesn't follow the same trim-then-check rule asregistry/url/tag: it never trims, soas = 'tokyonight '(trailing-space typo) is flatly rejected as an "unusable directory name" instead of being trimmed and accepted like the same typo would be onregistryorurl. Not a security issue — rejecting is the safe direction — but it's a different rule for a field the docs don't call out either way, worth a one-line README/doc note or aligning the behavior for consistency.