From 3d7dca2c2602670afb5515d176182761701d90e2 Mon Sep 17 00:00:00 2001 From: Zack Annexstein Date: Tue, 24 Oct 2023 09:55:57 -0700 Subject: [PATCH 1/4] wip --- go.mod | 2 +- repository/repo-operations.go | 55 ++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 36b2217..41f3885 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/sirupsen/logrus v1.7.0 github.com/stretchr/testify v1.7.0 github.com/urfave/cli v1.22.5 + golang.org/x/crypto v0.6.0 golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 ) @@ -44,7 +45,6 @@ require ( github.com/skeema/knownhosts v1.1.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect - golang.org/x/crypto v0.6.0 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/term v0.5.0 // indirect diff --git a/repository/repo-operations.go b/repository/repo-operations.go index d1f3761..28d9d16 100644 --- a/repository/repo-operations.go +++ b/repository/repo-operations.go @@ -7,11 +7,15 @@ import ( "io/ioutil" "os" "os/exec" + "os/user" + "path/filepath" "strings" "time" + "github.com/ProtonMail/go-crypto/openpgp" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" + gogitconfig "github.com/go-git/go-git/v5/plumbing/format/config" "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/sirupsen/logrus" @@ -306,6 +310,54 @@ func commitLocalChanges(status git.Status, config *config.GitXargsConfig, reposi "Repo": remoteRepository.GetName(), }).Debug("Local repository worktree no longer clean, will stage and add new files and commit changes") + // Step 1: Locate the global git configuration file + usr, err := user.Current() + if err != nil { + return err // handle error + } + gitConfigPath := filepath.Join(usr.HomeDir, ".gitconfig") + + // Step 2: Open the global git configuration file + file, err := os.Open(gitConfigPath) + if err != nil { + return err // handle error + } + defer file.Close() + + // Step 3: Create a new Decoder + decoder := gogitconfig.NewDecoder(file) + + // Step 4: Decode the configuration + var cfg gogitconfig.Config + if err := decoder.Decode(&cfg); err != nil { + return err // handle error + } + + // Step 5: Access the required configuration values + signingKeyID := cfg.Section("user").Option("signingkey") + gpgProgramPath := cfg.Section("gpg").Option("program") + + if gpgProgramPath == "" { + // Locate the GPG program if not specified in the git configuration + gpgProgramPath, err = exec.LookPath("gpg") + if err != nil { + return err // handle error + } + } + + // After obtaining signingKeyID and gpgProgramPath, export the signing key + keyData, err := exec.Command(gpgProgramPath, "--export-secret-keys", "--armor", signingKeyID).Output() + if err != nil { + return err // handle error + } + + // Load the exported key into an *openpgp.Entity object + keyRing, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(keyData)) + if err != nil { + return err // handle error + } + signingKey := keyRing[0] // Assume the first key is the signing key + // Track the fact that worktree changes were made following execution config.Stats.TrackSingle(stats.WorktreeStatusDirty, remoteRepository) @@ -332,7 +384,8 @@ func commitLocalChanges(status git.Status, config *config.GitXargsConfig, reposi // option when configuring our commit option so that all modified and deleted files // will have their changes committed commitOps := &git.CommitOptions{ - All: true, + All: true, + SignKey: signingKey, } _, commitErr := worktree.Commit(config.CommitMessage, commitOps) From 5a1481d4e9336d5a2f7bb43e5f5a460fcf51f2d0 Mon Sep 17 00:00:00 2001 From: Zack Annexstein Date: Wed, 25 Oct 2023 09:53:57 -0700 Subject: [PATCH 2/4] commit signing --- go.mod | 5 +-- go.sum | 4 +++ repository/repo-operations.go | 67 ++++++++++++----------------------- 3 files changed, 30 insertions(+), 46 deletions(-) diff --git a/go.mod b/go.mod index 41f3885..a0ba35b 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,15 @@ module github.com/gruntwork-io/git-xargs go 1.18 require ( + github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 github.com/go-git/go-git/v5 v5.6.1 github.com/google/go-github/v43 v43.0.0 github.com/gruntwork-io/go-commons v0.8.2 github.com/pterm/pterm v0.12.42 github.com/sirupsen/logrus v1.7.0 github.com/stretchr/testify v1.7.0 + github.com/tcnksm/go-gitconfig v0.1.2 github.com/urfave/cli v1.22.5 - golang.org/x/crypto v0.6.0 golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 ) @@ -18,7 +19,6 @@ require ( atomicgo.dev/cursor v0.1.1 // indirect atomicgo.dev/keyboard v0.2.8 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect github.com/cloudflare/circl v1.1.0 // indirect github.com/containerd/console v1.0.3 // indirect @@ -45,6 +45,7 @@ require ( github.com/skeema/knownhosts v1.1.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect + golang.org/x/crypto v0.6.0 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/sys v0.5.0 // indirect golang.org/x/term v0.5.0 // indirect diff --git a/go.sum b/go.sum index e229c00..a7f18b8 100644 --- a/go.sum +++ b/go.sum @@ -420,6 +420,7 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= @@ -518,6 +519,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw= +github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= @@ -928,6 +931,7 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/repository/repo-operations.go b/repository/repo-operations.go index 28d9d16..971cbfb 100644 --- a/repository/repo-operations.go +++ b/repository/repo-operations.go @@ -7,17 +7,15 @@ import ( "io/ioutil" "os" "os/exec" - "os/user" - "path/filepath" "strings" "time" "github.com/ProtonMail/go-crypto/openpgp" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" - gogitconfig "github.com/go-git/go-git/v5/plumbing/format/config" "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/sirupsen/logrus" + gitconfig "github.com/tcnksm/go-gitconfig" "github.com/google/go-github/v43/github" @@ -310,53 +308,38 @@ func commitLocalChanges(status git.Status, config *config.GitXargsConfig, reposi "Repo": remoteRepository.GetName(), }).Debug("Local repository worktree no longer clean, will stage and add new files and commit changes") - // Step 1: Locate the global git configuration file - usr, err := user.Current() - if err != nil { - return err // handle error - } - gitConfigPath := filepath.Join(usr.HomeDir, ".gitconfig") - - // Step 2: Open the global git configuration file - file, err := os.Open(gitConfigPath) - if err != nil { - return err // handle error - } - defer file.Close() + signingKeyID, err := gitconfig.Global("user.signingkey") - // Step 3: Create a new Decoder - decoder := gogitconfig.NewDecoder(file) - - // Step 4: Decode the configuration - var cfg gogitconfig.Config - if err := decoder.Decode(&cfg); err != nil { - return err // handle error + // Define the commit options with the All field set to true + commitOps := &git.CommitOptions{ + All: true, } - // Step 5: Access the required configuration values - signingKeyID := cfg.Section("user").Option("signingkey") - gpgProgramPath := cfg.Section("gpg").Option("program") + // If user.signingkey is defined and there's no error retrieving it, add the signing key to the commit options + if err == nil && signingKeyID != "" { + gpgProgramPath, err := gitconfig.Global("gpg.program") + if err != nil { + gpgProgramPath, err = exec.LookPath("gpg") + if err != nil { + return err // handle error + } + } - if gpgProgramPath == "" { - // Locate the GPG program if not specified in the git configuration - gpgProgramPath, err = exec.LookPath("gpg") + // Export the signing key + keyData, err := exec.Command(gpgProgramPath, "--export-secret-keys", "--armor", signingKeyID).Output() if err != nil { return err // handle error } - } - // After obtaining signingKeyID and gpgProgramPath, export the signing key - keyData, err := exec.Command(gpgProgramPath, "--export-secret-keys", "--armor", signingKeyID).Output() - if err != nil { - return err // handle error - } + // Load the exported key into an *openpgp.Entity object + keyRing, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(keyData)) + if err != nil { + return err // handle error + } - // Load the exported key into an *openpgp.Entity object - keyRing, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(keyData)) - if err != nil { - return err // handle error + // Set the SignKey field in the commitOps object if we have a signing key to use in our git config + commitOps.SignKey = keyRing[0] } - signingKey := keyRing[0] // Assume the first key is the signing key // Track the fact that worktree changes were made following execution config.Stats.TrackSingle(stats.WorktreeStatusDirty, remoteRepository) @@ -383,10 +366,6 @@ func commitLocalChanges(status git.Status, config *config.GitXargsConfig, reposi // With all our untracked files staged, we can now create a commit, passing the All // option when configuring our commit option so that all modified and deleted files // will have their changes committed - commitOps := &git.CommitOptions{ - All: true, - SignKey: signingKey, - } _, commitErr := worktree.Commit(config.CommitMessage, commitOps) From 3885cf130c81b02700a7fbd8f7b01807780a429d Mon Sep 17 00:00:00 2001 From: Zack Annexstein Date: Wed, 25 Oct 2023 10:42:24 -0700 Subject: [PATCH 3/4] add more logging --- repository/repo-operations.go | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/repository/repo-operations.go b/repository/repo-operations.go index 971cbfb..495a6a4 100644 --- a/repository/repo-operations.go +++ b/repository/repo-operations.go @@ -317,25 +317,46 @@ func commitLocalChanges(status git.Status, config *config.GitXargsConfig, reposi // If user.signingkey is defined and there's no error retrieving it, add the signing key to the commit options if err == nil && signingKeyID != "" { + // Log the found signing key ID + logger.WithFields(logrus.Fields{ + "SigningKeyID": signingKeyID, + }).Debug("Found signing key in git global config, will attempt to use it for commit.") + gpgProgramPath, err := gitconfig.Global("gpg.program") if err != nil { gpgProgramPath, err = exec.LookPath("gpg") if err != nil { - return err // handle error + logger.WithFields(logrus.Fields{ + "Error": err.Error(), + }).Error("Failed to locate GPG program.") + return err } } + logger.WithFields(logrus.Fields{ + "GPGProgramPath": gpgProgramPath, + }).Debug("Located GPG program.") // Export the signing key keyData, err := exec.Command(gpgProgramPath, "--export-secret-keys", "--armor", signingKeyID).Output() if err != nil { - return err // handle error + logger.WithFields(logrus.Fields{ + "Error": err.Error(), + "GPGProgramPath": gpgProgramPath, + "SigningKeyID": signingKeyID, + }).Error("Failed to export signing key.") + return err } + logger.Debug("Successfully exported signing key.") // Load the exported key into an *openpgp.Entity object keyRing, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(keyData)) if err != nil { - return err // handle error + logger.WithFields(logrus.Fields{ + "Error": err.Error(), + }).Error("Failed to load signing key into openpgp.Entity object.") + return err } + logger.Debug("Successfully loaded signing key into openpgp.Entity object.") // Set the SignKey field in the commitOps object if we have a signing key to use in our git config commitOps.SignKey = keyRing[0] From a9f82cf9bc07936810e3d2bdb547e0247a0f1e4b Mon Sep 17 00:00:00 2001 From: Zack Annexstein Date: Wed, 25 Oct 2023 10:47:38 -0700 Subject: [PATCH 4/4] make these debug to follow other repo patterns --- repository/repo-operations.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/repository/repo-operations.go b/repository/repo-operations.go index 495a6a4..ad449ba 100644 --- a/repository/repo-operations.go +++ b/repository/repo-operations.go @@ -328,7 +328,7 @@ func commitLocalChanges(status git.Status, config *config.GitXargsConfig, reposi if err != nil { logger.WithFields(logrus.Fields{ "Error": err.Error(), - }).Error("Failed to locate GPG program.") + }).Debug("Failed to locate GPG program.") return err } } @@ -343,7 +343,7 @@ func commitLocalChanges(status git.Status, config *config.GitXargsConfig, reposi "Error": err.Error(), "GPGProgramPath": gpgProgramPath, "SigningKeyID": signingKeyID, - }).Error("Failed to export signing key.") + }).Debug("Failed to export signing key.") return err } logger.Debug("Successfully exported signing key.") @@ -353,7 +353,7 @@ func commitLocalChanges(status git.Status, config *config.GitXargsConfig, reposi if err != nil { logger.WithFields(logrus.Fields{ "Error": err.Error(), - }).Error("Failed to load signing key into openpgp.Entity object.") + }).Debug("Failed to load signing key into openpgp.Entity object.") return err } logger.Debug("Successfully loaded signing key into openpgp.Entity object.")