215 lines
4.7 KiB
Go
215 lines
4.7 KiB
Go
//go:build gitgo
|
|
// +build gitgo
|
|
|
|
package sync
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
|
|
"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/transport/ssh"
|
|
)
|
|
|
|
// GitImporter implements an Importer, where files to imports are located
|
|
// inside a local directory from your filesystem, backed by git.
|
|
type GitImporter struct {
|
|
li LocalImporter
|
|
Remote string
|
|
Branch string
|
|
Auth ssh.AuthMethod
|
|
}
|
|
|
|
func NewGitImporter(li LocalImporter, remote string, branch string) GitImporter {
|
|
var auth ssh.AuthMethod
|
|
|
|
// If there is no ssh-agent setup, try to use a default ssh key
|
|
if _, exists := os.LookupEnv("SSH_AUTH_SOCK"); !exists {
|
|
if home, exists := os.LookupEnv("HOME"); exists {
|
|
for _, d := range []string{"id_fic", "id_ed25519", "id_rsa"} {
|
|
pemBytes, err := ioutil.ReadFile(path.Join(home, ".ssh", d))
|
|
if err == nil {
|
|
log.Println("[GitImporter] Using", path.Join(home, ".ssh", d), "as ssh key to sync repository")
|
|
auth, err = ssh.NewPublicKeys("git", pemBytes, "")
|
|
if ccfg, err := auth.ClientConfig(); err != nil {
|
|
if hkc, err := ssh.NewKnownHostsCallback(); err != nil {
|
|
ccfg.HostKeyCallback = hkc
|
|
}
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return GitImporter{
|
|
li: li,
|
|
Remote: remote,
|
|
Branch: branch,
|
|
Auth: auth,
|
|
}
|
|
}
|
|
|
|
func (i GitImporter) Id() *string {
|
|
var gitinfo string
|
|
r, err := git.PlainOpen(i.li.Base)
|
|
if err == nil {
|
|
ref, err := r.Head()
|
|
if err == nil {
|
|
gitinfo = ref.Hash().String()
|
|
}
|
|
}
|
|
|
|
return &gitinfo
|
|
}
|
|
|
|
func (i GitImporter) Init() error {
|
|
// Check if the directory exists, create it if needed
|
|
if err := i.li.Init(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// If the directory is empty, clone it
|
|
if n, err := countFileInDir(i.li.Base); err != nil {
|
|
return err
|
|
} else if n == 0 {
|
|
_, err = git.PlainClone(i.li.Base, false, &git.CloneOptions{
|
|
URL: i.Remote,
|
|
ReferenceName: plumbing.ReferenceName(i.Branch),
|
|
RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
|
|
Auth: i.Auth,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Check if the .git directory exists, change the origin remote to our
|
|
r, err := git.PlainOpen(i.li.Base)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r.DeleteRemote("origin")
|
|
_, err = r.CreateRemote(&config.RemoteConfig{
|
|
Name: "origin",
|
|
URLs: []string{i.Remote},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (i GitImporter) Sync() error {
|
|
oneGitPull.Lock()
|
|
defer oneGitPull.Unlock()
|
|
|
|
r, err := git.PlainOpen(i.li.Base)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
w, err := r.Worktree()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Perform a git pull --rebase origin/master
|
|
err = w.Pull(&git.PullOptions{
|
|
RemoteName: "origin",
|
|
ReferenceName: plumbing.ReferenceName(i.Branch),
|
|
Depth: 1,
|
|
RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
|
|
Force: true,
|
|
Auth: i.Auth,
|
|
})
|
|
return err
|
|
}
|
|
|
|
func (i GitImporter) GetSubmodules() ([]GitSubmoduleStatus, error) {
|
|
oneGitPull.Lock()
|
|
defer oneGitPull.Unlock()
|
|
|
|
r, err := git.PlainOpen(i.li.Base)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
w, err := r.Worktree()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
modules, err := w.Submodules()
|
|
|
|
var ret []GitSubmoduleStatus
|
|
for _, mod := range modules {
|
|
st, err := mod.Status()
|
|
if err == nil {
|
|
ret = append(ret, GitSubmoduleStatus{
|
|
Hash: st.Expected.String(),
|
|
Path: st.Path,
|
|
Branch: strings.TrimSuffix(strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(st.Branch.String(), "("), "refs/"), "heads/"), ")"),
|
|
})
|
|
}
|
|
}
|
|
|
|
return ret, err
|
|
}
|
|
|
|
func (i GitImporter) GetSubmodule(repopath string) (*GitSubmoduleStatus, error) {
|
|
oneGitPull.Lock()
|
|
defer oneGitPull.Unlock()
|
|
|
|
r, err := git.PlainOpen(path.Join(i.li.Base, repopath))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
st, err := r.Head()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &GitSubmoduleStatus{
|
|
Hash: st.Hash().String(),
|
|
Path: repopath,
|
|
Branch: st.Name().Short(),
|
|
}, nil
|
|
}
|
|
|
|
func (i GitImporter) IsRepositoryUptodate(repopath string) (*GitSubmoduleStatus, error) {
|
|
oneGitPull.Lock()
|
|
defer oneGitPull.Unlock()
|
|
|
|
r, err := git.PlainOpen(path.Join(i.li.Base, repopath))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Perform a git pull --rebase origin/master
|
|
err = r.Fetch(&git.FetchOptions{
|
|
RemoteName: "origin",
|
|
RefSpecs: []config.RefSpec{config.RefSpec("+refs/heads/" + i.Branch + ":refs/remotes/origin/" + i.Branch)},
|
|
Auth: i.Auth,
|
|
})
|
|
|
|
st, err := r.Reference(plumbing.ReferenceName("origin/"+i.Branch), true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &GitSubmoduleStatus{
|
|
Hash: st.Hash().String(),
|
|
Path: repopath,
|
|
Branch: st.Name().Short(),
|
|
}, nil
|
|
}
|