mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-11 12:25:47 +02:00
Enhance support for GPG signed tags (#4394)
- **PR Description** When using gpg signing on tags via the git config `tag.gpgSign=true`, it is not possible to do a lightweight tag. You must do an annotated tag (Which is allowed to have an empty description). This PR augments the existing `WithGpgHandling` helper to allow it to specifically target commit signing and tag signing. It then uses that handler, and creates an annotated tag, when the git config demands tag GPG signing. By default it will launch the tag signing in a new subprocess. This does require that users click an extra `<enter>` after creating a signed tag, but this is currently the behavior for signed commits, so I don't feel too bad about that. If they want, they can use the LazyGit configuration option of `git.overrideGpg: true` to promise to LazyGit that they do not need a GPG sub-process (because they reliably already have gpg-agent or similar running in the background). This again matches the current behavior for GPG signed commits. This has no integration test because we don't have the machinery set up in place to set up a GPG key inside of our integration test framework. Fixes https://github.com/jesseduffield/lazygit/issues/2955
This commit is contained in:
commit
7d26a751bd
11 changed files with 69 additions and 46 deletions
|
@ -57,15 +57,31 @@ func (self *ConfigCommands) GetPager(width int) string {
|
|||
return utils.ResolvePlaceholderString(pagerTemplate, templateValues)
|
||||
}
|
||||
|
||||
// UsingGpg tells us whether the user has gpg enabled so that we can know
|
||||
// whether we need to run a subprocess to allow them to enter their password
|
||||
func (self *ConfigCommands) UsingGpg() bool {
|
||||
type GpgConfigKey string
|
||||
|
||||
const (
|
||||
CommitGpgSign GpgConfigKey = "commit.gpgSign"
|
||||
TagGpgSign GpgConfigKey = "tag.gpgSign"
|
||||
)
|
||||
|
||||
// NeedsGpgSubprocess tells us whether the user has gpg enabled for the specified action type
|
||||
// and needs a subprocess because they have a process where they manually
|
||||
// enter their password every time a GPG action is taken
|
||||
func (self *ConfigCommands) NeedsGpgSubprocess(key GpgConfigKey) bool {
|
||||
overrideGpg := self.UserConfig().Git.OverrideGpg
|
||||
if overrideGpg {
|
||||
return false
|
||||
}
|
||||
|
||||
return self.gitConfig.GetBool("commit.gpgsign")
|
||||
return self.gitConfig.GetBool(string(key))
|
||||
}
|
||||
|
||||
func (self *ConfigCommands) NeedsGpgSubprocessForCommit() bool {
|
||||
return self.NeedsGpgSubprocess(CommitGpgSign)
|
||||
}
|
||||
|
||||
func (self *ConfigCommands) GetGpgTagSign() bool {
|
||||
return self.gitConfig.GetBool(string(TagGpgSign))
|
||||
}
|
||||
|
||||
func (self *ConfigCommands) GetCoreEditor() string {
|
||||
|
|
|
@ -150,7 +150,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
|
|||
// we can make this GPG thing possible it just means we need to do this in two parts:
|
||||
// one where we handle the possibility of a credential request, and the other
|
||||
// where we continue the rebase
|
||||
if self.config.UsingGpg() {
|
||||
if self.config.NeedsGpgSubprocessForCommit() {
|
||||
return errors.New(self.Tr.DisabledForGPG)
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ func NewRebaseCommands(
|
|||
}
|
||||
|
||||
func (self *RebaseCommands) RewordCommit(commits []*models.Commit, index int, summary string, description string) error {
|
||||
if self.config.UsingGpg() {
|
||||
if self.config.NeedsGpgSubprocessForCommit() {
|
||||
return errors.New(self.Tr.DisabledForGPG)
|
||||
}
|
||||
|
||||
|
@ -413,7 +413,7 @@ func (self *RebaseCommands) BeginInteractiveRebaseForCommitRange(
|
|||
// we can make this GPG thing possible it just means we need to do this in two parts:
|
||||
// one where we handle the possibility of a credential request, and the other
|
||||
// where we continue the rebase
|
||||
if self.config.UsingGpg() {
|
||||
if self.config.NeedsGpgSubprocessForCommit() {
|
||||
return errors.New(self.Tr.DisabledForGPG)
|
||||
}
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
|
|||
},
|
||||
{
|
||||
testName: "returns error when using gpg",
|
||||
gitConfigMockResponses: map[string]string{"commit.gpgsign": "true"},
|
||||
gitConfigMockResponses: map[string]string{"commit.gpgSign": "true"},
|
||||
commits: []*models.Commit{{Name: "commit", Hash: "123456"}},
|
||||
commitIndex: 0,
|
||||
fileName: []string{"test999.txt"},
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package git_commands
|
||||
|
||||
import "github.com/jesseduffield/gocui"
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
)
|
||||
|
||||
type TagCommands struct {
|
||||
*GitCommon
|
||||
|
@ -12,24 +15,24 @@ func NewTagCommands(gitCommon *GitCommon) *TagCommands {
|
|||
}
|
||||
}
|
||||
|
||||
func (self *TagCommands) CreateLightweight(tagName string, ref string, force bool) error {
|
||||
func (self *TagCommands) CreateLightweightObj(tagName string, ref string, force bool) oscommands.ICmdObj {
|
||||
cmdArgs := NewGitCmd("tag").
|
||||
ArgIf(force, "--force").
|
||||
Arg("--", tagName).
|
||||
ArgIf(len(ref) > 0, ref).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
return self.cmd.New(cmdArgs)
|
||||
}
|
||||
|
||||
func (self *TagCommands) CreateAnnotated(tagName, ref, msg string, force bool) error {
|
||||
func (self *TagCommands) CreateAnnotatedObj(tagName, ref, msg string, force bool) oscommands.ICmdObj {
|
||||
cmdArgs := NewGitCmd("tag").Arg(tagName).
|
||||
ArgIf(force, "--force").
|
||||
ArgIf(len(ref) > 0, ref).
|
||||
Arg("-m", msg).
|
||||
ToArgv()
|
||||
|
||||
return self.cmd.New(cmdArgs).Run()
|
||||
return self.cmd.New(cmdArgs)
|
||||
}
|
||||
|
||||
func (self *TagCommands) HasTag(tagName string) bool {
|
||||
|
|
|
@ -106,7 +106,7 @@ func (gui *Gui) resetHelpersAndControllers() {
|
|||
Suggestions: suggestionsHelper,
|
||||
Files: helpers.NewFilesHelper(helperCommon),
|
||||
WorkingTree: helpers.NewWorkingTreeHelper(helperCommon, refsHelper, commitsHelper, gpgHelper),
|
||||
Tags: helpers.NewTagsHelper(helperCommon, commitsHelper),
|
||||
Tags: helpers.NewTagsHelper(helperCommon, commitsHelper, gpgHelper),
|
||||
BranchesHelper: helpers.NewBranchesHelper(helperCommon, worktreeHelper),
|
||||
GPG: helpers.NewGpgHelper(helperCommon),
|
||||
MergeAndRebase: rebaseHelper,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package helpers
|
||||
|
||||
import "github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
|
||||
type AmendHelper struct {
|
||||
c *HelperCommon
|
||||
gpg *GpgHelper
|
||||
|
@ -18,5 +20,5 @@ func NewAmendHelper(
|
|||
func (self *AmendHelper) AmendHead() error {
|
||||
cmdObj := self.c.Git().Commit.AmendHeadCmdObj()
|
||||
self.c.LogAction(self.c.Tr.Actions.AmendCommit)
|
||||
return self.gpg.WithGpgHandling(cmdObj, self.c.Tr.AmendingStatus, nil)
|
||||
return self.gpg.WithGpgHandling(cmdObj, git_commands.CommitGpgSign, self.c.Tr.AmendingStatus, nil, nil)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
@ -22,8 +23,8 @@ func NewGpgHelper(c *HelperCommon) *GpgHelper {
|
|||
// WithWaitingStatus we get stuck there and can't return to lazygit. We could
|
||||
// fix this bug, or just stop running subprocesses from within there, given that
|
||||
// we don't need to see a loading status if we're in a subprocess.
|
||||
func (self *GpgHelper) WithGpgHandling(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error {
|
||||
useSubprocess := self.c.Git().Config.UsingGpg()
|
||||
func (self *GpgHelper) WithGpgHandling(cmdObj oscommands.ICmdObj, configKey git_commands.GpgConfigKey, waitingStatus string, onSuccess func() error, refreshScope []types.RefreshableView) error {
|
||||
useSubprocess := self.c.Git().Config.NeedsGpgSubprocess(configKey)
|
||||
if useSubprocess {
|
||||
success, err := self.c.RunSubprocess(cmdObj)
|
||||
if success && onSuccess != nil {
|
||||
|
@ -31,20 +32,20 @@ func (self *GpgHelper) WithGpgHandling(cmdObj oscommands.ICmdObj, waitingStatus
|
|||
return err
|
||||
}
|
||||
}
|
||||
if err := self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC}); err != nil {
|
||||
if err := self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: refreshScope}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
} else {
|
||||
return self.runAndStream(cmdObj, waitingStatus, onSuccess)
|
||||
return self.runAndStream(cmdObj, waitingStatus, onSuccess, refreshScope)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *GpgHelper) runAndStream(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error {
|
||||
func (self *GpgHelper) runAndStream(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error, refreshScope []types.RefreshableView) error {
|
||||
return self.c.WithWaitingStatus(waitingStatus, func(gocui.Task) error {
|
||||
if err := cmdObj.StreamOutput().Run(); err != nil {
|
||||
_ = self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
_ = self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: refreshScope})
|
||||
return fmt.Errorf(
|
||||
self.c.Tr.GitCommandFailed, self.c.UserConfig().Keybinding.Universal.ExtrasMenu,
|
||||
)
|
||||
|
@ -56,6 +57,6 @@ func (self *GpgHelper) runAndStream(cmdObj oscommands.ICmdObj, waitingStatus str
|
|||
}
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: refreshScope})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package helpers
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
|
@ -10,36 +11,32 @@ import (
|
|||
type TagsHelper struct {
|
||||
c *HelperCommon
|
||||
commitsHelper *CommitsHelper
|
||||
gpg *GpgHelper
|
||||
}
|
||||
|
||||
func NewTagsHelper(c *HelperCommon, commitsHelper *CommitsHelper) *TagsHelper {
|
||||
func NewTagsHelper(c *HelperCommon, commitsHelper *CommitsHelper, gpg *GpgHelper) *TagsHelper {
|
||||
return &TagsHelper{
|
||||
c: c,
|
||||
commitsHelper: commitsHelper,
|
||||
gpg: gpg,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *TagsHelper) OpenCreateTagPrompt(ref string, onCreate func()) error {
|
||||
doCreateTag := func(tagName string, description string, force bool) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.CreatingTag, func(gocui.Task) error {
|
||||
if description != "" {
|
||||
self.c.LogAction(self.c.Tr.Actions.CreateAnnotatedTag)
|
||||
if err := self.c.Git().Tag.CreateAnnotated(tagName, ref, description, force); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
self.c.LogAction(self.c.Tr.Actions.CreateLightweightTag)
|
||||
if err := self.c.Git().Tag.CreateLightweight(tagName, ref, force); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var command oscommands.ICmdObj
|
||||
if description != "" || self.c.Git().Config.GetGpgTagSign() {
|
||||
self.c.LogAction(self.c.Tr.Actions.CreateAnnotatedTag)
|
||||
command = self.c.Git().Tag.CreateAnnotatedObj(tagName, ref, description, force)
|
||||
} else {
|
||||
self.c.LogAction(self.c.Tr.Actions.CreateLightweightTag)
|
||||
command = self.c.Git().Tag.CreateLightweightObj(tagName, ref, force)
|
||||
}
|
||||
|
||||
return self.gpg.WithGpgHandling(command, git_commands.TagGpgSign, self.c.Tr.CreatingTag, func() error {
|
||||
self.commitsHelper.OnCommitSuccess()
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{
|
||||
Mode: types.ASYNC, Scope: []types.RefreshableView{types.COMMITS, types.TAGS},
|
||||
})
|
||||
})
|
||||
return nil
|
||||
}, []types.RefreshableView{types.COMMITS, types.TAGS})
|
||||
}
|
||||
|
||||
onConfirm := func(tagName string, description string) error {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
|
@ -111,10 +112,11 @@ func (self *WorkingTreeHelper) HandleCommitPressWithMessage(initialMessage strin
|
|||
func (self *WorkingTreeHelper) handleCommit(summary string, description string, forceSkipHooks bool) error {
|
||||
cmdObj := self.c.Git().Commit.CommitCmdObj(summary, description, forceSkipHooks)
|
||||
self.c.LogAction(self.c.Tr.Actions.Commit)
|
||||
return self.gpgHelper.WithGpgHandling(cmdObj, self.c.Tr.CommittingStatus, func() error {
|
||||
self.commitsHelper.OnCommitSuccess()
|
||||
return nil
|
||||
})
|
||||
return self.gpgHelper.WithGpgHandling(cmdObj, git_commands.CommitGpgSign, self.c.Tr.CommittingStatus,
|
||||
func() error {
|
||||
self.commitsHelper.OnCommitSuccess()
|
||||
return nil
|
||||
}, nil)
|
||||
}
|
||||
|
||||
func (self *WorkingTreeHelper) switchFromCommitMessagePanelToEditor(filepath string, forceSkipHooks bool) error {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
|
@ -412,7 +413,8 @@ func (self *LocalCommitsController) handleReword(summary string, description str
|
|||
if models.IsHeadCommit(self.c.Model().Commits, self.c.Contexts().LocalCommits.GetSelectedLineIdx()) {
|
||||
// we've selected the top commit so no rebase is required
|
||||
return self.c.Helpers().GPG.WithGpgHandling(self.c.Git().Commit.RewordLastCommit(summary, description),
|
||||
self.c.Tr.RewordingStatus, nil)
|
||||
git_commands.CommitGpgSign,
|
||||
self.c.Tr.RewordingStatus, nil, nil)
|
||||
}
|
||||
|
||||
return self.c.WithWaitingStatus(self.c.Tr.RewordingStatus, func(gocui.Task) error {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue