summaryrefslogtreecommitdiffstats
path: root/modules/repository/repo.go
diff options
context:
space:
mode:
authorKN4CK3R <KN4CK3R@users.noreply.github.com>2021-04-09 00:25:57 +0200
committerGitHub <noreply@github.com>2021-04-08 18:25:57 -0400
commitc03e488e14fdaf1c0056952f40c5fc8124719a30 (patch)
tree22338add91196fad9f40f9a74033525ad8f591eb /modules/repository/repo.go
parentf544414a232c148d4baf2e9d807f6cbffed67928 (diff)
downloadgitea-c03e488e14fdaf1c0056952f40c5fc8124719a30.tar.gz
gitea-c03e488e14fdaf1c0056952f40c5fc8124719a30.zip
Add LFS Migration and Mirror (#14726)
* Implemented LFS client. * Implemented scanning for pointer files. * Implemented downloading of lfs files. * Moved model-dependent code into services. * Removed models dependency. Added TryReadPointerFromBuffer. * Migrated code from service to module. * Centralised storage creation. * Removed dependency from models. * Moved ContentStore into modules. * Share structs between server and client. * Moved method to services. * Implemented lfs download on clone. * Implemented LFS sync on clone and mirror update. * Added form fields. * Updated templates. * Fixed condition. * Use alternate endpoint. * Added missing methods. * Fixed typo and make linter happy. * Detached pointer parser from gogit dependency. * Fixed TestGetLFSRange test. * Added context to support cancellation. * Use ReadFull to probably read more data. * Removed duplicated code from models. * Moved scan implementation into pointer_scanner_nogogit. * Changed method name. * Added comments. * Added more/specific log/error messages. * Embedded lfs.Pointer into models.LFSMetaObject. * Moved code from models to module. * Moved code from models to module. * Moved code from models to module. * Reduced pointer usage. * Embedded type. * Use promoted fields. * Fixed unexpected eof. * Added unit tests. * Implemented migration of local file paths. * Show an error on invalid LFS endpoints. * Hide settings if not used. * Added LFS info to mirror struct. * Fixed comment. * Check LFS endpoint. * Manage LFS settings from mirror page. * Fixed selector. * Adjusted selector. * Added more tests. * Added local filesystem migration test. * Fixed typo. * Reset settings. * Added special windows path handling. * Added unit test for HTTPClient. * Added unit test for BasicTransferAdapter. * Moved into util package. * Test if LFS endpoint is allowed. * Added support for git:// * Just use a static placeholder as the displayed url may be invalid. * Reverted to original code. * Added "Advanced Settings". * Updated wording. * Added discovery info link. * Implemented suggestion. * Fixed missing format parameter. * Added Pointer.IsValid(). * Always remove model on error. * Added suggestions. * Use channel instead of array. * Update routers/repo/migrate.go * fmt Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: zeripath <art27@cantab.net>
Diffstat (limited to 'modules/repository/repo.go')
-rw-r--r--modules/repository/repo.go86
1 files changed, 86 insertions, 0 deletions
diff --git a/modules/repository/repo.go b/modules/repository/repo.go
index ede714673a..50eb185daa 100644
--- a/modules/repository/repo.go
+++ b/modules/repository/repo.go
@@ -7,12 +7,14 @@ package repository
import (
"context"
"fmt"
+ "net/url"
"path"
"strings"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
migration "code.gitea.io/gitea/modules/migrations/base"
"code.gitea.io/gitea/modules/setting"
@@ -120,6 +122,13 @@ func MigrateRepositoryGitData(ctx context.Context, u *models.User, repo *models.
log.Error("Failed to synchronize tags to releases for repository: %v", err)
}
}
+
+ if opts.LFS {
+ ep := lfs.DetermineEndpoint(opts.CloneAddr, opts.LFSEndpoint)
+ if err = StoreMissingLfsObjectsInRepository(ctx, repo, gitRepo, ep); err != nil {
+ log.Error("Failed to store missing LFS objects for repository: %v", err)
+ }
+ }
}
if err = repo.UpdateSize(models.DefaultDBContext()); err != nil {
@@ -132,6 +141,10 @@ func MigrateRepositoryGitData(ctx context.Context, u *models.User, repo *models.
Interval: setting.Mirror.DefaultInterval,
EnablePrune: true,
NextUpdateUnix: timeutil.TimeStampNow().AddDuration(setting.Mirror.DefaultInterval),
+ LFS: opts.LFS,
+ }
+ if opts.LFS {
+ mirrorModel.LFSEndpoint = opts.LFSEndpoint
}
if opts.MirrorInterval != "" {
@@ -300,3 +313,76 @@ func PushUpdateAddTag(repo *models.Repository, gitRepo *git.Repository, tagName
return models.SaveOrUpdateTag(repo, &rel)
}
+
+// StoreMissingLfsObjectsInRepository downloads missing LFS objects
+func StoreMissingLfsObjectsInRepository(ctx context.Context, repo *models.Repository, gitRepo *git.Repository, endpoint *url.URL) error {
+ client := lfs.NewClient(endpoint)
+ contentStore := lfs.NewContentStore()
+
+ pointerChan := make(chan lfs.PointerBlob)
+ errChan := make(chan error, 1)
+ go lfs.SearchPointerBlobs(ctx, gitRepo, pointerChan, errChan)
+
+ err := func() error {
+ for pointerBlob := range pointerChan {
+ meta, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: pointerBlob.Pointer, RepositoryID: repo.ID})
+ if err != nil {
+ return fmt.Errorf("StoreMissingLfsObjectsInRepository models.NewLFSMetaObject: %w", err)
+ }
+ if meta.Existing {
+ continue
+ }
+
+ log.Trace("StoreMissingLfsObjectsInRepository: LFS OID[%s] not present in repository %s", pointerBlob.Oid, repo.FullName())
+
+ err = func() error {
+ exist, err := contentStore.Exists(pointerBlob.Pointer)
+ if err != nil {
+ return fmt.Errorf("StoreMissingLfsObjectsInRepository contentStore.Exists: %w", err)
+ }
+ if !exist {
+ if setting.LFS.MaxFileSize > 0 && pointerBlob.Size > setting.LFS.MaxFileSize {
+ log.Info("LFS OID[%s] download denied because of LFS_MAX_FILE_SIZE=%d < size %d", pointerBlob.Oid, setting.LFS.MaxFileSize, pointerBlob.Size)
+ return nil
+ }
+
+ stream, err := client.Download(ctx, pointerBlob.Oid, pointerBlob.Size)
+ if err != nil {
+ return fmt.Errorf("StoreMissingLfsObjectsInRepository: LFS OID[%s] failed to download: %w", pointerBlob.Oid, err)
+ }
+ defer stream.Close()
+
+ if err := contentStore.Put(pointerBlob.Pointer, stream); err != nil {
+ return fmt.Errorf("StoreMissingLfsObjectsInRepository LFS OID[%s] contentStore.Put: %w", pointerBlob.Oid, err)
+ }
+ } else {
+ log.Trace("StoreMissingLfsObjectsInRepository: LFS OID[%s] already present in content store", pointerBlob.Oid)
+ }
+ return nil
+ }()
+ if err != nil {
+ if _, err2 := repo.RemoveLFSMetaObjectByOid(meta.Oid); err2 != nil {
+ log.Error("StoreMissingLfsObjectsInRepository RemoveLFSMetaObjectByOid[Oid: %s]: %w", meta.Oid, err2)
+ }
+
+ select {
+ case <-ctx.Done():
+ return nil
+ default:
+ }
+ return err
+ }
+ }
+ return nil
+ }()
+ if err != nil {
+ return err
+ }
+
+ err, has := <-errChan
+ if has {
+ return err
+ }
+
+ return nil
+}