File opensuse-lfs-associate-on-download-or-fallback-to-obs.patch of Package gitea
From f3052465ce08345c338e69e959286301069c25c4 Mon Sep 17 00:00:00 2001
From: Elisei Roca <eroca@suse.de>
Date: Tue, 25 Nov 2025 20:44:09 +0100
Subject: [PATCH] Associate on download or redirect old LFS oids to
api.opensuse.org
Partially specific to src.opensuse.org deployment where we want to avoid
copying all the old tar revisions from OBS to git LFS. So if the LFS OID is
not on the gitea store, we check the imported historic packages and redirect
there.
Co-authored-by: Adam Majer <adamm@zombino.com>
Co-authored-by: Stephan Kulow <coolo@suse.de>
---
models/git/lfs.go | 2 +-
modules/setting/lfs.go | 1 +
routers/web/repo/view.go | 4 +++-
services/lfs/server.go | 50 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/models/git/lfs.go b/models/git/lfs.go
index 8bba060ff9..2f2f13254e 100644
--- a/models/git/lfs.go
+++ b/models/git/lfs.go
@@ -223,7 +223,7 @@ func CountLFSMetaObjects(ctx context.Context, repoID int64) (int64, error) {
// LFSObjectAccessible checks if a provided Oid is accessible to the user
func LFSObjectAccessible(ctx context.Context, user *user_model.User, oid string) (bool, error) {
- if user.IsAdmin {
+ if user != nil && user.IsAdmin {
count, err := db.GetEngine(ctx).Count(&LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}})
return count > 0, err
}
diff --git a/modules/setting/lfs.go b/modules/setting/lfs.go
index 7f2d0ae159..11a17e452f 100644
--- a/modules/setting/lfs.go
+++ b/modules/setting/lfs.go
@@ -22,6 +22,7 @@ var LFS = struct {
MaxFileSize int64 `ini:"LFS_MAX_FILE_SIZE"`
LocksPagingNum int `ini:"LFS_LOCKS_PAGING_NUM"`
MaxBatchSize int `ini:"LFS_MAX_BATCH_SIZE"`
+ FallbackToOBS bool `ini:"FALLBACK_TO_OBS"`
Storage *Storage
}{}
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 86db44d41b..da1f6da4e9 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -96,7 +96,9 @@ func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) (buf []b
meta, err := git_model.GetLFSMetaObjectByOid(ctx, repoID, pointer.Oid)
if err != nil { // fallback to a plain file
fi.lfsMeta = &pointer
- log.Warn("Unable to access LFS pointer %s in repo %d: %v", pointer.Oid, repoID, err)
+ // OBS-LFS bridge should be consulted in the git_model.GetLFSMetaObjectByOid() ??
+ // bug: https://github.com/openSUSE/openSUSE-git/issues/83
+ log.Info("Unable to access LFS pointer %s in repo %d: %v", pointer.Oid, repoID, err)
return buf, dataRc, fi, nil
}
diff --git a/services/lfs/server.go b/services/lfs/server.go
index 4a1e0aaf7d..2a57301a3d 100644
--- a/services/lfs/server.go
+++ b/services/lfs/server.go
@@ -285,6 +285,35 @@ func BatchHandler(ctx *context.Context) {
responseObject = buildObjectResponse(rc, p, false, !exists, err)
} else {
var err *lfs_module.ObjectError
+
+ if exists && meta == nil {
+ accessible, accessibleErr := git_model.LFSObjectAccessible(ctx, ctx.Doer, p.Oid)
+ if accessibleErr != nil {
+ log.Error("Unable to check if LFS MetaObject [%s] is accessible. Error: %v", p.Oid, err)
+ writeStatus(ctx, http.StatusInternalServerError)
+ return
+ }
+ if accessible {
+ var newMetaObjErr error
+ meta, newMetaObjErr = git_model.NewLFSMetaObject(ctx, repository.ID, p)
+ if newMetaObjErr != nil {
+ log.Error("Unable to create LFS MetaObject [%s] for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err)
+ writeStatus(ctx, http.StatusInternalServerError)
+ return
+ }
+ } else {
+ exists = false
+ }
+ }
+
+ if setting.LFS.FallbackToOBS && (!exists || meta == nil) {
+ obsresp := obsResponseOrNil(rc, p)
+ if obsresp != nil {
+ responseObjects = append(responseObjects, obsresp)
+ continue
+ }
+ }
+
if !exists || meta == nil {
err = &lfs_module.ObjectError{
Code: http.StatusNotFound,
@@ -478,6 +507,27 @@ func getAuthenticatedRepository(ctx *context.Context, rc *requestContext, requir
return repository
}
+func obsResponseOrNil(rc *requestContext, pointer lfs_module.Pointer) *lfs_module.ObjectResponse {
+ url := fmt.Sprintf("http://localhost:9999/check/%s/%d", pointer.Oid, pointer.Size)
+ resp, err := http.Get(url)
+ if err != nil {
+ log.Debug("URL %s returned error", url)
+ return nil
+ }
+ defer resp.Body.Close()
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ log.Debug("URL %s didn't return body", url)
+ return nil
+ }
+ link := string(body)
+ rep := &lfs_module.ObjectResponse{Pointer: pointer}
+ rep.Actions = make(map[string]*lfs_module.Link)
+ header := make(map[string]string)
+ rep.Actions["download"] = &lfs_module.Link{Href: link, Header: header}
+ return rep
+}
+
func buildObjectResponse(rc *requestContext, pointer lfs_module.Pointer, download, upload bool, err *lfs_module.ObjectError) *lfs_module.ObjectResponse {
rep := &lfs_module.ObjectResponse{Pointer: pointer}
if err != nil {
--
2.52.0