diff options
author | KN4CK3R <KN4CK3R@users.noreply.github.com> | 2021-04-09 00:25:57 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-08 18:25:57 -0400 |
commit | c03e488e14fdaf1c0056952f40c5fc8124719a30 (patch) | |
tree | 22338add91196fad9f40f9a74033525ad8f591eb /modules/repository/repo.go | |
parent | f544414a232c148d4baf2e9d807f6cbffed67928 (diff) | |
download | gitea-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.go | 86 |
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 +} |