package git import ( "fmt" "os" "os/signal" "strings" "syscall" "coopcloud.tech/abra/pkg/log" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" ) // gitCloneIgnoreErr checks whether we can ignore a git clone error or not. func gitCloneIgnoreErr(err error) bool { if strings.Contains(err.Error(), "authentication required") { return true } if strings.Contains(err.Error(), "remote repository is empty") { return true } return false } // Clone runs a git clone which accounts for different default branches. func Clone(dir, url string) error { sigIntCh := make(chan os.Signal) signal.Notify(sigIntCh, os.Interrupt, syscall.SIGTERM) defer signal.Stop(sigIntCh) errCh := make(chan error) go func() { if _, err := os.Stat(dir); os.IsNotExist(err) { log.Debugf("git clone: %s", url) _, err := git.PlainClone(dir, false, &git.CloneOptions{ URL: url, Tags: git.AllTags, ReferenceName: plumbing.ReferenceName("refs/heads/main"), SingleBranch: true, }) if err != nil && gitCloneIgnoreErr(err) { log.Debugf("git clone: %s cloned successfully", dir) errCh <- nil } if err != nil { log.Debug("git clone: main branch failed, attempting master branch") _, err := git.PlainClone(dir, false, &git.CloneOptions{ URL: url, Tags: git.AllTags, ReferenceName: plumbing.ReferenceName("refs/heads/master"), SingleBranch: true, }) if err != nil && gitCloneIgnoreErr(err) { log.Debugf("git clone: %s cloned successfully", dir) errCh <- nil } if err != nil { errCh <- err } } log.Debugf("git clone: %s cloned successfully", dir) } else { log.Debugf("git clone: %s already exists", dir) } }() select { case <-sigIntCh: fmt.Println() // NOTE(d1): newline after ^C if err := os.RemoveAll(dir); err != nil { return fmt.Errorf("unable to clean up git clone of %s: %s", url, err) } return fmt.Errorf("git clone %s: cancelled due to interrupt", url) case err := <-errCh: return err } return nil }