NeogitOrg / neogit

An interactive and powerful Git interface for Neovim, inspired by Magit
MIT License
4.02k stars 236 forks source link

GPG key unlock doesn't work on commit #1195

Closed trevarj closed 7 months ago

trevarj commented 7 months ago

Description

With gpgSign set in gitconfig and pinentry-loopback or pinentry-tty enabled, commits cannot be made using neogit.

# ~/.gnupg/gpg.conf
 pinentry-mode loopback
# ~/.gnupg/gpg-agent.conf
pinentry-program /usr/bin/pinentry-tty
allow-loopback-pinentry

Relates to : https://github.com/NeogitOrg/neogit/issues/370

Neovim version

NVIM v0.10.0-dev-2489+gb413f5d04 Build type: RelWithDebInfo LuaJIT 2.1.1707061634

Operating system and version

Linux 6.7.9

Steps to reproduce

  1. :Neogit kind=auto
  2. Stage some changes
  3. cc
  4. Errors or freezes, depending on pinentry setting

Expected behavior

The user is prompted to unlock the gpg key and the changes are committed and the commit is signed.

Actual behavior

When pinentry loopback is enabled, there is just an error. When pinentry-tty is enabled without loopback, the UI gets messed up and nvim freezes

Minimal config

-- NOTE: See the end of this file if you are reporting an issue, etc. Ignore all the "scary" functions up top, those are
-- used for setup and other operations.
local M = {}

local base_root_path = vim.fn.fnamemodify(debug.getinfo(1, "S").source:sub(2), ":p:h") .. "/.min"
function M.root(path)
  return base_root_path .. "/" .. (path or "")
end

function M.load_plugin(plugin_name, plugin_url)
  local package_root = M.root("plugins/")
  local install_destination = package_root .. plugin_name
  vim.opt.runtimepath:append(install_destination)

  if not vim.loop.fs_stat(package_root) then
    vim.fn.mkdir(package_root, "p")
  end

  if not vim.loop.fs_stat(install_destination) then
    print(string.format("> Downloading plugin '%s' to '%s'", plugin_name, install_destination))
    vim.fn.system({
      "git",
      "clone",
      "--depth=1",
      plugin_url,
      install_destination,
    })
    if vim.v.shell_error > 0 then
      error(string.format("> Failed to clone plugin: '%s' in '%s'!", plugin_name, install_destination),
        vim.log.levels.ERROR)
    end
  end
end

---@alias PluginName string The plugin name, will be used as part of the git clone destination
---@alias PluginUrl string The git url at which a plugin is located, can be a path. See https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols for details
---@alias MinPlugins table<PluginName, PluginUrl>

---Do the initial setup. Downloads plugins, ensures the minimal init does not pollute the filesystem by keeping
---everything self contained to the CWD of the minimal init file. Run prior to running tests, reproducing issues, etc.
---@param plugins? table<PluginName, PluginUrl>
function M.setup(plugins)
  vim.opt.packpath = {}                      -- Empty the package path so we use only the plugins specified
  vim.opt.runtimepath:append(M.root(".min")) -- Ensure the runtime detects the root min dir

  -- Install required plugins
  if plugins ~= nil then
    for plugin_name, plugin_url in pairs(plugins) do
      M.load_plugin(plugin_name, plugin_url)
    end
  end

  vim.env.XDG_CONFIG_HOME = M.root("xdg/config")
  vim.env.XDG_DATA_HOME = M.root("xdg/data")
  vim.env.XDG_STATE_HOME = M.root("xdg/state")
  vim.env.XDG_CACHE_HOME = M.root("xdg/cache")

  -- NOTE: Cleanup the xdg cache on exit so new runs of the minimal init doesn't share any previous state, e.g. shada
  vim.api.nvim_create_autocmd("VimLeave", {
    callback = function()
      vim.fn.system({
        "rm",
        "-r",
        "-f",
        M.root("xdg")
      })
    end
  })
end

-- NOTE: If you have additional plugins you need to install to reproduce your issue, include them in the plugins
-- table within the setup call below.
M.setup({
  plenary = "https://github.com/nvim-lua/plenary.nvim.git",
  telescope = "https://github.com/nvim-telescope/telescope.nvim",
  diffview = "https://github.com/sindrets/diffview.nvim",
  neogit = "https://github.com/NeogitOrg/neogit"
})
-- WARN: Do all plugin setup, test runs, reproductions, etc. AFTER calling setup with a list of plugins!
-- Basically, do all that stuff AFTER this line.
require("neogit").setup({}) -- For instance, setup Neogit
trevarj commented 7 months ago

I'm happy to help out with fixing this but I don't have enough time to really dissect the code. I noticed there are a few issues about this in the past, so if there is any information on where this should be fixed, then I can hack around on it on my config and see if can get it to work. Thanks

trevarj commented 7 months ago

Update after tinkering with the code:

client.lua:

  local result = cmd.env(M.get_envs_git_editor()):in_pty(true).call_interactive { verbose = true }

making this call_interactive seems to resolve the issue perfectly.

I will put up a PR and think about how to do this only for commits and not all calls that use wrap()

CKolkey commented 7 months ago

I appreciate you digging into this :) The change makes sense, I think. Truth be told, call_interactive is from before I took over this project, and I'm not entirely clear on it's semantics, save for that it's usually used for stuff like this. Lets take a look at that PR and see

trevarj commented 7 months ago

@CKolkey thanks for the quick response. PR is up!