Add gpg key path and passphrase as args
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
This commit is contained in:
37
internal/bootstrap/git/commit_options.go
Normal file
37
internal/bootstrap/git/commit_options.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package git
|
||||
|
||||
// Option is a some configuration that modifies options for a commit.
|
||||
type Option interface {
|
||||
// ApplyToCommit applies this configuration to a given commit option.
|
||||
ApplyToCommit(*CommitOptions)
|
||||
}
|
||||
|
||||
// CommitOptions contains options for making a commit.
|
||||
type CommitOptions struct {
|
||||
*GPGSigningInfo
|
||||
}
|
||||
|
||||
// GPGSigningInfo contains information for signing a commit.
|
||||
type GPGSigningInfo struct {
|
||||
PrivateKeyPath string
|
||||
Passphrase string
|
||||
KeyID string
|
||||
}
|
||||
|
||||
type GpgSigningOption struct {
|
||||
*GPGSigningInfo
|
||||
}
|
||||
|
||||
func (w GpgSigningOption) ApplyToCommit(in *CommitOptions) {
|
||||
in.GPGSigningInfo = w.GPGSigningInfo
|
||||
}
|
||||
|
||||
func WithGpgSigningOption(path, passphrase, keyID string) Option {
|
||||
return GpgSigningOption{
|
||||
GPGSigningInfo: &GPGSigningInfo{
|
||||
PrivateKeyPath: path,
|
||||
Passphrase: passphrase,
|
||||
KeyID: keyID,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ type Git interface {
|
||||
Init(url, branch string) (bool, error)
|
||||
Clone(ctx context.Context, url, branch string, caBundle []byte) (bool, error)
|
||||
Write(path string, reader io.Reader) error
|
||||
Commit(message Commit) (string, error)
|
||||
Commit(message Commit, options ...Option) (string, error)
|
||||
Push(ctx context.Context, caBundle []byte) error
|
||||
Status() (bool, error)
|
||||
Head() (string, error)
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp"
|
||||
gogit "github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
@@ -40,6 +41,12 @@ type GoGit struct {
|
||||
repository *gogit.Repository
|
||||
}
|
||||
|
||||
type CommitOptions struct {
|
||||
GpgKeyPath string
|
||||
GpgKeyPassphrase string
|
||||
KeyID string
|
||||
}
|
||||
|
||||
func New(path string, auth transport.AuthMethod) *GoGit {
|
||||
return &GoGit{
|
||||
path: path,
|
||||
@@ -127,7 +134,7 @@ func (g *GoGit) Write(path string, reader io.Reader) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *GoGit) Commit(message git.Commit) (string, error) {
|
||||
func (g *GoGit) Commit(message git.Commit, opts ...git.Option) (string, error) {
|
||||
if g.repository == nil {
|
||||
return "", git.ErrNoGitRepository
|
||||
}
|
||||
@@ -142,6 +149,12 @@ func (g *GoGit) Commit(message git.Commit) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// apply the options
|
||||
options := &git.CommitOptions{}
|
||||
for _, opt := range opts {
|
||||
opt.ApplyToCommit(options)
|
||||
}
|
||||
|
||||
// go-git has [a bug](https://github.com/go-git/go-git/issues/253)
|
||||
// whereby it thinks broken symlinks to absolute paths are
|
||||
// modified. There's no circumstance in which we want to commit a
|
||||
@@ -173,13 +186,24 @@ func (g *GoGit) Commit(message git.Commit) (string, error) {
|
||||
return head.Hash().String(), git.ErrNoStagedFiles
|
||||
}
|
||||
|
||||
commit, err := wt.Commit(message.Message, &gogit.CommitOptions{
|
||||
commitOpts := &gogit.CommitOptions{
|
||||
Author: &object.Signature{
|
||||
Name: message.Name,
|
||||
Email: message.Email,
|
||||
When: time.Now(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if options.GPGSigningInfo != nil {
|
||||
entity, err := getOpenPgpEntity(*options.GPGSigningInfo)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
commitOpts.SignKey = entity
|
||||
}
|
||||
|
||||
commit, err := wt.Commit(message.Message, commitOpts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -232,3 +256,41 @@ func (g *GoGit) Path() string {
|
||||
func isRemoteBranchNotFoundErr(err error, ref string) bool {
|
||||
return strings.Contains(err.Error(), fmt.Sprintf("couldn't find remote ref %q", ref))
|
||||
}
|
||||
|
||||
func getOpenPgpEntity(info git.GPGSigningInfo) (*openpgp.Entity, error) {
|
||||
r, err := os.Open(info.PrivateKeyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entityList, err := openpgp.ReadKeyRing(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(entityList) == 0 {
|
||||
return nil, fmt.Errorf("no entity formed")
|
||||
}
|
||||
|
||||
var entity *openpgp.Entity
|
||||
if info.KeyID != "" {
|
||||
for _, ent := range entityList {
|
||||
if ent.PrimaryKey.KeyIdString() == info.KeyID {
|
||||
entity = ent
|
||||
}
|
||||
}
|
||||
|
||||
if entity == nil {
|
||||
return nil, fmt.Errorf("no key matching the key id was found")
|
||||
}
|
||||
} else {
|
||||
entity = entityList[0]
|
||||
}
|
||||
|
||||
err = entity.PrivateKey.Decrypt([]byte(info.Passphrase))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return entity, nil
|
||||
}
|
||||
|
||||
66
internal/bootstrap/git/gogit/gogit_test.go
Normal file
66
internal/bootstrap/git/gogit/gogit_test.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// +build unit
|
||||
|
||||
package gogit
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/bootstrap/git"
|
||||
)
|
||||
|
||||
func TestGetOpenPgpEntity(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
keyPath string
|
||||
passphrase string
|
||||
id string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "no default key id given",
|
||||
keyPath: "testdata/private.key",
|
||||
passphrase: "flux",
|
||||
id: "",
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "key id given",
|
||||
keyPath: "testdata/private.key",
|
||||
passphrase: "flux",
|
||||
id: "0619327DBD777415",
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "wrong key id",
|
||||
keyPath: "testdata/private.key",
|
||||
passphrase: "flux",
|
||||
id: "0619327DBD777416",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "wrong password",
|
||||
keyPath: "testdata/private.key",
|
||||
passphrase: "fluxe",
|
||||
id: "0619327DBD777415",
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gpgInfo := git.GPGSigningInfo{
|
||||
PrivateKeyPath: tt.keyPath,
|
||||
Passphrase: tt.passphrase,
|
||||
KeyID: tt.id,
|
||||
}
|
||||
|
||||
_, err := getOpenPgpEntity(gpgInfo)
|
||||
if err != nil && !tt.expectErr {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
if err == nil && tt.expectErr {
|
||||
t.Errorf("expected error when %s", tt.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
BIN
internal/bootstrap/git/gogit/testdata/private.key
vendored
Normal file
BIN
internal/bootstrap/git/gogit/testdata/private.key
vendored
Normal file
Binary file not shown.
Reference in New Issue
Block a user