217 lines
4.4 KiB
Go
217 lines
4.4 KiB
Go
package git
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"gitea.antoine-langlois.net/datahearth/config-mapper/internal/configuration"
|
|
"gitea.antoine-langlois.net/datahearth/config-mapper/internal/misc"
|
|
"github.com/go-git/go-git/v5"
|
|
"github.com/go-git/go-git/v5/plumbing/object"
|
|
"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/plumbing/transport/ssh"
|
|
"github.com/mitchellh/mapstructure"
|
|
)
|
|
|
|
var (
|
|
ErrDirIsFile = errors.New("path is a file")
|
|
)
|
|
|
|
type RepositoryActions interface {
|
|
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)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
repo, err := git.PlainClone(r.repoPath, false, &git.CloneOptions{
|
|
URL: r.url,
|
|
Progress: os.Stdout,
|
|
Auth: r.auth,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r.repository = repo
|
|
return nil
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
if !s.IsDir() {
|
|
return ErrDirIsFile
|
|
}
|
|
|
|
repo, err := git.PlainOpen(r.repoPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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 {
|
|
return err
|
|
}
|
|
|
|
for file := range status {
|
|
_, err = w.Add(file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if _, err := w.Commit(msg, &git.CommitOptions{
|
|
Author: r.GetAuthor(),
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
return r.repository.Push(&git.PushOptions{})
|
|
}
|
|
|
|
func (r *Repository) GetWorktree() (*git.Worktree, error) {
|
|
return r.repository.Worktree()
|
|
}
|
|
|
|
func (r *Repository) GetAuthor() *object.Signature {
|
|
return &object.Signature{
|
|
Name: r.author.name,
|
|
Email: r.author.email,
|
|
When: time.Now(),
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|