package providers import ( "context" "time" "git.front.kjuulh.io/kjuulh/kraken/internal/services/storage" "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" "go.uber.org/zap" "go.uber.org/zap/zapio" ) // Git is a native git provider, it can clone, pull // , push and as in abstraction on native git operations type Git struct { logger *zap.Logger gitConfig *GitConfig } type GitRepo struct { repo *git.Repository } type GitAuth string const ( GIT_AUTH_SSH GitAuth = "ssh" GIT_AUTH_USERNAME_PASSWORD GitAuth = "username_password" GIT_AUTH_ACCESS_TOKEN GitAuth = "access_token" GIT_AUTH_ANONYMOUS GitAuth = "anonymous" GIT_AUTH_SSH_AGENT GitAuth = "ssh_agent" ) type GitConfig struct { AuthOption GitAuth User string Password string AccessToken string SshPublicKeyFilePath string SshPrivateKeyPassword string } func NewGit(logger *zap.Logger, gitConfig *GitConfig) *Git { return &Git{logger: logger, gitConfig: gitConfig} } func (g *Git) Clone(ctx context.Context, storageArea *storage.Area, repoUrl string) (*GitRepo, error) { g.logger.Debug( "cloning repository", zap.String("repoUrl", repoUrl), zap.String("path", storageArea.Path), ) auth, err := g.GetAuth() if err != nil { return nil, err } cloneOptions := git.CloneOptions{ URL: repoUrl, Auth: auth, RemoteName: "origin", ReferenceName: "refs/heads/main", SingleBranch: true, NoCheckout: false, Depth: 1, RecurseSubmodules: 1, Progress: &zapio.Writer{ Log: g.logger.With(zap.String("process", "go-git")), Level: zap.DebugLevel, }, Tags: 0, InsecureSkipTLS: false, CABundle: []byte{}, } repo, err := git.PlainCloneContext(ctx, storageArea.Path, false, &cloneOptions) if err != nil { return nil, err } g.logger.Debug("done cloning repo") return &GitRepo{repo: repo}, nil } func (g *Git) Add(ctx context.Context, storageArea *storage.Area, gitRepo *GitRepo) (*git.Worktree, error) { worktree, err := gitRepo.repo.Worktree() if err != nil { return nil, err } err = worktree.AddWithOptions(&git.AddOptions{ All: true, }) if err != nil { return nil, err } status, err := worktree.Status() if err != nil { return nil, err } g.logger.Info("git status", zap.String("status", status.String())) return worktree, nil } func (g *Git) Commit(ctx context.Context, gitRepo *GitRepo) error { worktree, err := gitRepo.repo.Worktree() if err != nil { return err } _, err = worktree.Commit("some-commit", &git.CommitOptions{ All: true, Author: &object.Signature{ Name: "kraken", Email: "kraken@kasperhermansen.com", When: time.Now(), }, Committer: &object.Signature{ Name: "kraken", Email: "kraken@kasperhermansen.com", When: time.Now(), }, }) if err != nil { return err } return nil } func (g *Git) GetAuth() (transport.AuthMethod, error) { switch g.gitConfig.AuthOption { case GIT_AUTH_SSH: sshKey, err := ssh.NewPublicKeysFromFile( g.gitConfig.User, g.gitConfig.SshPublicKeyFilePath, g.gitConfig.SshPrivateKeyPassword, ) if err != nil { return nil, err } return sshKey, nil case GIT_AUTH_USERNAME_PASSWORD: return &http.BasicAuth{ Username: g.gitConfig.User, Password: g.gitConfig.Password, }, nil case GIT_AUTH_ACCESS_TOKEN: return &http.BasicAuth{ Username: "required-username", Password: g.gitConfig.AccessToken, }, nil case GIT_AUTH_ANONYMOUS: return nil, nil case GIT_AUTH_SSH_AGENT: return ssh.NewSSHAgentAuth(g.gitConfig.User) default: return nil, nil } }