Files
ci/internal/git/git.go
2026-06-18 21:19:47 +08:00

101 lines
2.4 KiB
Go

package git
import (
"fmt"
"os"
"path/filepath"
gogit "github.com/go-git/go-git/v5"
"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"
)
// Service handles Git operations for projects.
type Service struct{}
// New creates a new Git Service.
func New() *Service {
return &Service{}
}
// CloneOrPull clones a repo if it doesn't exist locally, or pulls latest changes.
// Returns the commit hash of HEAD after the operation.
func (s *Service) CloneOrPull(url, branch, username, password, dstDir string) (string, error) {
// Check if repo already exists
repo, err := gogit.PlainOpen(dstDir)
if err == nil {
// Repo exists, pull
return s.pull(repo, branch, username, password)
}
// Clone fresh
return s.clone(url, branch, username, password, dstDir)
}
func (s *Service) clone(url, branch, username, password, dstDir string) (string, error) {
// Ensure parent directory exists
os.MkdirAll(filepath.Dir(dstDir), 0755)
// Remove target if it exists (shouldn't, but be safe)
os.RemoveAll(dstDir)
var auth transport.AuthMethod
if username != "" || password != "" {
auth = &http.BasicAuth{
Username: username,
Password: password,
}
}
refName := plumbing.NewBranchReferenceName(branch)
repo, err := gogit.PlainClone(dstDir, false, &gogit.CloneOptions{
URL: url,
Auth: auth,
ReferenceName: refName,
SingleBranch: true,
Depth: 1,
Progress: nil,
})
if err != nil {
return "", fmt.Errorf("clone failed: %w", err)
}
return getHeadHash(repo)
}
func (s *Service) pull(repo *gogit.Repository, branch, username, password string) (string, error) {
w, err := repo.Worktree()
if err != nil {
return "", fmt.Errorf("worktree: %w", err)
}
var auth transport.AuthMethod
if username != "" || password != "" {
auth = &http.BasicAuth{
Username: username,
Password: password,
}
}
refName := plumbing.NewBranchReferenceName(branch)
err = w.Pull(&gogit.PullOptions{
RemoteName: "origin",
Auth: auth,
ReferenceName: refName,
SingleBranch: true,
})
if err != nil && err != gogit.NoErrAlreadyUpToDate {
return "", fmt.Errorf("pull failed: %w", err)
}
return getHeadHash(repo)
}
func getHeadHash(repo *gogit.Repository) (string, error) {
ref, err := repo.Head()
if err != nil {
return "", err
}
return ref.Hash().String(), nil
}