WIP: Refactor entire project #16
4 changed files with 233 additions and 269 deletions
154
pkg/git/commit.go
Normal file
154
pkg/git/commit.go
Normal file
|
@ -0,0 +1,154 @@
|
|||
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)
|
||||
}
|
241
pkg/git/git.go
241
pkg/git/git.go
|
@ -1,241 +0,0 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.ar21.de/yolokube/grafana-backuper/pkg/grafana"
|
||||
"github.com/ProtonMail/go-crypto/openpgp"
|
||||
"github.com/go-git/go-billy/v5"
|
||||
"github.com/go-git/go-billy/v5/memfs"
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
"github.com/go-git/go-git/v5/storage/memory"
|
||||
)
|
||||
|
||||
var (
|
||||
fs billy.Filesystem
|
||||
storer *memory.Storage
|
||||
)
|
||||
|
||||
type Payload struct {
|
||||
Author *object.Signature
|
||||
Committer *object.Signature
|
||||
Content []byte
|
||||
Dashboard grafana.FoundBoard
|
||||
DashboardInfo grafana.BoardProperties
|
||||
Directory string
|
||||
KeyFile string
|
||||
Repository *git.Repository
|
||||
Version grafana.DashboardVersion
|
||||
}
|
||||
|
||||
func NewPayload(keyFile string) *Payload {
|
||||
fs = memfs.New()
|
||||
storer = memory.NewStorage()
|
||||
|
||||
return &Payload{
|
||||
Author: nil,
|
||||
Committer: nil,
|
||||
Content: []byte{},
|
||||
Dashboard: grafana.FoundBoard{},
|
||||
DashboardInfo: grafana.BoardProperties{},
|
||||
Directory: "",
|
||||
KeyFile: keyFile,
|
||||
Repository: nil,
|
||||
Version: grafana.DashboardVersion{},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Payload) AddAuthor(name, email string) {
|
||||
p.Author = &object.Signature{
|
||||
Name: name,
|
||||
Email: email,
|
||||
When: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Payload) UpdateAuthor(timestamp time.Time) {
|
||||
p.Author.When = timestamp
|
||||
}
|
||||
|
||||
func (p *Payload) AddCommitter(name, email string) {
|
||||
p.Committer = &object.Signature{
|
||||
Name: name,
|
||||
Email: email,
|
||||
When: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Payload) UpdateCommitter() {
|
||||
p.Committer.When = time.Now()
|
||||
}
|
||||
|
||||
func (p *Payload) UpdateContent(content []byte) {
|
||||
p.Content = content
|
||||
}
|
||||
|
||||
func (p *Payload) UpdateDashboard(dashboard grafana.FoundBoard) {
|
||||
p.Dashboard = dashboard
|
||||
}
|
||||
|
||||
func (p *Payload) UpdateDashboardInfo(dashboardInfo grafana.BoardProperties) {
|
||||
p.DashboardInfo = dashboardInfo
|
||||
}
|
||||
|
||||
func (p *Payload) UpdateVersion(version grafana.DashboardVersion) {
|
||||
p.Version = version
|
||||
}
|
||||
|
||||
func (p *Payload) GetRepo(repoURL, user, password string) (err error) {
|
||||
p.Repository, err = git.Clone(
|
||||
storer,
|
||||
fs,
|
||||
&git.CloneOptions{
|
||||
Auth: genAuth(user, password),
|
||||
URL: repoURL,
|
||||
Progress: os.Stdout,
|
||||
},
|
||||
)
|
||||
|
||||
p.Directory = filepath.Base(repoURL)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Payload) IsVersionCommitted(branch string) bool {
|
||||
refName := plumbing.NewBranchReferenceName(branch)
|
||||
|
||||
ref, err := p.Repository.Reference(refName, false)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
commitIter, err := p.Repository.Log(&git.LogOptions{From: ref.Hash()})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
err = commitIter.ForEach(func(commit *object.Commit) error {
|
||||
if strings.Contains(commit.Message, fmt.Sprintf("Update %s", p.Version.DashboardUID)) && strings.Contains(commit.Message, fmt.Sprintf("version %d", p.Version.ID)) {
|
||||
return fmt.Errorf("version already committed")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return err != nil
|
||||
}
|
||||
|
||||
func genAuth(user, password string) *http.BasicAuth {
|
||||
return &http.BasicAuth{
|
||||
Username: user,
|
||||
Password: password,
|
||||
}
|
||||
}
|
||||
|
||||
func commitDashboard(repo *git.Repository, content []byte, commitMsg, dashboardTitle, folderTitle, gitRepoDirectory, keyFile string, author, committer *object.Signature) (err error) {
|
||||
var (
|
||||
file billy.File
|
||||
signer *openpgp.Entity
|
||||
worktree *git.Worktree
|
||||
)
|
||||
|
||||
if strings.TrimSpace(keyFile) != "" {
|
||||
signer, err = getSigner(keyFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
worktree, err = repo.Worktree()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = fs.MkdirAll(folderTitle, 0755); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := filepath.Join(folderTitle, fmt.Sprintf("%s.json", dashboardTitle))
|
||||
if file, err = fs.Create(filePath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = file.Write(content); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = file.Close(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = worktree.Add(filePath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = worktree.Commit(
|
||||
commitMsg,
|
||||
&git.CommitOptions{
|
||||
Author: author,
|
||||
Committer: committer,
|
||||
SignKey: signer,
|
||||
},
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Payload) CreateCommit() error {
|
||||
var commitmsg string
|
||||
|
||||
if p.Version.Message != "" {
|
||||
commitmsg = fmt.Sprintf(
|
||||
"%s: Update %s to version %d => %s",
|
||||
p.Dashboard.Title,
|
||||
p.Version.DashboardUID,
|
||||
p.Version.ID,
|
||||
p.Version.Message,
|
||||
)
|
||||
} else {
|
||||
commitmsg = fmt.Sprintf(
|
||||
"%s: Update %s to version %d",
|
||||
p.Dashboard.Title,
|
||||
p.Version.DashboardUID,
|
||||
p.Version.ID,
|
||||
)
|
||||
}
|
||||
|
||||
p.UpdateAuthor(p.Version.Created)
|
||||
p.UpdateCommitter()
|
||||
|
||||
return commitDashboard(
|
||||
p.Repository,
|
||||
p.Content,
|
||||
commitmsg,
|
||||
p.Dashboard.Title,
|
||||
p.DashboardInfo.FolderTitle,
|
||||
p.Directory,
|
||||
p.KeyFile,
|
||||
p.Author,
|
||||
p.Committer,
|
||||
)
|
||||
}
|
||||
|
||||
func (p *Payload) PushToRemote(user, password string) error {
|
||||
origin, err := p.Repository.Remote("origin")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return origin.Push(
|
||||
&git.PushOptions{
|
||||
Auth: genAuth(user, password),
|
||||
Progress: os.Stdout,
|
||||
},
|
||||
)
|
||||
}
|
79
pkg/git/project.go
Normal file
79
pkg/git/project.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"github.com/go-git/go-billy/v5"
|
||||
"github.com/go-git/go-billy/v5/memfs"
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
"github.com/go-git/go-git/v5/storage/memory"
|
||||
)
|
||||
|
||||
type ProjectOption func(*Project)
|
||||
|
||||
func WithBasicAuth(user, pass string) ProjectOption {
|
||||
return func(p *Project) {
|
||||
p.auth = &http.BasicAuth{
|
||||
Username: user,
|
||||
Password: pass,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithBranch(branch string) ProjectOption {
|
||||
return func(p *Project) {
|
||||
p.Branch = branch
|
||||
}
|
||||
}
|
||||
|
||||
func WithKey(path string) ProjectOption {
|
||||
return func(p *Project) {
|
||||
p.KeyFile = path
|
||||
}
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
Branch string
|
||||
RepoURL string
|
||||
KeyFile string
|
||||
|
||||
auth transport.AuthMethod
|
||||
fs billy.Filesystem
|
||||
storer *memory.Storage
|
||||
repository *git.Repository
|
||||
}
|
||||
|
||||
func NewProject(url string, options ...ProjectOption) *Project {
|
||||
project := &Project{
|
||||
Branch: "",
|
||||
RepoURL: url,
|
||||
KeyFile: "",
|
||||
fs: memfs.New(),
|
||||
storer: memory.NewStorage(),
|
||||
repository: nil,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(project)
|
||||
}
|
||||
|
||||
return project
|
||||
}
|
||||
|
||||
func (p *Project) Clone() error {
|
||||
cloneOpts := git.CloneOptions{
|
||||
URL: p.RepoURL,
|
||||
}
|
||||
|
||||
if p.auth != nil {
|
||||
cloneOpts.Auth = p.auth
|
||||
}
|
||||
|
||||
var err error
|
||||
p.repository, err = git.Clone(
|
||||
p.storer,
|
||||
p.fs,
|
||||
&cloneOpts,
|
||||
)
|
||||
return err
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/armor"
|
||||
)
|
||||
|
||||
func getSigner(keyFile string) (*openpgp.Entity, error) {
|
||||
file, err := os.Open(keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
block, err := armor.Decode(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entityList, err := openpgp.ReadKeyRing(block.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return entityList[0], nil
|
||||
}
|
Loading…
Reference in a new issue