|
| 1 | +# CI shares the local Neovim toolchain from a single source |
| 2 | + |
| 3 | +The e2e test environment is defined once, in `slop-env/` (the Neovim overlay in |
| 4 | +`slop-env/nix/` and the runtime config in `slop-env/nvim/`), and consumed through |
| 5 | +the root `flake.nix`. CI builds its test image from that same source rather than |
| 6 | +from a parallel copy. Concretely, the root flake exposes a headless |
| 7 | +`devShells.test` — the same `nvim-dev` derivation, packpath, and |
| 8 | +`~/.config/nvim-dev → slop-env/nvim` symlink the local devShell uses, minus the |
| 9 | +jail/claude/sandbox machinery — and `ci/Dockerfile` builds from it. The former |
| 10 | +duplicate, `ci/nix/kickstart-nix.nvim/`, is deleted. |
| 11 | + |
| 12 | +We adopted this after the test harness diverged between environments: CI ran a |
| 13 | +copy pinned to nixpkgs `nixos-25.05` (Neovim 0.11.2) while local development ran |
| 14 | +`slop-env/` on `nixos-26.05` (Neovim 0.12.2), and the two config copies had |
| 15 | +already drifted (old `require('lspconfig').setup` vs. `vim.lsp.config`/`enable`, |
| 16 | +`nixfmt` vs. `nixfmt-rfc-style`). The whole harness depends on one fragile chain |
| 17 | +— Plenary spawns each spec in a child Neovim that loads `~/.config/nvim-dev/init.lua`, |
| 18 | +and that `init.lua` is what wires `NVIM_PACKPATH` onto the packpath, sets the |
| 19 | +indent options that make `lua_ls` emit spaces not tabs, and configures the LSPs |
| 20 | +(see [the test-harness notes](../../tests/run.sh)). Two copies of that chain means |
| 21 | +two ways for it to silently rot. One source means the version local developers |
| 22 | +run is the version CI gates on. |
| 23 | + |
| 24 | +A flake may only import paths inside its own root, so a sub-flake under `ci/nix/` |
| 25 | +cannot import `../../../slop-env`. Routing CI through the root flake (which already |
| 26 | +does `import ./slop-env/nix/neovim-overlay.nix`) is therefore the mechanism that |
| 27 | +makes a single source possible, not merely the tidier option. |
| 28 | + |
| 29 | +## Consequences |
| 30 | + |
| 31 | +- The e2e Docker path is the only CI surface that consolidates here. The |
| 32 | + binary-download workflows (`typecheck`, `typecheck-debug`, `gendocs`, `style`, |
| 33 | + `lint`) fetch upstream release artifacts and are not Nix-based; they pin their |
| 34 | + Neovim / lua-language-server / StyLua versions independently and must be bumped |
| 35 | + to match `slop-env` separately to keep the environments aligned. Version parity |
| 36 | + across those is a manual obligation, not something the flake enforces. |
| 37 | +- `e2e-test.yml` is unchanged: `docker run … nvim-dev --headless -c |
| 38 | + "PlenaryBustedDirectory <dir>" -c qa`, with no `minimal_init`. A `minimal_init` |
| 39 | + would replace `init.lua` and break the bootstrap chain above, so the harness |
| 40 | + must always run the spec child through the real dev config. |
| 41 | +- Test fixtures are opened by **relative** path (`tests/ft/…`), never the Docker |
| 42 | + WORKDIR absolute (`/ninjection/…`); the working directory is the project root in |
| 43 | + both environments. |
| 44 | +- The root flake gains a CI-facing output (`devShells.test`). Its inputs include |
| 45 | + `jail-nix` / `llm-agents` / `nix-slop-dev`; building `test` must not force those |
| 46 | + (they stay lazy). If a build does pull them, the fallback is to make `slop-env/` |
| 47 | + its own flake so a lean CI consumer can import it without the agent inputs. |
| 48 | +- Bumping the shared source bumps everything at once — Neovim, treesitter |
| 49 | + grammars, `lua_ls`, plugins — so a single nixpkgs bump can change formatting and |
| 50 | + type-check output together. That is the intended trade: one knob, one |
| 51 | + verification pass, no per-environment surprises. |
0 commit comments