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 }