Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions .github/actions/setup-kapi-vim/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: 'Set up kapi-vim'
description: 'Build a kapi-vim package, put its nvim on PATH, and restore every lazy.nvim plugin to the committed lazy-lock.json'
inputs:
target:
description: 'Package to build: lite or bundle (bundle reads $KAPI_VIM_LANGS)'
required: true
save-cache:
description: "Save the plugin cache on a miss. Off for callers that change plugins afterwards, so they can't store them under this lockfile's key."
default: 'true'
runs:
using: 'composite'
steps:
- uses: ./.github/actions/install-nix
# --impure so bundle can read $KAPI_VIM_LANGS; a no-op for lite, which
# never calls builtins.getEnv.
- name: Build and put nvim on PATH
shell: bash
run: |
out=$(nix build --impure ".#${{ inputs.target }}" --print-build-logs --no-link --print-out-paths)
echo "$out/bin" >> "$GITHUB_PATH"
# Confirmed root cause of CI's inflated startup numbers (see PR #44):
# without this cache, plugin files land fresh from a network git clone
# on every run, and each cold file read shows up inside nvim's own
# require()/sourcing timings (--startuptime measures the whole call
# including the read, not just execution). That's what was producing
# 100-600ms readings vs. ~25ms locally for the identical build -- not a
# slow plugin, not CPU contention, not codesign/Gatekeeper overhead (all
# measured and ruled out, see ci.yml's benchmark step). Adding this cache
# alone took every matrix entry down to ~20-27ms, matching local numbers
# almost exactly. Keyed on OS and arch because telescope-fzf-native's
# native build output is platform-specific.
- id: cache
uses: actions/cache/restore@v4
with:
path: ~/.local/share/nvim/lazy
key: lazy-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('lazy-lock.json') }}
# The wrapped nvim loads its config from the read-only store, so
# lazy.nvim reads and writes the lockfile through a stdpath("state")
# copy (see lua/config/lazy.lua), hence the copy in below.
#
# A bare `Lazy! restore` on a fresh runner (cache miss above) doesn't
# reproduce the lock: the first-run install handles LazyVim before the
# plugins its specs import are known, rewrites the lockfile without
# their entries, and then installs them at their upstream target. So
# install first, put the committed lock back, restore, and check it
# took. On a cache hit every plugin is already on disk, so the first
# `+qa` installs nothing.
- name: Restore plugins
shell: bash
run: |
nvim --headless +qa
cp lazy-lock.json ~/.local/state/nvim/lazy-lock.json
nvim --headless "+Lazy! restore" +qa
cmp lazy-lock.json ~/.local/state/nvim/lazy-lock.json
# Saved here, straight after the checked restore, rather than at job end:
# the cache then always holds exactly the locked plugins.
- if: inputs.save-cache == 'true' && steps.cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ~/.local/share/nvim/lazy
key: ${{ steps.cache.outputs.cache-primary-key }}
28 changes: 4 additions & 24 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ jobs:
# new user gets, not a hermetic nix build -- but a reproducible one:
# `:Lazy sync` fetches whatever's newest upstream and rewrites the
# lockfile, making this job a moving target; `:Lazy restore` pins to the
# committed lockfile instead (see lua/config/lazy.lua for why that lockfile
# is actually reachable here). Then verifies startup is clean and the
# committed lockfile instead (see .github/actions/setup-kapi-vim for how,
# and the plugin cache that keeps the benchmark below honest). Then verifies startup is clean and the
# expected LSP binaries for that entry's languages resolve on PATH (see
# smoke-test.lua).
#
Expand Down Expand Up @@ -100,29 +100,9 @@ jobs:
KAPI_VIM_LANGS: ${{ matrix.langs }}
steps:
- uses: actions/checkout@v7
- uses: ./.github/actions/install-nix
- id: build
run: echo "out=$(nix build --impure .#${{ matrix.target }} --print-build-logs --no-link --print-out-paths)" >> "$GITHUB_OUTPUT"
- name: Put bin on PATH
run: echo "${{ steps.build.outputs.out }}/bin" >> "$GITHUB_PATH"
# Confirmed root cause of CI's inflated startup numbers below (see
# PR #44): without this cache, plugin files land fresh from a network
# git clone on every run, and each cold file read shows up inside
# nvim's own require()/sourcing timings (--startuptime measures the
# whole call including the read, not just execution). That's what was
# producing 100-600ms readings vs. ~25ms locally for the identical
# build -- not a slow plugin, not CPU contention, not codesign/
# Gatekeeper overhead (all measured and ruled out, see below). Adding
# this cache alone took every matrix entry down to ~20-27ms, matching
# local numbers almost exactly. Keyed on runs-on because
# telescope-fzf-native's native build output is arch-specific.
- name: Cache lazy.nvim plugins
uses: actions/cache@v4
- uses: ./.github/actions/setup-kapi-vim
with:
path: ~/.local/share/nvim/lazy
key: lazy-${{ matrix.runs-on }}-${{ hashFiles('lazy-lock.json') }}
- name: Restore plugins
run: nvim --headless "+Lazy! restore" +qa
target: ${{ matrix.target }}
- name: Run smoke test
run: nvim --headless -S .github/scripts/smoke-test.lua
# Appended here rather than a separate workflow: this job already paid
Expand Down
29 changes: 8 additions & 21 deletions .github/workflows/lazy-update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,19 @@ jobs:
actions: write
steps:
- uses: actions/checkout@v7
- uses: ./.github/actions/install-nix
- id: build
run: echo "out=$(nix build .#lite --print-build-logs --no-link --print-out-paths)" >> "$GITHUB_OUTPUT"
- name: Put bin on PATH
run: echo "${{ steps.build.outputs.out }}/bin" >> "$GITHUB_PATH"
# The wrapped nvim loads its config from the read-only store, so
# lazy.nvim reads and writes the lockfile through a stdpath("state")
# copy (see lua/config/lazy.lua), hence the copies in and out below.
#
# A bare `Lazy! restore` on a fresh runner doesn't reproduce the lock:
# the first-run install handles LazyVim before the plugins its specs
# import are known, rewrites the lockfile without their entries, and
# then installs them at their upstream target. So install first, put
# the committed lock back, restore, and check it took -- otherwise the
# update below would be measured from the wrong commits.
- name: Restore plugins
run: |
nvim --headless +qa
cp lazy-lock.json ~/.local/state/nvim/lazy-lock.json
nvim --headless "+Lazy! restore" +qa
cmp lazy-lock.json ~/.local/state/nvim/lazy-lock.json
# Reads CI's plugin cache for the same lockfile, but never writes it:
# the plugins on disk are about to move past that lockfile.
- uses: ./.github/actions/setup-kapi-vim
with:
target: lite
save-cache: 'false'
- name: Update to new releases
env:
LAZY_UPDATE_REPORT: ${{ runner.temp }}/report.md
run: |
nvim --headless -c "luafile .github/scripts/lazy-update.lua"
# Bring lazy.nvim's stdpath("state") lockfile back into the
# checkout (see .github/actions/setup-kapi-vim).
cp ~/.local/state/nvim/lazy-lock.json lazy-lock.json
cat "$LAZY_UPDATE_REPORT" >> "$GITHUB_STEP_SUMMARY"
- id: diff
Expand Down
Loading