Repository navigation
Add Dependabot for actions and a weekly plugin update workflow #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| name: Set up Neovim | ||
| description: >- | ||
| Install Neovim on a Windows runner and point it at this config with empty data, state and cache directories. | ||
| Check the repo out with `path: nvim` first, since Neovim reads $XDG_CONFIG_HOME/nvim. | ||
|
|
||
| inputs: | ||
| version: | ||
| description: Neovim release to install | ||
| default: 0.11.4 # keep in sync with the Neovim you run locally | ||
|
|
||
| runs: | ||
| using: composite | ||
| steps: | ||
| - name: Isolate Neovim directories | ||
| shell: pwsh | ||
| run: | | ||
| "XDG_CONFIG_HOME=$env:GITHUB_WORKSPACE" >> $env:GITHUB_ENV | ||
| foreach ($kind in 'DATA', 'STATE', 'CACHE') { | ||
| "XDG_${kind}_HOME=$(Join-Path $env:RUNNER_TEMP "xdg\$($kind.ToLower())")" >> $env:GITHUB_ENV | ||
| } | ||
|
|
||
| - name: Install Neovim ${{ inputs.version }} | ||
| shell: pwsh | ||
| env: | ||
| NVIM_VERSION: ${{ inputs.version }} | ||
| run: | | ||
| $zip = Join-Path $env:RUNNER_TEMP nvim-win64.zip | ||
| Invoke-WebRequest "https://github.com/neovim/neovim/releases/download/v$env:NVIM_VERSION/nvim-win64.zip" -OutFile $zip | ||
| Expand-Archive $zip -DestinationPath $env:RUNNER_TEMP | ||
| $bin = Join-Path $env:RUNNER_TEMP nvim-win64\bin | ||
| & "$bin\nvim.exe" --version | ||
| $bin >> $env:GITHUB_PATH | ||
|
|
||
| - name: Provide make | ||
| # telescope-fzf-native and LuaSnip build with `make`; the runner image only ships mingw32-make. | ||
| shell: pwsh | ||
| run: | | ||
| if (Get-Command make -ErrorAction SilentlyContinue) { exit 0 } | ||
| $mingw = Get-Command mingw32-make -ErrorAction SilentlyContinue | ||
| if ($mingw) { | ||
| $bin = Join-Path $env:RUNNER_TEMP make-bin | ||
| New-Item -ItemType Directory $bin -Force | Out-Null | ||
| Copy-Item $mingw.Source (Join-Path $bin make.exe) | ||
| $bin >> $env:GITHUB_PATH | ||
| } else { | ||
| choco install make --no-progress -y | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| version: 2 | ||
| updates: | ||
| # lazy.nvim plugins aren't a Dependabot ecosystem; .github/workflows/plugin-update.yml updates lazy-lock.json. | ||
| - package-ecosystem: github-actions | ||
| directories: | ||
| - / | ||
| - /.github/actions/* | ||
| exclude-paths: | ||
| - "*.lock.yml" # generated by `gh aw compile`; refresh those with `gh aw upgrade` instead | ||
| schedule: | ||
| interval: monthly | ||
| groups: | ||
| actions: | ||
| patterns: ["*"] | ||
| commit-message: | ||
| prefix: ci |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| -- Update every plugin, rewrite lazy-lock.json and write a Markdown summary for the update PR. | ||
| -- Run by .github/workflows/plugin-update.yml, after the setup-nvim action isolated Neovim's directories: | ||
| -- nvim --headless "+luafile .github/scripts/plugin-update.lua" | ||
| -- Env: OLD_LOCKFILE is a copy of lazy-lock.json from before the update; the summary goes to SUMMARY_FILE. | ||
|
|
||
| local function read_json(path) | ||
| return vim.json.decode(table.concat(vim.fn.readfile(path), "\n")) | ||
| end | ||
|
|
||
| local function short(sha) | ||
| return "`" .. sha:sub(1, 7) .. "`" | ||
| end | ||
|
|
||
| local function update() | ||
| local Config = require("lazy.core.config") | ||
| local Plugin = require("lazy.core.plugin") | ||
|
|
||
| require("lazy").update({ wait = true, show = false }) | ||
|
|
||
| local old, new = read_json(vim.env.OLD_LOCKFILE), read_json(Config.options.lockfile) | ||
| local names = vim.tbl_keys(vim.tbl_extend("force", {}, old, new)) | ||
| table.sort(names) | ||
|
|
||
| local rows = {} | ||
| for _, name in ipairs(names) do | ||
| local from, to = old[name] and old[name].commit, new[name] and new[name].commit | ||
| if from ~= to then | ||
| local plugin = Config.plugins[name] | ||
| local repo = plugin and plugin.url and (plugin.url:gsub("%.git$", "")) | ||
| local change | ||
| if not from then | ||
| change = "added at " .. short(to) | ||
| elseif not to then | ||
| change = "removed" | ||
| elseif repo and repo:match("^https://github%.com/") then | ||
| change = ("[%s...%s](%s/compare/%s...%s)"):format(short(from), short(to), repo, from, to) | ||
| else | ||
| change = short(from) .. "..." .. short(to) | ||
| end | ||
| table.insert(rows, ("| %s | %s |"):format(name, change)) | ||
| end | ||
| end | ||
|
|
||
| local failed = {} | ||
| for name, plugin in pairs(Config.plugins) do | ||
| if Plugin.has_errors(plugin) then | ||
| table.insert(failed, name) | ||
| end | ||
| end | ||
| table.sort(failed) | ||
|
|
||
| local run = ("%s/%s/actions/runs/%s"):format( | ||
| vim.env.GITHUB_SERVER_URL or "https://github.com", | ||
| vim.env.GITHUB_REPOSITORY or "", | ||
| vim.env.GITHUB_RUN_ID or "" | ||
| ) | ||
| local lines = { | ||
| ("Weekly `:Lazy update` from the [plugin update workflow](%s)."):format(run), | ||
| "CI installs these exact commits into empty directories and runs the smoke test; merge if it's green.", | ||
| "", | ||
| "| Plugin | Change |", | ||
| "| --- | --- |", | ||
| } | ||
| vim.list_extend(lines, rows) | ||
| if #failed > 0 then | ||
| vim.list_extend(lines, { | ||
| "", | ||
| "> [!WARNING]", | ||
| "> These plugins reported errors while updating (see the workflow log): " .. table.concat(failed, ", "), | ||
| }) | ||
| end | ||
| vim.fn.writefile(lines, vim.env.SUMMARY_FILE) | ||
|
|
||
| io.stdout:write(("\n%d plugin(s) changed, %d with errors\n"):format(#rows, #failed)) | ||
| end | ||
|
|
||
| local ok, err = xpcall(update, debug.traceback) | ||
| if not ok then | ||
| io.stdout:write("\nplugin update failed:\n" .. err .. "\n") | ||
| end | ||
| vim.cmd(ok and "qa!" or "cquit 1") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| name: Plugin update | ||
|
|
||
| # Weekly `:Lazy update`. Commits the new lazy-lock.json to simonk/dev/plugin-update, opens or refreshes a PR, | ||
| # and dispatches CI on it, which runs the smoke test on the new commits. On a PR that changes the updater it only | ||
| # does a dry run and shows the would-be PR body in the job summary. | ||
|
|
||
| on: | ||
| schedule: | ||
| - cron: "0 15 * * 1" # Mondays 15:00 UTC | ||
| workflow_dispatch: | ||
| pull_request: | ||
| paths: | ||
| - .github/workflows/plugin-update.yml | ||
| - .github/scripts/plugin-update.lua | ||
| - .github/actions/setup-nvim/** | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | ||
|
|
||
| jobs: | ||
| update: | ||
| name: Update plugins | ||
| runs-on: windows-latest | ||
| timeout-minutes: 30 | ||
| permissions: | ||
| contents: write # push the update branch | ||
| pull-requests: write # open or refresh the PR | ||
| actions: write # dispatch CI; pushes made with GITHUB_TOKEN don't trigger workflows | ||
| env: | ||
| BRANCH: simonk/dev/plugin-update | ||
| BOT_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com | ||
| GH_TOKEN: ${{ github.token }} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Supply chain: the Update plugins step runs
A compromised or broken plugin could push to Suggested fix: split into two jobs.
|
||
| defaults: | ||
| run: | ||
| shell: pwsh | ||
| working-directory: nvim | ||
| steps: | ||
| - uses: actions/checkout@v7 | ||
| with: | ||
| path: nvim # Neovim reads $XDG_CONFIG_HOME/nvim | ||
|
|
||
| - uses: ./nvim/.github/actions/setup-nvim | ||
|
|
||
| - name: Update plugins | ||
| id: update | ||
| env: | ||
| OLD_LOCKFILE: ${{ runner.temp }}/old-lock.json | ||
| SUMMARY_FILE: ${{ runner.temp }}/pr-body.md | ||
| run: | | ||
| Copy-Item lazy-lock.json $env:OLD_LOCKFILE | ||
| nvim --headless "+luafile .github/scripts/plugin-update.lua" | ||
| if ($LASTEXITCODE) { exit $LASTEXITCODE } | ||
| if (-not (git status --porcelain -- lazy-lock.json)) { | ||
| 'Every plugin is already at its latest commit.' >> $env:GITHUB_STEP_SUMMARY | ||
| exit 0 | ||
| } | ||
| Get-Content $env:SUMMARY_FILE >> $env:GITHUB_STEP_SUMMARY | ||
| "changed=true" >> $env:GITHUB_OUTPUT | ||
|
|
||
| - name: Open or refresh the PR | ||
| if: steps.update.outputs.changed == 'true' && github.event_name != 'pull_request' | ||
| run: | | ||
| $PSNativeCommandUseErrorActionPreference = $true | ||
| $pr = gh pr list --head $env:BRANCH --state open --json number --jq '.[].number' | ||
| if ($pr) { | ||
| $authors = gh pr view $pr --json commits --jq '.commits[].authors[].email' | ||
| if ($authors | Where-Object { $_ -ne $env:BOT_EMAIL }) { | ||
| "::warning::#$pr has commits from someone else, so it wasn't overwritten. Merge or close it, then rerun this workflow." | ||
| exit 0 | ||
| } | ||
| } | ||
|
|
||
| git config user.name 'github-actions[bot]' | ||
| git config user.email $env:BOT_EMAIL | ||
| git switch --quiet -C $env:BRANCH | ||
| git commit --quiet -m 'Update plugins' -- lazy-lock.json | ||
| git push --quiet --force origin "HEAD:refs/heads/$env:BRANCH" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The author check only runs when an open PR exists. If the branch is left behind after a PR is closed without merging, Consider recording the remote SHA ( |
||
|
|
||
| $body = Join-Path $env:RUNNER_TEMP pr-body.md | ||
| if ($pr) { | ||
| gh pr edit $pr --body-file $body | ||
| } else { | ||
| gh pr create --base main --head $env:BRANCH --title 'Update plugins' --body-file $body | ||
| } | ||
| gh workflow run ci.yml --ref $env:BRANCH | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pinned Neovim version now lives here, but
.github/agents/pr-reviewer.agent.md(line 28) still tells reviewers to find it in.github/workflows/ci.yml, which no longer has one. Point that line at.github/actions/setup-nvim/action.yml, or reviewers can't tell which Neovim API level to check against.