package git import ( "context" "fmt" "os" "os/signal" "strings" "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 { ctx := context.Background() ctx, cancelCtx := context.WithCancel(ctx) sigIntCh := make(chan os.Signal, 1) signal.Notify(sigIntCh, os.Interrupt) defer func() { signal.Stop(sigIntCh) cancelCtx() }() errCh := make(chan error) go func() { if _, err := os.Stat(dir); os.IsNotExist(err) { log.Debugf("git clone: %s", url) _, err := git.PlainCloneContext(ctx, 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 := ctx.Err(); err != nil { errCh <- err } if err != nil { log.Debug("git clone: main branch failed, attempting master branch") _, err := git.PlainCloneContext(ctx, 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) } errCh <- nil }() select { case <-sigIntCh: cancelCtx() 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", dir, err) } return fmt.Errorf("git clone %s: cancelled due to interrupt", dir) case err := <-errCh: return err } return nil }