Semaphore/db_lib/GoGitClient.go

275 lines
5.8 KiB
Go
Raw Normal View History

2023-09-23 17:12:35 +02:00
package db_lib
2023-02-26 07:22:47 +01:00
import (
"errors"
"github.com/semaphoreui/semaphore/db"
"github.com/semaphoreui/semaphore/pkg/task_logger"
"github.com/semaphoreui/semaphore/util"
2023-02-26 07:22:47 +01:00
"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"
"github.com/go-git/go-git/v5/plumbing/transport/http"
2023-02-26 07:22:47 +01:00
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
"github.com/go-git/go-git/v5/storage/memory"
ssh2 "golang.org/x/crypto/ssh"
2023-02-26 07:22:47 +01:00
)
type GoGitClient struct{}
2023-03-10 20:42:01 +01:00
type ProgressWrapper struct {
Logger task_logger.Logger
2023-02-26 07:22:47 +01:00
}
2023-03-10 20:42:01 +01:00
func (t ProgressWrapper) Write(p []byte) (n int, err error) {
2023-02-26 07:22:47 +01:00
t.Logger.Log(string(p))
return len(p), nil
}
2023-02-26 10:27:11 +01:00
func getAuthMethod(r GitRepository) (transport.AuthMethod, error) {
2023-02-26 07:22:47 +01:00
if r.Repository.SSHKey.Type == db.AccessKeySSH {
var sshKeyBuff = r.Repository.SSHKey.SshKey.PrivateKey
2023-02-26 07:22:47 +01:00
if r.Repository.SSHKey.SshKey.Login == "" {
2023-04-26 16:33:30 +02:00
r.Repository.SSHKey.SshKey.Login = "git"
}
publicKey, sshErr := ssh.NewPublicKeys(r.Repository.SSHKey.SshKey.Login, []byte(sshKeyBuff), r.Repository.SSHKey.SshKey.Passphrase)
if sshErr != nil {
2023-04-26 16:33:30 +02:00
r.Logger.Log("Unable to creating ssh auth method")
return nil, sshErr
}
publicKey.HostKeyCallback = ssh2.InsecureIgnoreHostKey()
return publicKey, sshErr
2023-02-26 07:22:47 +01:00
} else if r.Repository.SSHKey.Type == db.AccessKeyLoginPassword {
password := &http.BasicAuth{
2023-04-26 16:33:30 +02:00
Username: r.Repository.SSHKey.LoginPassword.Login,
Password: r.Repository.SSHKey.LoginPassword.Password,
}
2023-02-26 07:22:47 +01:00
return password, nil
2023-02-26 07:22:47 +01:00
} else if r.Repository.SSHKey.Type == db.AccessKeyNone {
2023-04-26 16:33:30 +02:00
return nil, nil
2023-02-26 07:22:47 +01:00
} else {
2023-09-23 17:12:35 +02:00
return nil, errors.New("unsupported auth method")
2023-02-26 07:22:47 +01:00
}
}
2023-02-26 10:27:11 +01:00
func openRepository(r GitRepository, targetDir GitRepositoryDirType) (*git.Repository, error) {
2023-02-26 07:22:47 +01:00
var dir string
switch targetDir {
case GitRepositoryTmpPath:
2023-02-26 07:22:47 +01:00
dir = util.Config.TmpPath
case GitRepositoryFullPath:
2023-02-26 07:22:47 +01:00
dir = r.GetFullPath()
default:
panic("unknown Repository directory type")
}
return git.PlainOpen(dir)
}
func (c GoGitClient) Clone(r GitRepository) error {
r.Logger.Log("Cloning Repository " + r.Repository.GitURL)
2023-02-26 10:27:11 +01:00
authMethod, authErr := getAuthMethod(r)
2023-02-26 07:22:47 +01:00
if authErr != nil {
return authErr
}
cloneOpt := &git.CloneOptions{
URL: r.Repository.GetGitURL(),
2023-03-10 20:42:01 +01:00
Progress: ProgressWrapper{r.Logger},
2023-02-26 07:22:47 +01:00
RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
ReferenceName: plumbing.NewBranchReferenceName(r.Repository.GitBranch),
Auth: authMethod,
}
_, err := git.PlainClone(r.GetFullPath(), false, cloneOpt)
if err != nil {
r.Logger.Log("Unable to clone repository: " + err.Error())
}
return err
}
func (c GoGitClient) Pull(r GitRepository) error {
r.Logger.Log("Updating Repository " + r.Repository.GitURL)
rep, err := openRepository(r, GitRepositoryFullPath)
2023-02-26 07:22:47 +01:00
if err != nil {
return err
}
wt, err := rep.Worktree()
if err != nil {
return err
}
2023-02-26 10:27:11 +01:00
authMethod, authErr := getAuthMethod(r)
if authErr != nil {
return authErr
}
2023-02-26 07:22:47 +01:00
// Pull the latest changes from the origin remote and merge into the current branch
2023-10-26 13:22:31 +02:00
err = wt.Pull(&git.PullOptions{RemoteName: "origin",
Auth: authMethod,
RecurseSubmodules: git.DefaultSubmoduleRecursionDepth})
2023-02-26 10:27:11 +01:00
if err != nil && err != git.NoErrAlreadyUpToDate {
2023-02-26 07:22:47 +01:00
r.Logger.Log("Unable to pull latest changes")
return err
}
return nil
}
func (c GoGitClient) Checkout(r GitRepository, target string) error {
r.Logger.Log("Checkout repository to " + target)
rep, err := openRepository(r, GitRepositoryFullPath)
2023-02-26 07:22:47 +01:00
if err != nil {
return err
}
wt, err := rep.Worktree()
if err != nil {
return err
}
err = wt.Checkout(&git.CheckoutOptions{
Hash: plumbing.NewHash(target),
})
return err
}
func (c GoGitClient) CanBePulled(r GitRepository) bool {
rep, err := openRepository(r, GitRepositoryFullPath)
2023-02-26 07:22:47 +01:00
if err != nil {
return false
}
2023-02-26 10:27:11 +01:00
authMethod, err := getAuthMethod(r)
2023-02-26 07:22:47 +01:00
if err != nil {
return false
}
err = rep.Fetch(&git.FetchOptions{
Auth: authMethod,
})
if err != nil && err != git.NoErrAlreadyUpToDate {
return false
}
2023-02-26 07:22:47 +01:00
head, err := rep.Head()
if err != nil {
return false
}
headCommit, err := rep.CommitObject(head.Hash())
if err != nil {
return false
}
hash, err := rep.ResolveRevision(plumbing.Revision("origin/" + r.Repository.GitBranch))
if err != nil {
return false
}
lastCommit, err := rep.CommitObject(*hash)
if err != nil {
return false
}
isAncestor, err := headCommit.IsAncestor(lastCommit)
return isAncestor && err == nil
}
func (c GoGitClient) GetLastCommitMessage(r GitRepository) (msg string, err error) {
r.Logger.Log("Get current commit message")
rep, err := openRepository(r, GitRepositoryFullPath)
2023-02-26 07:22:47 +01:00
if err != nil {
return
}
headRef, err := rep.Head()
if err != nil {
return
}
headCommit, err := rep.CommitObject(headRef.Hash())
if err != nil {
return
}
2023-02-26 09:36:46 +01:00
msg = headCommit.Message
2023-02-26 07:22:47 +01:00
if len(msg) > 100 {
msg = msg[0:100]
}
2023-02-26 10:27:11 +01:00
r.Logger.Log("Message: " + msg)
2023-02-26 07:22:47 +01:00
return
}
func (c GoGitClient) GetLastCommitHash(r GitRepository) (hash string, err error) {
r.Logger.Log("Get current commit hash")
rep, err := openRepository(r, GitRepositoryFullPath)
2023-02-26 07:22:47 +01:00
if err != nil {
return
}
headRef, err := rep.Head()
if err != nil {
return
}
hash = headRef.Hash().String()
return
}
func (c GoGitClient) GetLastRemoteCommitHash(r GitRepository) (hash string, err error) {
rem := git.NewRemote(memory.NewStorage(), &config.RemoteConfig{
Name: "origin",
URLs: []string{r.Repository.GitURL},
})
2023-02-26 10:27:11 +01:00
auth, err := getAuthMethod(r)
2023-02-26 07:22:47 +01:00
if err != nil {
return
}
refs, err := rem.List(&git.ListOptions{
Auth: auth,
})
if err != nil {
return
}
var lastRemoteRef *plumbing.Reference
for _, rf := range refs {
if rf.Name().Short() == r.Repository.GitBranch {
lastRemoteRef = rf
}
}
if lastRemoteRef != nil {
hash = lastRemoteRef.Hash().String()
}
return
}