110 lines
2.7 KiB
Go
110 lines
2.7 KiB
Go
package repo
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"os/exec"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"skillmgr/internal/config"
|
||
)
|
||
|
||
// URLToPathName 将 URL 转换为缓存目录名
|
||
// 例如: https://github.com/user/repo.git -> github.com_user_repo
|
||
func URLToPathName(url string) string {
|
||
clean := strings.TrimPrefix(url, "https://")
|
||
clean = strings.TrimPrefix(clean, "http://")
|
||
clean = strings.TrimSuffix(clean, ".git")
|
||
clean = strings.ReplaceAll(clean, "/", "_")
|
||
return clean
|
||
}
|
||
|
||
// CloneOrPull 克隆或更新仓库
|
||
// 如果仓库不存在则 clone,存在则 pull
|
||
func CloneOrPull(url, branch string) (string, error) {
|
||
cachePath, err := config.GetCachePath()
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
repoPath := filepath.Join(cachePath, URLToPathName(url))
|
||
|
||
// 检查是否已存在
|
||
if _, err := os.Stat(filepath.Join(repoPath, ".git")); err == nil {
|
||
// 已存在,执行 pull
|
||
return repoPath, pullRepo(repoPath, branch)
|
||
}
|
||
|
||
// 不存在,执行 clone
|
||
return repoPath, cloneRepo(url, branch, repoPath)
|
||
}
|
||
|
||
// cloneRepo 克隆仓库
|
||
func cloneRepo(url, branch, dest string) error {
|
||
args := []string{"clone", "--depth", "1"}
|
||
if branch != "" {
|
||
args = append(args, "--branch", branch)
|
||
}
|
||
args = append(args, url, dest)
|
||
|
||
cmd := exec.Command("git", args...)
|
||
output, err := cmd.CombinedOutput()
|
||
if err != nil {
|
||
return fmt.Errorf("git clone 失败: %w\n%s", err, output)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// pullRepo 更新仓库
|
||
func pullRepo(path, branch string) error {
|
||
// 先 fetch
|
||
fetchCmd := exec.Command("git", "-C", path, "fetch", "origin")
|
||
if output, err := fetchCmd.CombinedOutput(); err != nil {
|
||
return fmt.Errorf("git fetch 失败: %w\n%s", err, output)
|
||
}
|
||
|
||
// 然后 pull
|
||
pullArgs := []string{"-C", path, "pull", "origin"}
|
||
if branch != "" {
|
||
pullArgs = append(pullArgs, branch)
|
||
}
|
||
pullCmd := exec.Command("git", pullArgs...)
|
||
output, err := pullCmd.CombinedOutput()
|
||
if err != nil {
|
||
return fmt.Errorf("git pull 失败: %w\n%s", err, output)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// GetRepoPath 获取仓库缓存路径
|
||
func GetRepoPath(url string) (string, error) {
|
||
cachePath, err := config.GetCachePath()
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return filepath.Join(cachePath, URLToPathName(url)), nil
|
||
}
|
||
|
||
// CloneTemporary 克隆临时仓库到临时目录
|
||
// 返回临时目录路径和清理函数
|
||
func CloneTemporary(url, branch string) (repoPath string, cleanup func(), err error) {
|
||
// 创建临时目录
|
||
tmpDir, err := os.MkdirTemp("", "skillmgr-temp-*")
|
||
if err != nil {
|
||
return "", nil, fmt.Errorf("创建临时目录失败: %w", err)
|
||
}
|
||
|
||
cleanup = func() {
|
||
os.RemoveAll(tmpDir)
|
||
}
|
||
|
||
// 克隆到临时目录
|
||
if err := cloneRepo(url, branch, tmpDir); err != nil {
|
||
cleanup()
|
||
return "", nil, err
|
||
}
|
||
|
||
return tmpDir, cleanup, nil
|
||
}
|