feat(cli): add git push option with message
This commit is contained in:
parent
759604ebda
commit
71526ec28d
|
@ -2,13 +2,17 @@ storage:
|
|||
# Where will be the repository folder located ? [DEFAULT: MacOS($TMPDIR/config-mapper) | Linux(/tmp/config-mapper)]
|
||||
location: /path/to/folder
|
||||
git:
|
||||
# * by default, if ssh dict is set with its keys filled, I'll try to clone with SSH
|
||||
# username used for commit author
|
||||
name: USERNAME
|
||||
# email used for commit author
|
||||
email: EMAIL
|
||||
repository: git@github.com:DataHearth/my-config.git
|
||||
basic-auth:
|
||||
username: USERNAME
|
||||
# * NOTE: if you're having trouble with error "authentication required", you should maybe use a token access
|
||||
# * In some cases, it's due to 2FA authentication enabled on the git hosting provided
|
||||
password: TOKEN
|
||||
# * by default, if ssh dict is set with its keys filled, I'll try to clone with SSH
|
||||
ssh:
|
||||
# path can be relative and can contain environment variables
|
||||
private-key: /path/to/private/key
|
||||
|
|
30
cmd/cli.go
30
cmd/cli.go
|
@ -4,6 +4,8 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
mapper "github.com/datahearth/config-mapper/internal"
|
||||
"github.com/pterm/pterm"
|
||||
|
@ -38,7 +40,7 @@ var initCmd = &cobra.Command{
|
|||
|
||||
logger.Println("initializing config-mapper folder from configuration...")
|
||||
|
||||
if _, err := mapper.OpenGitRepo(config.Storage.Git, config.Storage.Location); err != nil {
|
||||
if _, err := mapper.NewRepository(config.Storage.Git, config.Storage.Path); err != nil {
|
||||
errLogger.Printf(pterm.Red(fmt.Sprintf("failed to initialize folder: %v\n", err)))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -59,7 +61,7 @@ var loadCmd = &cobra.Command{
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
el := mapper.NewElement([]mapper.ItemLocation{}, config.Storage.Location)
|
||||
el := mapper.NewElement([]mapper.ItemLocation{}, config.Storage.Path)
|
||||
|
||||
if !viper.GetBool("load-disable-files") {
|
||||
el.AddItems(config.Files)
|
||||
|
@ -88,13 +90,17 @@ var saveCmd = &cobra.Command{
|
|||
saved location based on your configuration file`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var config mapper.Configuration
|
||||
|
||||
if err := viper.Unmarshal(&config); err != nil {
|
||||
errLogger.Printf(pterm.Red(fmt.Sprintf("failed to decode configuration: %v\n", err)))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
el := mapper.NewElement([]mapper.ItemLocation{}, config.Storage.Location)
|
||||
repo, err := mapper.NewRepository(config.Storage.Git, config.Storage.Path)
|
||||
if err != nil {
|
||||
errLogger.Printf(pterm.Red(fmt.Sprintf("failed to open repository at %s: %v\n", config.Storage.Path, err)))
|
||||
os.Exit(1)
|
||||
}
|
||||
el := mapper.NewElement([]mapper.ItemLocation{}, config.Storage.Path)
|
||||
|
||||
if !viper.GetBool("save-disable-files") {
|
||||
el.AddItems(config.Files)
|
||||
|
@ -114,6 +120,18 @@ var saveCmd = &cobra.Command{
|
|||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if viper.GetBool("push") {
|
||||
s, _ := pterm.DefaultSpinner.WithShowTimer(true).WithRemoveWhenDone(false).Start("Pushing changes to remote repository")
|
||||
|
||||
if err := repo.PushChanges(viper.GetString("message")); err != nil {
|
||||
errLogger.Printf(pterm.Red(fmt.Sprintf("failed to push changes to repository: %v\n", err)))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
s.Stop()
|
||||
s.Success("Changes pushed")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -136,9 +154,13 @@ func init() {
|
|||
saveCmd.PersistentFlags().Bool("disable-files", false, "files will be ignored")
|
||||
saveCmd.PersistentFlags().Bool("disable-folders", false, "folders will be ignored")
|
||||
saveCmd.PersistentFlags().Bool("disable-pkgs", false, "package managers will be ignored")
|
||||
saveCmd.Flags().BoolP("push", "p", false, "new configurations will be committed and pushed")
|
||||
saveCmd.Flags().StringP("message", "m", strconv.FormatInt(time.Now().Unix(), 10), "combined with --push to set a commit message")
|
||||
viper.BindPFlag("save-disable-files", saveCmd.PersistentFlags().Lookup("disable-files"))
|
||||
viper.BindPFlag("save-disable-folders", saveCmd.PersistentFlags().Lookup("disable-folders"))
|
||||
viper.BindPFlag("save-disable-pkgs", saveCmd.PersistentFlags().Lookup("disable-pkgs"))
|
||||
viper.BindPFlag("push", saveCmd.Flags().Lookup("push"))
|
||||
viper.BindPFlag("message", saveCmd.Flags().Lookup("message"))
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
|
|
|
@ -23,14 +23,16 @@ type ItemLocation struct {
|
|||
}
|
||||
|
||||
type Storage struct {
|
||||
Location string `mapstructure:"location" yaml:"location"`
|
||||
Git Git `mapstructure:"git" yaml:"git"`
|
||||
Path string `mapstructure:"location" yaml:"location"`
|
||||
Git Git `mapstructure:"git" yaml:"git"`
|
||||
}
|
||||
|
||||
type Git struct {
|
||||
Repository string `mapstructure:"repository" yaml:"repository"`
|
||||
BasicAuth BasicAuth `mapstructure:"basic-auth" yaml:"basic-auth"`
|
||||
SSH Ssh `mapstructure:"ssh" yaml:"ssh"`
|
||||
URL string `mapstructure:"repository" yaml:"repository"`
|
||||
Name string `mapstructure:"name" yaml:"name"`
|
||||
Email string `mapstructure:"email" yaml:"email"`
|
||||
BasicAuth BasicAuth `mapstructure:"basic-auth" yaml:"basic-auth"`
|
||||
SSH Ssh `mapstructure:"ssh" yaml:"ssh"`
|
||||
}
|
||||
|
||||
type BasicAuth struct {
|
||||
|
|
137
internal/git.go
137
internal/git.go
|
@ -3,8 +3,10 @@ package mapper
|
|||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
|
@ -15,56 +17,115 @@ var (
|
|||
ErrInvalidEnv = errors.New("found invalid environment variable in path")
|
||||
)
|
||||
|
||||
func OpenGitRepo(c Git, l string) (*git.Repository, error) {
|
||||
s, err := os.Stat(l)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
var auth transport.AuthMethod
|
||||
type RepositoryActions interface {
|
||||
PushChanges(msg string) error
|
||||
openRepository() error
|
||||
}
|
||||
|
||||
if c.SSH.Passphrase != "" && c.SSH.PrivateKey != "" {
|
||||
privateKey, err := absolutePath(c.SSH.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
type Repository struct {
|
||||
auth transport.AuthMethod
|
||||
repository *git.Repository
|
||||
repoPath string
|
||||
author author
|
||||
url string
|
||||
}
|
||||
|
||||
if _, err := os.Stat(privateKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
type author struct {
|
||||
name string
|
||||
email string
|
||||
}
|
||||
|
||||
auth, err = ssh.NewPublicKeysFromFile("git", privateKey, c.SSH.Passphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
auth = &http.BasicAuth{
|
||||
Username: c.BasicAuth.Username,
|
||||
Password: c.BasicAuth.Password,
|
||||
}
|
||||
}
|
||||
func NewRepository(config Git, repoPath string) (RepositoryActions, error) {
|
||||
var auth transport.AuthMethod
|
||||
|
||||
repo, err := git.PlainClone(l, false, &git.CloneOptions{
|
||||
URL: c.Repository,
|
||||
Progress: os.Stdout,
|
||||
Auth: auth,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return repo, nil
|
||||
if config.SSH.Passphrase != "" && config.SSH.PrivateKey != "" {
|
||||
privateKey, err := absolutePath(config.SSH.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, err
|
||||
if _, err := os.Stat(privateKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
auth, err = ssh.NewPublicKeysFromFile("git", privateKey, config.SSH.Passphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
auth = &http.BasicAuth{
|
||||
Username: config.BasicAuth.Username,
|
||||
Password: config.BasicAuth.Password,
|
||||
}
|
||||
}
|
||||
|
||||
if !s.IsDir() {
|
||||
return nil, ErrDirIsFile
|
||||
repo := &Repository{
|
||||
auth: auth,
|
||||
repository: nil,
|
||||
repoPath: repoPath,
|
||||
author: author{
|
||||
name: config.Name,
|
||||
email: config.Email,
|
||||
},
|
||||
}
|
||||
|
||||
repo, err := git.PlainOpen(l)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
r.repository = repo
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Repository) PushChanges(msg string) error {
|
||||
w, err := r.repository.Worktree()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.Add("."); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Commit(msg, &git.CommitOptions{
|
||||
Author: &object.Signature{
|
||||
Name: r.author.name,
|
||||
Email: r.author.email,
|
||||
When: time.Now(),
|
||||
},
|
||||
})
|
||||
|
||||
return r.repository.Push(&git.PushOptions{})
|
||||
}
|
||||
|
|
Reference in New Issue