package git import ( "errors" "os" "time" "github.com/ProtonMail/go-crypto/openpgp" "github.com/ProtonMail/go-crypto/openpgp/armor" "github.com/go-git/go-billy/v5" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" ) type CommitOption func(*Commit) func WithAuthor(name, email string) CommitOption { return func(c *Commit) { c.Author = &object.Signature{ Name: name, Email: email, When: time.Now(), } } } func WithCommitter(name, email string) CommitOption { return func(c *Commit) { c.Committer = &object.Signature{ Name: name, Email: email, When: time.Now(), } } } func WithFileContent(content []byte, file billy.File) CommitOption { return func(c *Commit) { c.Content = content c.File = file } } type Commit struct { Author *object.Signature Committer *object.Signature Content []byte File billy.File project *Project } func (p *Project) NewCommit(options ...CommitOption) *Commit { commit := &Commit{project: p} for _, option := range options { option(commit) } return commit } func (c *Commit) Create(msg string) error { var ( signer *openpgp.Entity ) if c.project.KeyFile != "" { file, err := os.Open(c.project.KeyFile) if err != nil { return err } defer file.Close() block, err := armor.Decode(file) if err != nil { return err } entityList, err := openpgp.ReadKeyRing(block.Body) if err != nil || len(entityList) < 1 { return err } signer = entityList[0] } worktree, err := c.project.repository.Worktree() if err != nil { return err } _, err = c.project.repository.Branch(c.project.Branch) if errors.Is(err, git.ErrBranchNotFound) { err = c.project.repository.CreateBranch( &config.Branch{Name: c.project.Branch, Remote: c.project.Branch}, ) if err != nil { return err } } if err != nil { return err } err = worktree.Checkout(&git.CheckoutOptions{ Branch: plumbing.ReferenceName(c.project.Branch), }) if err != nil { return err } if _, err = c.File.Write(c.Content); err != nil { return err } if err = c.File.Close(); err != nil { return err } if _, err = worktree.Add(c.File.Name()); err != nil { return err } commitOpts := git.CommitOptions{Author: c.Author} if c.Committer != nil { commitOpts.Committer = c.Committer } if signer != nil { commitOpts.SignKey = signer } _, err = worktree.Commit(msg, &commitOpts) return err } func (c *Commit) Push() error { origin, err := c.project.repository.Remote("origin") if err != nil { return err } pushOpts := git.PushOptions{} if c.project.auth != nil { pushOpts.Auth = c.project.auth } return origin.Push(&pushOpts) }