From 624fe246939e3aa0e104f2ee6e1a54daa1199251 Mon Sep 17 00:00:00 2001 From: DmitryFrolovTri <23313323+DmitryFrolovTri@users.noreply.github.com> Date: Thu, 18 Dec 2025 19:06:11 +0000 Subject: [PATCH] refactor to remove ENABLE_SIZE_LIMIT option and replace it with -1 in the config. --- custom/conf/app.example.ini | 24 ++++++---- models/repo/repo.go | 9 ++-- modules/base/tool.go | 8 ++++ modules/setting/repository.go | 59 +++++++++++++++--------- options/locale/locale_en-US.ini | 12 +++-- routers/private/hook_pre_receive.go | 6 ++- routers/web/admin/repos.go | 14 ++---- routers/web/explore/repo.go | 10 +++- routers/web/repo/setting/setting.go | 13 ++++-- services/forms/repo_form.go | 1 - templates/admin/repo/list.tmpl | 18 +++----- templates/repo/settings/options.tmpl | 13 +++--- tests/integration/git_general_test.go | 4 +- tests/integration/lfs_size_limit_test.go | 20 ++++---- 14 files changed, 123 insertions(+), 88 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index ca4ccecbeb..b3116cde0f 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1084,14 +1084,22 @@ LEVEL = Info ;; Allow to fork repositories without maximum number limit ;ALLOW_FORK_WITHOUT_MAXIMUM_LIMIT = true ; -;; Enable applying a global size limit defined by REPO_SIZE_LIMIT. Each repository can have a value that overrides the global limit -;; "false" means no limit will be enforced, even if specified on a repository -;ENABLE_SIZE_LIMIT = false -; -;; Specify a global repository size limit in bytes to apply for each repository. 0 - No limit -;; If repository has it's own limit set in UI it will override the global setting -;; Standard units of measurements for size can be used like B, KB, KiB, ... , EB, EiB, ... -;REPO_SIZE_LIMIT = 0 +;; Specify a global repository size limit in bytes to apply for each repository. -1 - Disabled, 0 - Enabled with no limit +;; If repository has it's own limit set in repositiry settings UI it will override the global setting +;; Standard units of measurements for size can be used like B, KB, KiB, ... , EB, EiB, etc or if not provided - bytes +;; This is experimental and subject to change +;REPO_SIZE_LIMIT = -1 + +;; Specify a global LFS size limit in bytes to apply for each repository. -1 - Disabled, 0 - Enabled with no limit +;; If repository has it's own limit set in repository settings UI it will override the global setting +;; Standard units of measurements for size can be used like B, KB, KiB, ... , EB, EiB, etc or if not provided - bytes +;; This is experimental and subject to change +;LFS_SIZE_LIMIT = -1 +;; +;; If true, LFS size will be included in the repository size calculation. +;; Which means even if LFS_SIZE_LIMIT is not set (-1) pushes will be rejected if LFS size + repository size exceeds REPO_SIZE_LIMIT +;; This is experimental and subject to change +;LFS_SIZE_IN_REPO_SIZE = false ;; Allow to fork repositories into the same owner (user or organization) ;; This feature is experimental, not fully tested, and may be changed in the future diff --git a/models/repo/repo.go b/models/repo/repo.go index 02333b13e9..59b4fe627e 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -203,7 +203,6 @@ type Repository struct { TemplateID int64 `xorm:"INDEX"` SizeLimit int64 `xorm:"NOT NULL DEFAULT 0"` Size int64 `xorm:"NOT NULL DEFAULT 0"` - EnableSizeLimit bool `xorm:"NOT NULL DEFAULT true"` GitSize int64 `xorm:"NOT NULL DEFAULT 0"` LFSSize int64 `xorm:"NOT NULL DEFAULT 0"` LFSSizeLimit int64 `xorm:"NOT NULL DEFAULT 0"` @@ -645,13 +644,13 @@ func (repo *Repository) GetActualSizeLimit() int64 { // RepoSizeIsOversized return true if is over size limitation func (repo *Repository) IsRepoSizeOversized(additionalSize int64) bool { - return setting.EnableSizeLimit && repo.GetActualSizeLimit() > 0 && repo.GitSize+additionalSize > repo.GetActualSizeLimit() + return repo.ShouldCheckRepoSize() && repo.GitSize+additionalSize > repo.GetActualSizeLimit() } // ShouldCheckRepoSize returns true if size limit checking is enabled and limit is non zero for this specific repository // this is used to enable size checking during pre-receive hook func (repo *Repository) ShouldCheckRepoSize() bool { - return setting.EnableSizeLimit && repo.GetActualSizeLimit() > 0 + return setting.RepoSizeLimit > -1 && repo.GetActualSizeLimit() > 0 } // GetActualLFSSizeLimit returns repository LFS size limit in bytes @@ -666,7 +665,7 @@ func (repo *Repository) GetActualLFSSizeLimit() int64 { // ShouldCheckLFSSize returns true if LFS size limit checking is enabled for this repository func (repo *Repository) ShouldCheckLFSSize() bool { - return setting.EnableSizeLimit && repo.GetActualLFSSizeLimit() > 0 + return setting.LFSSizeLimit > -1 && repo.GetActualLFSSizeLimit() > 0 } // IsLFSSizeOversized returns true if adding additionalSize would exceed the LFS size limit @@ -678,7 +677,7 @@ func (repo *Repository) IsLFSSizeOversized(additionalSize int64) bool { // IsRepoAndLFSSizeOversized checks if combined repo + LFS size exceeds repo size limit // This is used when LFS_SIZE_IN_REPO_SIZE is enabled func (repo *Repository) IsRepoAndLFSSizeOversized(additionalGitSize, additionalLFSSize int64) bool { - if !setting.LFSSizeInRepoSize || !setting.EnableSizeLimit { + if !setting.LFSSizeInRepoSize || setting.RepoSizeLimit == -1 { return false } limit := repo.GetActualSizeLimit() diff --git a/modules/base/tool.go b/modules/base/tool.go index 66ef71cfef..bae62c491c 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -12,6 +12,7 @@ import ( "fmt" "hash" "strconv" + "strings" "time" "code.gitea.io/gitea/modules/setting" @@ -92,11 +93,18 @@ func CreateTimeLimitCode[T time.Time | string](data string, minutes int, startTi // FileSize calculates the file size and generate user-friendly string. func FileSize(s int64) string { + if s == -1 { + return "-1" + } return humanize.IBytes(uint64(s)) } // Get FileSize bytes value from String. func GetFileSize(s string) (int64, error) { + s = strings.TrimSpace(s) + if s == "-1" { + return -1, nil + } v, err := humanize.ParseBytes(s) iv := int64(v) return iv, err diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 6e99cb4090..1a09d3cd89 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -6,6 +6,7 @@ package setting import ( "os/exec" "path/filepath" + "strconv" "strings" "code.gitea.io/gitea/modules/log" @@ -276,32 +277,50 @@ var ( RepoRootPath string ScriptType = "bash" - EnableSizeLimit = true - RepoSizeLimit int64 - LFSSizeLimit int64 + // Repository size limits + RepoSizeLimit int64 = -1 + LFSSizeLimit int64 = -1 LFSSizeInRepoSize bool ) -func SaveGlobalRepositorySetting(enableSizeLimit bool, repoSizeLimit, lfsSizeLimit int64, lfsSizeInRepoSize bool) error { - EnableSizeLimit = enableSizeLimit +func SaveGlobalRepositorySetting(repoSizeLimit, lfsSizeLimit int64, lfsSizeInRepoSize bool) error { RepoSizeLimit = repoSizeLimit LFSSizeLimit = lfsSizeLimit LFSSizeInRepoSize = lfsSizeInRepoSize - sec := CfgProvider.Section("repository") - if EnableSizeLimit { - sec.Key("ENABLE_SIZE_LIMIT").SetValue("true") - } else { - sec.Key("ENABLE_SIZE_LIMIT").SetValue("false") + + cfg, err := CfgProvider.PrepareSaving() + if err != nil { + return err } - sec.Key("REPO_SIZE_LIMIT").SetValue(humanize.Bytes(uint64(RepoSizeLimit))) - sec.Key("LFS_SIZE_LIMIT").SetValue(humanize.Bytes(uint64(LFSSizeLimit))) - if lfsSizeInRepoSize { - sec.Key("LFS_SIZE_IN_REPO_SIZE").SetValue("true") + sec := cfg.Section("repository") + if RepoSizeLimit == -1 { + sec.Key("REPO_SIZE_LIMIT").SetValue("-1") } else { - sec.Key("LFS_SIZE_IN_REPO_SIZE").SetValue("false") + sec.Key("REPO_SIZE_LIMIT").SetValue(humanize.Bytes(uint64(RepoSizeLimit))) } - return nil + if LFSSizeLimit == -1 { + sec.Key("LFS_SIZE_LIMIT").SetValue("-1") + } else { + sec.Key("LFS_SIZE_LIMIT").SetValue(humanize.Bytes(uint64(LFSSizeLimit))) + } + sec.Key("LFS_SIZE_IN_REPO_SIZE").SetValue(strconv.FormatBool(LFSSizeInRepoSize)) + return cfg.Save() +} + +func parseSize(sec ConfigSection, key string, def int64) int64 { + v := sec.Key(key).MustString("") + if v == "" { + return def + } + if v == "-1" { + return -1 + } + size, err := humanize.ParseBytes(v) + if err != nil { + return def + } + return int64(size) } func loadRepositoryFrom(rootCfg ConfigProvider) { @@ -309,12 +328,8 @@ func loadRepositoryFrom(rootCfg ConfigProvider) { // Determine and create root git repository path. sec := rootCfg.Section("repository") - EnableSizeLimit = sec.Key("ENABLE_SIZE_LIMIT").MustBool(false) - - v, _ := humanize.ParseBytes(sec.Key("REPO_SIZE_LIMIT").MustString("0")) - RepoSizeLimit = int64(v) - v, _ = humanize.ParseBytes(sec.Key("LFS_SIZE_LIMIT").MustString("0")) - LFSSizeLimit = int64(v) + RepoSizeLimit = parseSize(sec, "REPO_SIZE_LIMIT", -1) + LFSSizeLimit = parseSize(sec, "LFS_SIZE_LIMIT", -1) LFSSizeInRepoSize = sec.Key("LFS_SIZE_IN_REPO_SIZE").MustBool(false) Repository.DisableHTTPGit = sec.Key("DISABLE_HTTP_GIT").MustBool() diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 1a1cf1e92c..3b935006bc 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1188,11 +1188,14 @@ form.reach_limit_of_creation_1 = The owner has already reached the limit of %d r form.reach_limit_of_creation_n = The owner has already reached the limit of %d repositories. form.name_reserved = The repository name "%s" is reserved. form.name_pattern_not_allowed = The pattern "%s" is not allowed in a repository name. -form.repo_size_limit_negative = Repository size limitation cannot be negative. +form.invalid_repo_size_limit = Invalid format. Use -1, 0, or size (e.g. 500MiB). +form.invalid_repo_size_limit_repo = Invalid format. Use 0 or size (e.g. 500MiB). +form.invalid_lfs_size_limit = Invalid format. Use -1, 0, or size (e.g. 500MiB). +form.invalid_lfs_size_limit_repo = Invalid format. Use 0 or size (e.g. 500MiB). form.repo_size_limit_only_by_admins = Only administrators can change the repository size limitation. -form.invalid_lfs_size_limit = Invalid LFS size limit format. -form.lfs_size_limit_negative = LFS size limitation cannot be negative. form.lfs_size_limit_only_by_admins = Only administrators can change the LFS size limitation. +repo_size_limit_helper = Set to 0 to use the global limit. +lfs_size_limit_helper = Set to 0 to use the global limit. need_auth = Authorization migrate_options = Migration Options @@ -3465,7 +3468,8 @@ config.lfs_size_in_repo_size = Count LFS in Repository Size config.lfs_size_in_repo_size.desc = When enabled, LFS size is included in repository size calculations config.global_repo_size_limit_manage_panel = Manage Global Repository Size Limit config.update_settings = Update Settings -config.invalid_repo_size = Invalid repository size %s +config.invalid_repo_size = Invalid format: %s. Use -1, 0, or size (e.g. 500MiB). +config.invalid_lfs_size = Invalid format: %s. Use -1, 0, or size (e.g. 500MiB). config.save_repo_size_setting_failed = Failed to save global repository settings %s config.repository_setting_success = Global repository setting has been updated diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index 7cc1c10f2d..43e6199cf5 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -777,6 +777,10 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) { if _, inOld := oldLFSPtrs[oid]; !inOld { addedLFSSize += sz if _, inOther := otherLFSPtrs[oid]; !inOther { + // Check if the object is already in the database for this repository (e.g. orphan or referenced by hidden ref) + if _, err := git_model.GetLFSMetaObjectByOid(ctx, repo.ID, oid); err == nil { + continue + } incomingNewToRepoLFS += sz } } @@ -880,7 +884,7 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) { // 2) Repo (git) size limit when NOT counting LFS into repo size if repo.ShouldCheckRepoSize() && !setting.LFSSizeInRepoSize { limit := repo.GetActualSizeLimit() - if limit > 0 && predictedGitAfter > limit { + if limit > 0 && predictedGitAfter > limit && predictedGitAfter > currentGit { log.Warn("Forbidden: repository size limit exceeded: %s > %s for repo %-v", base.FileSize(predictedGitAfter), base.FileSize(limit), diff --git a/routers/web/admin/repos.go b/routers/web/admin/repos.go index 4fce654b96..d5e2052847 100644 --- a/routers/web/admin/repos.go +++ b/routers/web/admin/repos.go @@ -33,7 +33,6 @@ func Repos(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("admin.repositories") ctx.Data["PageIsAdminRepositories"] = true - ctx.Data["EnableSizeLimit"] = setting.EnableSizeLimit ctx.Data["RepoSizeLimit"] = base.FileSize(setting.RepoSizeLimit) ctx.Data["LFSSizeLimit"] = base.FileSize(setting.LFSSizeLimit) ctx.Data["LFSSizeInRepoSize"] = setting.LFSSizeInRepoSize @@ -62,14 +61,13 @@ func UpdateRepoPost(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("admin.repositories") ctx.Data["PageIsAdminRepositories"] = true - repoSizeLimit, err := base.GetFileSize(form.RepoSizeLimit) - - ctx.Data["EnableSizeLimit"] = form.EnableSizeLimit ctx.Data["RepoSizeLimit"] = form.RepoSizeLimit + ctx.Data["LFSSizeLimit"] = form.LFSSizeLimit ctx.Data["LFSSizeInRepoSize"] = form.LFSSizeInRepoSize + repoSizeLimit, err := base.GetFileSize(form.RepoSizeLimit) if err != nil { - ctx.Data["Err_Repo_Size_Limit"] = err.Error() + ctx.Data["Err_Repo_Size_Limit"] = form.RepoSizeLimit explore.RenderRepoSearch(ctx, &explore.RepoSearchOptions{ Private: true, PageSize: setting.UI.Admin.RepoPagingNum, @@ -80,10 +78,8 @@ func UpdateRepoPost(ctx *context.Context) { } lfsSizeLimit, err := base.GetFileSize(form.LFSSizeLimit) - ctx.Data["LFSSizeLimit"] = form.LFSSizeLimit - if err != nil { - ctx.Data["Err_LFS_Size_Limit"] = err.Error() + ctx.Data["Err_LFS_Size_Limit"] = form.LFSSizeLimit explore.RenderRepoSearch(ctx, &explore.RepoSearchOptions{ Private: true, PageSize: setting.UI.Admin.RepoPagingNum, @@ -93,7 +89,7 @@ func UpdateRepoPost(ctx *context.Context) { return } - err = setting.SaveGlobalRepositorySetting(form.EnableSizeLimit, repoSizeLimit, lfsSizeLimit, form.LFSSizeInRepoSize) + err = setting.SaveGlobalRepositorySetting(repoSizeLimit, lfsSizeLimit, form.LFSSizeInRepoSize) if err != nil { ctx.Data["Err_Repo_Size_Save"] = err.Error() explore.RenderRepoSearch(ctx, &explore.RepoSearchOptions{ diff --git a/routers/web/explore/repo.go b/routers/web/explore/repo.go index 401c24fc90..eeb3c49c5b 100644 --- a/routers/web/explore/repo.go +++ b/routers/web/explore/repo.go @@ -142,13 +142,19 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { ctx.Data["Page"] = pager if ctx.Data["Err_Repo_Size_Limit"] != nil { - ctx.RenderWithErr(ctx.Tr("admin.config.invalid_repo_size", ctx.Data["Err_Repo_Size_Limit"].(string)), + ctx.RenderWithErr(ctx.Tr("admin.config.invalid_repo_size", ctx.Data["Err_Repo_Size_Limit"]), + opts.TplName, nil) + return + } + + if ctx.Data["Err_LFS_Size_Limit"] != nil { + ctx.RenderWithErr(ctx.Tr("admin.config.invalid_lfs_size", ctx.Data["Err_LFS_Size_Limit"]), opts.TplName, nil) return } if ctx.Data["Err_Repo_Size_Save"] != nil { - ctx.RenderWithErr(ctx.Tr("admin.config.save_repo_size_setting_failed", ctx.Data["Err_Repo_Size_Save"].(string)), + ctx.RenderWithErr(ctx.Tr("admin.config.save_repo_size_setting_failed", ctx.Data["Err_Repo_Size_Save"]), opts.TplName, nil) return } diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index eca3991754..0480e9d1f6 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -64,7 +64,8 @@ func SettingsCtxData(ctx *context.Context) { ctx.Data["Err_RepoSize"] = ctx.Repo.Repository.IsRepoSizeOversized(ctx.Repo.Repository.GetActualSizeLimit() / 10) // less than 10% left ctx.Data["ActualSizeLimit"] = ctx.Repo.Repository.GetActualSizeLimit() ctx.Data["ActualLFSSizeLimit"] = ctx.Repo.Repository.GetActualLFSSizeLimit() - ctx.Data["EnableSizeLimit"] = setting.EnableSizeLimit + ctx.Data["RepoSizeLimit"] = setting.RepoSizeLimit + ctx.Data["LFSSizeLimit"] = setting.LFSSizeLimit ctx.Data["LFSSizeInRepoSize"] = setting.LFSSizeInRepoSize signing, _ := gitrepo.GetSigningKey(ctx, ctx.Repo.Repository) @@ -213,6 +214,8 @@ func handleSettingsPostUpdate(ctx *context.Context) { repo.Name = newRepoName repo.LowerName = strings.ToLower(newRepoName) repo.Description = form.Description + ctx.Data["RepoSizeLimitText"] = form.RepoSizeLimit + ctx.Data["LFSSizeLimitText"] = form.LFSSizeLimit repo.Website = form.Website repo.IsTemplate = form.Template @@ -222,13 +225,13 @@ func handleSettingsPostUpdate(ctx *context.Context) { repoSizeLimit, err = base.GetFileSize(form.RepoSizeLimit) if err != nil { ctx.Data["Err_RepoSizeLimit"] = true - ctx.RenderWithErr(ctx.Tr("repo.form.invalid_repo_size_limit"), tplSettingsOptions, &form) + ctx.RenderWithErr(ctx.Tr("repo.form.invalid_repo_size_limit_repo"), tplSettingsOptions, &form) return } } if repoSizeLimit < 0 { ctx.Data["Err_RepoSizeLimit"] = true - ctx.RenderWithErr(ctx.Tr("repo.form.repo_size_limit_negative"), tplSettingsOptions, &form) + ctx.RenderWithErr(ctx.Tr("repo.form.invalid_repo_size_limit_repo"), tplSettingsOptions, &form) return } @@ -245,13 +248,13 @@ func handleSettingsPostUpdate(ctx *context.Context) { lfsSizeLimit, err = base.GetFileSize(form.LFSSizeLimit) if err != nil { ctx.Data["Err_LFSSizeLimit"] = true - ctx.RenderWithErr(ctx.Tr("repo.form.invalid_lfs_size_limit"), tplSettingsOptions, &form) + ctx.RenderWithErr(ctx.Tr("repo.form.invalid_lfs_size_limit_repo"), tplSettingsOptions, &form) return } } if lfsSizeLimit < 0 { ctx.Data["Err_LFSSizeLimit"] = true - ctx.RenderWithErr(ctx.Tr("repo.form.lfs_size_limit_negative"), tplSettingsOptions, &form) + ctx.RenderWithErr(ctx.Tr("repo.form.invalid_lfs_size_limit_repo"), tplSettingsOptions, &form) return } if !ctx.Doer.IsAdmin && repo.LFSSizeLimit != lfsSizeLimit { diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 4b5bebe7e0..0d45bb7326 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -22,7 +22,6 @@ import ( type UpdateGlobalRepoFrom struct { RepoSizeLimit string LFSSizeLimit string - EnableSizeLimit bool LFSSizeInRepoSize bool } diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl index adebc70521..f65d0460c0 100644 --- a/templates/admin/repo/list.tmpl +++ b/templates/admin/repo/list.tmpl @@ -6,18 +6,6 @@