Skip to content
Closed
65 changes: 51 additions & 14 deletions cmd/entire/cli/objectsigner.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"sync"

"github.com/entireio/cli/cmd/entire/cli/logging"
"github.com/go-git/go-git/v6"
"github.com/go-git/go-git/v6/config"
format "github.com/go-git/go-git/v6/plumbing/format/config"
"github.com/go-git/go-git/v6/x/plugin"
Expand All @@ -27,11 +28,13 @@ func RegisterObjectSigner() {
return nil
}

sysCfg := loadScopedConfig(cfgSource, config.SystemScope)
globalCfg := loadScopedConfig(cfgSource, config.GlobalScope)
var repo *git.Repository
repo, err = git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true})
if err != nil {
repo = nil
}

// Merge system then global so that global settings take precedence.
merged := config.Merge(sysCfg, globalCfg)
merged, sshSignProgram := loadObjectSignerConfig(cfgSource, repo)

if !merged.Commit.GpgSign.IsTrue() {
return nil
Expand All @@ -43,7 +46,7 @@ func RegisterObjectSigner() {
// will be unsigned, which is acceptable since signing is best-effort.
// The default program is "ssh-keygen", which works with go-git's
// native SSH agent signing and does not need to be skipped.
if auto.Format(merged.GPG.Format) == auto.FormatSSH && hasCustomSSHSignProgram(merged.Raw) {
if auto.Format(merged.GPG.Format) == auto.FormatSSH && isCustomSSHSignProgram(sshSignProgram) {
logging.Debug(context.Background(), "skipping native SSH commit signing: custom gpg.ssh.program is configured")
return nil
}
Expand All @@ -65,6 +68,18 @@ func RegisterObjectSigner() {
})
}

func loadObjectSignerConfig(source plugin.ConfigSource, repo *git.Repository) (*config.Config, string) {
sysCfg := loadScopedConfig(source, config.SystemScope)
globalCfg := loadScopedConfig(source, config.GlobalScope)
localCfg := config.NewConfig()
if repo != nil {
localCfg = loadRepoLocalConfig(repo)
}
Comment thread
pfleidi marked this conversation as resolved.
Outdated
sshSignProgram := effectiveSSHSignProgram(sysCfg.Raw, globalCfg.Raw, localCfg.Raw)
merged := config.Merge(sysCfg, globalCfg, localCfg)
return &merged, sshSignProgram
}

// connectSSHAgent connects to the SSH agent via SSH_AUTH_SOCK.
// Returns nil if the agent is unavailable.
func connectSSHAgent() agent.Agent { //nolint:ireturn // must return the ssh agent interface
Expand All @@ -87,22 +102,34 @@ var scopeName = map[config.Scope]string{
config.SystemScope: "system",
}

// hasCustomSSHSignProgram checks whether gpg.ssh.program is set to a
// non-default value in the raw config. The git default is "ssh-keygen",
// which works with go-git's native SSH agent signing. Custom programs
// (e.g. 1Password's op-ssh-sign) use a separate signing mechanism that
// go-git cannot invoke.
// go-git's Config struct does not expose this field, so we read it directly.
func hasCustomSSHSignProgram(raw *format.Config) bool {
func rawSSHSignProgram(raw *format.Config) string {
if raw == nil {
return false
return ""
}

program := raw.Section("gpg").Subsection("ssh").Option("program")
return raw.Section("gpg").Subsection("ssh").Option("program")
}

func isCustomSSHSignProgram(program string) bool {
return program != "" && program != "ssh-keygen"
}
Comment thread
pfleidi marked this conversation as resolved.

func effectiveSSHSignProgram(raws ...*format.Config) string {
for i := len(raws) - 1; i >= 0; i-- {
raw := raws[i]
if raw == nil {
continue
}

program := rawSSHSignProgram(raw)
if program != "" {
return program
}
}

return ""
}

func loadScopedConfig(source plugin.ConfigSource, scope config.Scope) *config.Config {
name := scopeName[scope]

Expand All @@ -120,3 +147,13 @@ func loadScopedConfig(source plugin.ConfigSource, scope config.Scope) *config.Co

return cfg
}

func loadRepoLocalConfig(repo *git.Repository) *config.Config {
cfg, err := repo.Storer.Config()
if err != nil {
fmt.Fprintf(os.Stderr, "warning: failed to parse local git config: %v\n", err)
return config.NewConfig()
}

return cfg
}
Loading
Loading