Create GitHub releases from inside Neovim, driven by the gh CLI.
Instead of gh's terminal prompts, nvim-ghrelease lists your existing releases,
suggests the next semantic version, and asks each question gh release create asks.
Free-text answers (tag, title, release notes) open in a dedicated buffer per step —
edit, then :wq to move to the next question. When every answer is collected it runs
gh release create and reports the new release URL.
https://github.com/user-attachments/assets/47dda2ee-c6a8-4229-8cee-7135bb7712a5
major / minor / patch / prerelease / custom).:wq saves the answer and advances.Publish / Save as draft / Cancel step, plus a prerelease question.vim.system), no blocking of the editor.vim.system; the plugin refuses to load and warns on older versions). Tested in CI on 0.10, 0.11, stable, and nightly.gh, installed and authenticated: gh auth login{
"mesirendon/nvim-ghrelease",
-- Load at startup so the default keymap (<leader>gr) is registered.
event = "VeryLazy",
opts = {}, -- optional; see Configuration
}
setup()(viaopts) is what installs the default keymaps, so the plugin must be loaded for them to exist. If you prefer to lazy-load only on the keypress, disable the built-in maps and let lazy own the key:{ "mesirendon/nvim-ghrelease", opts = { keymaps = false }, keys = { { "<leader>gr", "<cmd>GhRelease<cr>", desc = "GitHub Release: create" } }, }
use({
"mesirendon/nvim-ghrelease",
config = function()
require("ghrelease").setup()
end,
})
Plug 'mesirendon/nvim-ghrelease'
" then, in your Lua config:
lua require('ghrelease').setup()
add({ source = "mesirendon/nvim-ghrelease" })
Calling setup() is optional — the :GhRelease command is available as soon as the
plugin is on the runtimepath.
Run inside a GitHub repository:
:GhRelease
or from Lua:
require("ghrelease").create()
Then follow the steps:
<CR>, q, or <Esc> to continue.custom.:wq. If a release already exists for that
tag, you're asked to retry with another tag, overwrite the existing release, or
cancel.In any editing buffer, :wq accepts and continues; :q keeps whatever is shown.
If you chose to overwrite, the flow runs gh release edit instead of
gh release create. Otherwise, when the tag already exists locally but has no
release yet, --verify-tag is added to gh release create so it refuses to
create the release unless that tag is also on the remote — protecting you from
tagging the wrong commit.
setup() installs one default keymap, sitting in the <leader>g (git) family:
| Keys | Mode | Action | Command |
|---|---|---|---|
<leader>gr |
n |
GitHub Release: create | :GhRelease |
The description shows up in which-key automatically under your git group.
Pass a keymaps table to setup() (or opts in lazy.nvim). Each key is an action
name; the value is the normal-mode lhs, or false to skip that action:
require("ghrelease").setup({
keymaps = {
create = "<leader>oR", -- remap "create" to <leader>oR
},
})
Disable a single action:
require("ghrelease").setup({
keymaps = { create = false },
})
Disable all default keymaps and map it yourself:
require("ghrelease").setup({ keymaps = false })
vim.keymap.set("n", "<leader>R", "<cmd>GhRelease<cr>",
{ desc = "GitHub Release: create", silent = true })
Defaults, with overrides passed to setup():
require("ghrelease").setup({
list_limit = 30, -- releases shown in the context view
default_bump = "patch", -- highlighted first in the version menu
tag_prefix = "v", -- prefix for the suggested first tag
first_tag = "v0.1.0", -- seed tag when the repo has no releases
win = "float", -- editing window style: "float" | "split" | "vsplit"
height = 12, -- editing buffer height / float rows
keymaps = { -- default keymaps; false disables all (see Keymaps)
create = "<leader>gr",
},
})
The choice menus use vim.ui.select, so they integrate automatically with
dressing.nvim, telescope-ui-select, etc.
Run all specs headlessly (tests/init.lua auto-discovers every tests/*_spec.lua file):
nvim --headless -l tests/init.lua
MIT