1package backend23import (4 "context"5 "errors"6 "io"7 "path"8 "path/filepath"9 "strconv"1011 "github.com/charmbracelet/soft-serve/pkg/config"12 "github.com/charmbracelet/soft-serve/pkg/db"13 "github.com/charmbracelet/soft-serve/pkg/lfs"14 "github.com/charmbracelet/soft-serve/pkg/proto"15 "github.com/charmbracelet/soft-serve/pkg/storage"16 "github.com/charmbracelet/soft-serve/pkg/store"17)1819// StoreRepoMissingLFSObjects stores missing LFS objects for a repository.20func StoreRepoMissingLFSObjects(ctx context.Context, repo proto.Repository, dbx *db.DB, store store.Store, lfsClient lfs.Client) error {21 cfg := config.FromContext(ctx)22 repoID := strconv.FormatInt(repo.ID(), 10)23 lfsRoot := filepath.Join(cfg.DataPath, "lfs", repoID)2425 // TODO: support S3 storage26 strg := storage.NewLocalStorage(lfsRoot)27 pointerChan := make(chan lfs.PointerBlob)28 errChan := make(chan error, 1)29 r, err := repo.Open()30 if err != nil {31 return err32 }3334 go lfs.SearchPointerBlobs(ctx, r, pointerChan, errChan)3536 download := func(pointers []lfs.Pointer) error {37 return lfsClient.Download(ctx, pointers, func(p lfs.Pointer, content io.ReadCloser, objectError error) error {38 if objectError != nil {39 return objectError40 }4142 defer content.Close() // nolint: errcheck43 return dbx.TransactionContext(ctx, func(tx *db.Tx) error {44 if err := store.CreateLFSObject(ctx, tx, repo.ID(), p.Oid, p.Size); err != nil {45 return db.WrapError(err)46 }4748 _, err := strg.Put(path.Join("objects", p.RelativePath()), content)49 return err50 })51 })52 }5354 var batch []lfs.Pointer55 for pointer := range pointerChan {56 obj, err := store.GetLFSObjectByOid(ctx, dbx, repo.ID(), pointer.Oid)57 if err != nil && !errors.Is(err, db.ErrRecordNotFound) {58 return db.WrapError(err)59 }6061 exist, err := strg.Exists(path.Join("objects", pointer.RelativePath()))62 if err != nil {63 return err64 }6566 if exist && obj.ID == 0 {67 if err := store.CreateLFSObject(ctx, dbx, repo.ID(), pointer.Oid, pointer.Size); err != nil {68 return db.WrapError(err)69 }70 } else {71 batch = append(batch, pointer.Pointer)72 // Limit batch requests to 20 objects73 if len(batch) >= 20 {74 if err := download(batch); err != nil {75 return err76 }7778 batch = nil79 }80 }81 }8283 if err, ok := <-errChan; ok {84 return err85 }8687 return nil88}