Skip to content

feat(hm): expose a headless startup check for the wrapped neovim - #105

Merged
myuron merged 5 commits into
mainfrom
feat/startup-check
Oct 17, 2026
Merged

myuron merged 5 commits into
mainfrom
feat/startup-check

Conversation

@myuron

@myuron myuron commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Summary

Adds a derivation that starts the wrapped Neovim headless with the user's config and fails on any startup error. A dotfiles CI can then catch a broken plugin bump with one line:

checks.${system}.nvim = config.programs.nvimx.env.check;

API

Both entry points share one implementation in nix/lib/check.nix:

  • lib.mkCheck { env, configDir, ignoreErrors }.
  • env.check: makeEnv gains optional configDir and check arguments. env.check throws a clear error if it is forced while configDir is null; the throw is lazy.
  • Home-manager: the module passes configDir through and adds one option, programs.nvimx.check.ignoreErrors.
  • Drv paths: env.wrapped and env.farm are unchanged (compared with main).
  • configDir form: it must be a path literal or a store path. An absolute string fails at evaluation in pure mode, not inside the sandbox.

What the check does

It runs in the sandbox with a copy of configDir and temporary XDG/HOME directories, using the driver lua/nvimx/startup-check.lua.

  • Loading: every plugin installed in the farm is loaded through lazy.load.
    • Dev plugins are outside the farm, so they are skipped by construction, and their "not installed" error is filtered out.
    • A plugin that is in the spec but missing from the lock fails with a "re-run nvimx-lock" hint.
    • A config that never calls lazy.setup() fails rather than passing with nothing checked.
  • Error collection, done inside Neovim rather than by grepping stderr:
    • vim.ui_attach with ext_messages, keeping only error kinds;
    • a guard on vim.notify that also catches ERROR notifications from plugins that replace it, whether with a function or with a callable table such as nvim-notify. It stores each assigned function wrapped, so the common local orig = vim.notify chaining pattern calls the previous function and never loops. A depth counter records a chained ERROR only once.
    • v:errmsg is not used, because silent! sets it.
  • Robustness:
    • timeout -k 10 120: plain SIGTERM can't stop nvim while it is busy in Lua. Codes 124 and 137 are both reported as a timeout.
    • A success sentinel, so a config that runs :qa during startup can't pass by exiting 0.
  • Network-fetching plugins:
    • programs.nvimx.check.ignoreErrors / ignoreErrors takes plain substrings to allow.
    • NVIMX_CHECK=1 is exported so a config can switch such plugins off.
    • Trade-off: an ERROR that the user's own notify filter drops is still recorded; ignoreErrors is the way out.

Tests

  • checks.startup-check:
    • the good, tolerated (missing dev plugin) and allowlisted configs must build;
    • env.check.runner runs on these broken fixtures, each expected to fail with a specific message: init error, error in a lazy-loaded config, replaced notify, a callable-table notifier like nvim-notify (vim.notify = require("notify")), chained notify + ERROR (reported exactly once, no hang), stale lock, no lazy, early quit;
    • a chained notify that only emits WARN must pass.
  • checks.hm-module-startup-check: proves configDir and ignoreErrors reach makeEnv through the module.
  • Mutations, each failing the check:
    • removing the dev-plugin skip;
    • removing the depth == 0 guard;
    • disabling the sentinel;
    • disabling the notify guard;
    • skipping lazy.load.

The 120 s timeout path itself is not exercised (too slow for CI).

Verification

  • nix fmt -- --ci, nix flake check
  • nix build .#checks.x86_64-linux.{startup-check,hm-module-startup-check}, nix eval .#checks.aarch64-darwin.{startup-check,hm-module-startup-check}.drvPath
  • env.wrapped / env.farm drvPaths identical on main and on this branch.

Closes #79

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 9 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 63c31d56-9ad4-44f1-86b6-bf72a5ad4515
📥 Commits

Reviewing files that changed from the base of the PR and between 883733e and e5980e1.

📒 Files selected for processing: 20
  • README.md
  • lua/nvimx/startup-check.lua
  • nix/home-manager/default.nix
  • nix/lib/check.nix
  • nix/lib/default.nix
  • nix/lib/make-env.nix
  • tests/checks/default.nix
  • tests/checks/hm-module-startup-check.nix
  • tests/checks/scripts/startup-check.sh
  • tests/checks/startup-check.nix
  • tests/fixtures/startup-check/chain-error/init.lua
  • tests/fixtures/startup-check/chain-warn/init.lua
  • tests/fixtures/startup-check/early-quit/init.lua
  • tests/fixtures/startup-check/init-error/init.lua
  • tests/fixtures/startup-check/lazy-config-error/init.lua
  • tests/fixtures/startup-check/no-lazy/init.lua
  • tests/fixtures/startup-check/notify-callable/init.lua
  • tests/fixtures/startup-check/notify-error/init.lua
  • tests/fixtures/startup-check/stale-lock/init.lua
  • tests/fixtures/startup-check/tolerated/init.lua
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

myuron and others added 5 commits October 17, 2026 14:07
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… ones

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sses

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@myuron
myuron force-pushed the feat/startup-check branch from e5980e1 to 1695abd Compare October 17, 2026 05:08
@myuron
myuron merged commit 030db70 into main Oct 17, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(hm): expose a headless startup check for the wrapped neovim

1 participant