This repository has been archived on 2024-02-15. You can view files and clone it, but cannot push or open issues or pull requests.
config-mapper/internal/git/git.go

217 lines
4.4 KiB
Go
Raw Normal View History

package git
2022-02-24 22:38:17 +01:00
import (
"errors"
"fmt"
2022-02-24 22:38:17 +01:00
"os"
"time"
2022-02-24 22:38:17 +01:00
"gitea.antoine-langlois.net/datahearth/config-mapper/internal/configuration"
"gitea.antoine-langlois.net/datahearth/config-mapper/internal/misc"
2022-02-24 22:38:17 +01:00
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/object"
2022-02-26 01:28:36 +01:00
"github.com/go-git/go-git/v5/plumbing/transport"
2022-02-24 22:38:17 +01:00
"github.com/go-git/go-git/v5/plumbing/transport/http"
2022-02-26 01:28:36 +01:00
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
"github.com/mitchellh/mapstructure"
2022-02-24 22:38:17 +01:00
)
var (
ErrDirIsFile = errors.New("path is a file")
2022-02-24 22:38:17 +01:00
)
type RepositoryActions interface {
2022-04-07 20:08:44 +02:00
PushChanges(msg string, newLines, removedLines []string) error
GetWorktree() (*git.Worktree, error)
GetAuthor() *object.Signature
openRepository() error
}
type Repository struct {
auth transport.AuthMethod
repository *git.Repository
repoPath string
author author
url string
}
type author struct {
name string
email string
}
func NewRepository(config configuration.Git, repoPath string) (RepositoryActions, error) {
var auth transport.AuthMethod
if config.URL == "" {
return nil, errors.New("a repository URI is needed (either using GIT protocol or HTTPS)")
}
repoPath, err := misc.AbsolutePath(repoPath)
if err != nil {
return nil, err
}
switch sshConfig := config.SSH.(type) {
case map[string]interface{}:
var outConfig configuration.Ssh
if err := mapstructure.Decode(sshConfig, &outConfig); err != nil {
return nil, err
}
auth, err = getSSHAuthMethod(outConfig)
if err != nil {
return nil, err
}
case []interface{}:
for i, c := range sshConfig {
if _, ok := c.(map[interface{}]interface{}); !ok {
fmt.Printf("invalid format for configuration n°%d", i)
continue
}
var outConfig configuration.Ssh
if err := mapstructure.Decode(c, &outConfig); err != nil {
fmt.Printf("failed to decode ssh configuration n°%d: %v\n", i, err)
continue
}
auth, err = getSSHAuthMethod(outConfig)
if err != nil {
fmt.Printf("failed to create SSH authentication method for configuration n°%d: %v\n", i, err)
continue
}
}
if auth == nil {
auth = &http.BasicAuth{
Username: config.BasicAuth.Username,
Password: config.BasicAuth.Password,
}
}
default:
return nil, errors.New("git ssh configuration canno't be unmarshaled. Please, pass a valid configuration")
}
repo := &Repository{
auth: auth,
repository: nil,
repoPath: repoPath,
url: config.URL,
author: author{
name: config.Name,
email: config.Email,
},
}
if err := repo.openRepository(); err != nil {
return nil, err
}
return repo, nil
}
func (r *Repository) openRepository() error {
s, err := os.Stat(r.repoPath)
2022-02-24 22:38:17 +01:00
if err != nil {
if os.IsNotExist(err) {
repo, err := git.PlainClone(r.repoPath, false, &git.CloneOptions{
URL: r.url,
2022-02-24 22:38:17 +01:00
Progress: os.Stdout,
Auth: r.auth,
2022-02-24 22:38:17 +01:00
})
if err != nil {
return err
2022-02-24 22:38:17 +01:00
}
r.repository = repo
return nil
2022-02-24 22:38:17 +01:00
}
return err
2022-02-24 22:38:17 +01:00
}
2022-02-26 01:28:36 +01:00
if !s.IsDir() {
return ErrDirIsFile
2022-02-24 22:38:17 +01:00
}
repo, err := git.PlainOpen(r.repoPath)
2022-02-24 22:38:17 +01:00
if err != nil {
return err
2022-02-24 22:38:17 +01:00
}
2022-04-07 20:08:44 +02:00
w, err := repo.Worktree()
if err != nil {
return err
}
err = w.Pull(&git.PullOptions{
Auth: r.auth,
})
if err != nil && err != git.NoErrAlreadyUpToDate {
return err
}
r.repository = repo
return nil
}
2022-04-07 20:08:44 +02:00
func (r *Repository) PushChanges(msg string, newLines, removedLines []string) error {
w, err := r.repository.Worktree()
if err != nil {
return err
}
status, err := w.Status()
if err != nil {
2022-04-07 20:53:19 +02:00
return err
2022-04-07 20:08:44 +02:00
}
for file := range status {
_, err = w.Add(file)
if err != nil {
return err
}
}
2022-04-07 20:53:19 +02:00
if _, err := w.Commit(msg, &git.CommitOptions{
Author: r.GetAuthor(),
}); err != nil {
return err
}
2022-03-01 19:52:02 +01:00
2022-04-07 20:53:19 +02:00
return r.repository.Push(&git.PushOptions{})
2022-04-07 20:08:44 +02:00
}
2022-04-07 20:08:44 +02:00
func (r *Repository) GetWorktree() (*git.Worktree, error) {
return r.repository.Worktree()
}
2022-04-07 20:08:44 +02:00
func (r *Repository) GetAuthor() *object.Signature {
return &object.Signature{
Name: r.author.name,
Email: r.author.email,
When: time.Now(),
}
2022-02-24 22:38:17 +01:00
}
func getSSHAuthMethod(config configuration.Ssh) (transport.AuthMethod, error) {
if config.Passphrase == "" && config.PrivateKey == "" {
return nil, errors.New("passphrase and private are empty")
}
privateKey, err := misc.AbsolutePath(config.PrivateKey)
if err != nil {
return nil, err
}
if _, err := os.Stat(privateKey); err != nil {
return nil, err
}
auth, err := ssh.NewPublicKeysFromFile("git", privateKey, config.Passphrase)
if err != nil {
return nil, err
}
return auth, nil
}