From 7942523c3f13ca7a0f37dfeea8472cf0c12f16a8 Mon Sep 17 00:00:00 2001 From: DmitryFrolovTri <23313323+DmitryFrolovTri@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:18:03 +0000 Subject: [PATCH 1/3] SizeLimit w/o DB changes --- custom/conf/app.example.ini | 15 +- models/migrations/migrations.go | 1 - models/migrations/v1_26/v324.go | 18 - models/repo/repo.go | 49 +-- modules/setting/repository.go | 39 +- modules/structs/repo.go | 10 +- options/locale/locale_en-US.json | 31 +- routers/api/v1/repo/repo.go | 9 - routers/private/hook_pre_receive.go | 65 +-- routers/web/admin/repos.go | 45 +- routers/web/admin/repos_test.go | 26 -- routers/web/repo/repo.go | 1 - routers/web/repo/setting/setting.go | 55 +-- services/forms/repo_form.go | 15 +- services/lfs/server.go | 50 +-- services/repository/create.go | 4 - templates/admin/repo/list.tmpl | 52 ++- templates/repo/settings/options.tmpl | 22 +- .../api_helper_for_declarative_test.go | 30 -- tests/integration/git_general_test.go | 33 -- tests/integration/lfs_size_limit_test.go | 411 ------------------ tests/integration/size_limit_test.go | 202 +++++++++ 22 files changed, 303 insertions(+), 880 deletions(-) delete mode 100644 models/migrations/v1_26/v324.go delete mode 100644 routers/web/admin/repos_test.go delete mode 100644 tests/integration/lfs_size_limit_test.go create mode 100644 tests/integration/size_limit_test.go diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index b7210f2ec6..4db927d174 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1087,22 +1087,15 @@ LEVEL = Info ;; Allow to fork repositories without maximum number limit ;ALLOW_FORK_WITHOUT_MAXIMUM_LIMIT = true ; -;; 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 +;; Specify a global repository Git size limit in bytes. -1 - Disabled, 0 - No growth allowed ;; 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 +;GIT_SIZE_MAX = -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 +;; Specify a global repository LFS size limit in bytes. -1 - Disabled, 0 - No growth allowed ;; 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 +;LFS_SIZE_MAX = -1 ;; 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/migrations/migrations.go b/models/migrations/migrations.go index 5fd100f996..e8ebb5df43 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -398,7 +398,6 @@ func prepareMigrationTasks() []*migration { // Gitea 1.25.0 ends at migration ID number 322 (database version 323) newMigration(323, "Add support for actions concurrency", v1_26.AddActionsConcurrency), - newMigration(324, "Add support for repository size limit - SizeLimit and LFSSizeLimit columns to repository table", v1_26.AddSizeLimitOnRepo), } return preparedMigrations } diff --git a/models/migrations/v1_26/v324.go b/models/migrations/v1_26/v324.go deleted file mode 100644 index 5fc9774cdb..0000000000 --- a/models/migrations/v1_26/v324.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package v1_26 - -import ( - "xorm.io/xorm" -) - -func AddSizeLimitOnRepo(x *xorm.Engine) error { - type Repository struct { - ID int64 `xorm:"pk autoincr"` - SizeLimit int64 `xorm:"NOT NULL DEFAULT 0"` - LFSSizeLimit int64 `xorm:"NOT NULL DEFAULT 0"` - } - - return x.Sync2(new(Repository)) -} diff --git a/models/repo/repo.go b/models/repo/repo.go index 59b4fe627e..6ee0e9c860 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -201,11 +201,9 @@ type Repository struct { BaseRepo *Repository `xorm:"-"` IsTemplate bool `xorm:"INDEX NOT NULL DEFAULT false"` TemplateID int64 `xorm:"INDEX"` - SizeLimit int64 `xorm:"NOT NULL DEFAULT 0"` Size int64 `xorm:"NOT NULL DEFAULT 0"` GitSize int64 `xorm:"NOT NULL DEFAULT 0"` LFSSize int64 `xorm:"NOT NULL DEFAULT 0"` - LFSSizeLimit int64 `xorm:"NOT NULL DEFAULT 0"` CodeIndexerStatus *RepoIndexerStatus `xorm:"-"` StatsIndexerStatus *RepoIndexerStatus `xorm:"-"` IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"` @@ -633,58 +631,43 @@ func (repo *Repository) IsOwnedBy(userID int64) bool { } // GetActualSizeLimit returns repository size limit in bytes -// or global repository limit setting if per repository size limit is not set func (repo *Repository) GetActualSizeLimit() int64 { - sizeLimit := repo.SizeLimit - if setting.RepoSizeLimit > 0 && sizeLimit == 0 { - sizeLimit = setting.RepoSizeLimit - } - return sizeLimit + return setting.Repository.GitSizeMax } // RepoSizeIsOversized return true if is over size limitation func (repo *Repository) IsRepoSizeOversized(additionalSize int64) bool { - return repo.ShouldCheckRepoSize() && repo.GitSize+additionalSize > repo.GetActualSizeLimit() + limit := repo.GetActualSizeLimit() + if limit < 0 { + return false + } + newSize := repo.GitSize + additionalSize + return newSize > limit && newSize > repo.GitSize } -// 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 +// ShouldCheckRepoSize returns true if size limit checking is enabled func (repo *Repository) ShouldCheckRepoSize() bool { - return setting.RepoSizeLimit > -1 && repo.GetActualSizeLimit() > 0 + return setting.Repository.GitSizeMax > -1 } // GetActualLFSSizeLimit returns repository LFS size limit in bytes -// or global LFS size limit setting if per repository LFS size limit is not set func (repo *Repository) GetActualLFSSizeLimit() int64 { - lfsSizeLimit := repo.LFSSizeLimit - if setting.LFSSizeLimit > 0 && lfsSizeLimit == 0 { - lfsSizeLimit = setting.LFSSizeLimit - } - return lfsSizeLimit + return setting.Repository.LFSSizeMax } -// ShouldCheckLFSSize returns true if LFS size limit checking is enabled for this repository +// ShouldCheckLFSSize returns true if LFS size limit checking is enabled func (repo *Repository) ShouldCheckLFSSize() bool { - return setting.LFSSizeLimit > -1 && repo.GetActualLFSSizeLimit() > 0 + return setting.Repository.LFSSizeMax > -1 } // IsLFSSizeOversized returns true if adding additionalSize would exceed the LFS size limit func (repo *Repository) IsLFSSizeOversized(additionalSize int64) bool { - return repo.ShouldCheckLFSSize() && - repo.LFSSize+additionalSize > repo.GetActualLFSSizeLimit() -} - -// 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.RepoSizeLimit == -1 { + limit := repo.GetActualLFSSizeLimit() + if limit < 0 { return false } - limit := repo.GetActualSizeLimit() - if limit == 0 { - return false - } - return (repo.GitSize+additionalGitSize)+(repo.LFSSize+additionalLFSSize) > limit + newSize := repo.LFSSize + additionalSize + return newSize > limit && newSize > repo.LFSSize } // CanCreateBranch returns true if repository meets the requirements for creating new branches. diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 1a09d3cd89..1ab7b32fb5 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -6,7 +6,6 @@ package setting import ( "os/exec" "path/filepath" - "strconv" "strings" "code.gitea.io/gitea/modules/log" @@ -56,6 +55,8 @@ var ( DisableDownloadSourceArchives bool AllowForkWithoutMaximumLimit bool AllowForkIntoSameOwner bool + GitSizeMax int64 `ini:"GIT_SIZE_MAX"` + LFSSizeMax int64 `ini:"LFS_SIZE_MAX"` // StreamArchives makes Gitea stream git archive files to the client directly instead of creating an archive first. // Ideally all users should use this streaming method. However, at the moment we don't know whether there are @@ -276,36 +277,11 @@ var ( } RepoRootPath string ScriptType = "bash" - - // Repository size limits - RepoSizeLimit int64 = -1 - LFSSizeLimit int64 = -1 - LFSSizeInRepoSize bool ) -func SaveGlobalRepositorySetting(repoSizeLimit, lfsSizeLimit int64, lfsSizeInRepoSize bool) error { - RepoSizeLimit = repoSizeLimit - LFSSizeLimit = lfsSizeLimit - LFSSizeInRepoSize = lfsSizeInRepoSize - - cfg, err := CfgProvider.PrepareSaving() - if err != nil { - return err - } - - sec := cfg.Section("repository") - if RepoSizeLimit == -1 { - sec.Key("REPO_SIZE_LIMIT").SetValue("-1") - } else { - sec.Key("REPO_SIZE_LIMIT").SetValue(humanize.Bytes(uint64(RepoSizeLimit))) - } - 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 UpdateGlobalRepositoryLimit(gitSizeMax, lfsSizeMax int64) { + Repository.GitSizeMax = gitSizeMax + Repository.LFSSizeMax = lfsSizeMax } func parseSize(sec ConfigSection, key string, def int64) int64 { @@ -328,9 +304,8 @@ func loadRepositoryFrom(rootCfg ConfigProvider) { // Determine and create root git repository path. sec := rootCfg.Section("repository") - 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.GitSizeMax = parseSize(sec, "GIT_SIZE_MAX", -1) + Repository.LFSSizeMax = parseSize(sec, "LFS_SIZE_MAX", -1) Repository.DisableHTTPGit = sec.Key("DISABLE_HTTP_GIT").MustBool() Repository.UseCompatSSHURI = sec.Key("USE_COMPAT_SSH_URI").MustBool() diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 0dcc0096da..90ac1688e4 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -153,10 +153,6 @@ type CreateRepoOption struct { // ObjectFormatName of the underlying git repository // enum: sha1,sha256 ObjectFormatName string `json:"object_format_name" binding:"MaxSize(6)"` - // SizeLimit of the repository - SizeLimit int64 `json:"size_limit"` - // LFSSizeLimit of the repository - LFSSizeLimit int64 `json:"lfs_size_limit"` } // EditRepoOption options when editing a repository's properties @@ -227,11 +223,7 @@ type EditRepoOption struct { DefaultAllowMaintainerEdit *bool `json:"default_allow_maintainer_edit,omitempty"` // set to `true` to archive this repository. Archived *bool `json:"archived,omitempty"` - // SizeLimit of the repository. - SizeLimit *int64 `json:"size_limit,omitempty"` - // LFSSizeLimit of the repository. - LFSSizeLimit *int64 `json:"lfs_size_limit,omitempty"` - // set to a string like `8h30m0s` to set the mirror interval time + // set a string like `8h30m0s` to set the mirror interval time MirrorInterval *string `json:"mirror_interval,omitempty"` // enable prune - remove obsolete remote-tracking references when mirroring EnablePrune *bool `json:"enable_prune,omitempty"` diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index fe3453d019..7b8395d17a 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -987,9 +987,7 @@ "repo_name_profile_private_hint": ".profile-private is a special repository that you can use to add a README.md to your organization member profile, visible only to organization members. Make sure it's private and initialize it with a README in the profile directory to get started.", "repo_name_helper": "Good repository names use short, memorable and unique keywords. A repository named \".profile\" or \".profile-private\" could be used to add a README.md for the user/organization profile.", "repo_size": "Repository Size", - "repo_size_limit": "Repository Size Limit", "lfs_size": "LFS Size", - "lfs_size_limit": "LFS Size Limit", "template": "Template", "template_select": "Select a template.", "template_helper": "Make repository a template", @@ -1116,14 +1114,6 @@ "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.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.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", "migrate_service": "Migration Service", @@ -3064,6 +3054,7 @@ "orgs.members": "Members", "orgs.new_orga": "New Organization", "repos.repo_manage_panel": "Repository Management", + "repos.settings": "Global Repository Settings", "repos.unadopted": "Unadopted Repositories", "repos.unadopted.no_more": "No more unadopted repositories found", "repos.owner": "Owner", @@ -3072,6 +3063,11 @@ "repos.issues": "Issues", "repos.size": "Size", "repos.lfs_size": "LFS Size", + "repos.git_size_max": "Max Git Size (Global)", + "repos.git_size_max_helper": "Maximum Git size allowed for a single repository. Set to -1 for unlimited.", + "repos.lfs_size_max": "Max LFS Size (Global)", + "repos.lfs_size_max_helper": "Maximum LFS size allowed for a single repository. Set to -1 for unlimited.", + "repos.update_success": "Global repository limits have been updated.", "packages.package_manage_panel": "Package Management", "packages.total_size": "Total Size: %s", "packages.unreferenced_size": "Unreferenced Size: %s", @@ -3321,19 +3317,6 @@ "config.open_with_editor_app_help": "The \"Open with\" editors for the clone menu. If left empty, the default will be used. Expand to see the default.", "config.git_guide_remote_name": "Repository remote name for git commands in the guide", "config.repository_config": "Repository Configuration", - "config.enable_size_limit": "Enable Size Limit", - "config.repo_size_limit": "Default Repository Size Limit", - "config.repo_size_limit.desc": "Maximum repository size in MiB. Leave empty for no limit.", - "config.lfs_size_limit": "Default LFS Size Limit", - "config.lfs_size_limit.desc": "Maximum LFS size in MiB. Leave empty for no limit.", - "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 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", "config.git_config": "Git Configuration", "config.git_disable_diff_highlight": "Disable Diff Syntax Highlight", "config.git_max_diff_lines": "Max Diff Lines (for a single file)", @@ -3812,4 +3795,4 @@ "symbolic_link": "Symbolic link", "submodule": "Submodule" } -} +} \ No newline at end of file diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index ada70dda4c..bb6bda587d 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -263,8 +263,6 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre TrustModel: repo_model.ToTrustModel(opt.TrustModel), IsTemplate: opt.Template, ObjectFormatName: opt.ObjectFormatName, - SizeLimit: opt.SizeLimit, - LFSSizeLimit: opt.LFSSizeLimit, }) if err != nil { if repo_model.IsErrRepoAlreadyExist(err) { @@ -751,13 +749,6 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err } } - if opts.SizeLimit != nil { - repo.SizeLimit = *opts.SizeLimit - } - if opts.LFSSizeLimit != nil { - repo.LFSSizeLimit = *opts.LFSSizeLimit - } - if err := repo_service.UpdateRepository(ctx, repo, visibilityChanged); err != nil { ctx.APIErrorInternal(err) return err diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index 43e6199cf5..edf0ea01f3 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -550,13 +550,6 @@ func scanLFSPointersFromObjectIDs(ctx *gitea_context.PrivateContext, repoPath st return out, nil } -func maxInt64(a, b int64) int64 { - if a > b { - return a - } - return b -} - // HookPreReceive checks whether a individual commit is acceptable func HookPreReceive(ctx *gitea_context.PrivateContext) { startTime := time.Now() @@ -587,7 +580,7 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) { var duration time.Duration needGitDelta := repo.ShouldCheckRepoSize() - needLFSDelta := repo.ShouldCheckLFSSize() || setting.LFSSizeInRepoSize + needLFSDelta := repo.ShouldCheckLFSSize() // Only do CountObjects (push/repo) when we're doing the repo-size limit at all if needGitDelta { @@ -827,7 +820,6 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) { currentGit := repo.GitSize currentLFS := repo.LFSSize - currentCombined := currentGit + currentLFS gitDelta := addedSize - removedSize predictedGitAfter := currentGit + gitDelta @@ -835,38 +827,24 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) { lfsDelta := incomingNewToRepoLFS - removedLFSSize predictedLFSAfter := currentLFS + lfsDelta - predictedCombinedAfter := predictedGitAfter - if setting.LFSSizeInRepoSize { - predictedCombinedAfter = predictedGitAfter + predictedLFSAfter - } - - // Avoid nil panic when repo-size check is disabled but LFS delta is enabled (combined mode / LFS limit) - pushBytes := int64(0) - if pushSize != nil { - pushBytes = maxInt64(0, pushSize.Size+pushSize.SizePack) - } - // One summary line (time included here only) if repo.ShouldCheckRepoSize() || repo.ShouldCheckLFSSize() { log.Warn( - "SizeCheck summary: took=%s repo=%s/%s git(pred=%s cur=%s delta=%s) lfs(pred=%s cur=%s delta=%s) combined(pred=%s) limits(repo=%s lfs=%s) LFSSizeInRepoSize=%v push=%s", + "SizeCheck summary: took=%s repo=%s/%s git(pred=%s cur=%s delta=%s) lfs(pred=%s cur=%s delta=%s) limits(git=%s lfs=%s)", duration, repo.OwnerName, repo.Name, base.FileSize(predictedGitAfter), base.FileSize(currentGit), base.FileSize(gitDelta), base.FileSize(predictedLFSAfter), base.FileSize(currentLFS), base.FileSize(lfsDelta), - base.FileSize(predictedCombinedAfter), - base.FileSize(repo.GetActualSizeLimit()), - base.FileSize(repo.GetActualLFSSizeLimit()), - setting.LFSSizeInRepoSize, - base.FileSize(pushBytes), + base.FileSize(setting.Repository.GitSizeMax), + base.FileSize(setting.Repository.LFSSizeMax), ) } - // 1) LFS-only limit: compare against predicted LFS after push + // 1) LFS size limit: compare against predicted LFS after push if repo.ShouldCheckLFSSize() { lfsLimit := repo.GetActualLFSSizeLimit() - if lfsLimit > 0 && predictedLFSAfter > lfsLimit && predictedLFSAfter > currentLFS { - log.Warn("Forbidden: LFS limit exceeded: %s > %s for repo %-v", + if lfsLimit >= 0 && predictedLFSAfter > lfsLimit && predictedLFSAfter > currentLFS { + log.Warn("Forbidden: LFS size limit exceeded: %s > %s for repo %-v", base.FileSize(predictedLFSAfter), base.FileSize(lfsLimit), repo, @@ -881,17 +859,17 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) { } } - // 2) Repo (git) size limit when NOT counting LFS into repo size - if repo.ShouldCheckRepoSize() && !setting.LFSSizeInRepoSize { + // 2) Git size limit + if repo.ShouldCheckRepoSize() { limit := repo.GetActualSizeLimit() - if limit > 0 && predictedGitAfter > limit && predictedGitAfter > currentGit { - log.Warn("Forbidden: repository size limit exceeded: %s > %s for repo %-v", + if limit >= 0 && predictedGitAfter > limit && predictedGitAfter > currentGit { + log.Warn("Forbidden: Repository git size limit exceeded: %s > %s for repo %-v", base.FileSize(predictedGitAfter), base.FileSize(limit), repo, ) ctx.JSON(http.StatusForbidden, private.Response{ - UserMsg: fmt.Sprintf("Repository size limit exceeded: %s > then limit of %s", + UserMsg: fmt.Sprintf("Repository git size limit exceeded: %s > then limit of %s", base.FileSize(predictedGitAfter), base.FileSize(limit), ), @@ -900,25 +878,6 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) { } } - // 3) Combined limit when LFS is counted in repo size - if setting.LFSSizeInRepoSize && repo.ShouldCheckRepoSize() { - limit := repo.GetActualSizeLimit() - if limit > 0 && predictedCombinedAfter > limit && predictedCombinedAfter > currentCombined { - log.Warn("Forbidden: combined repo and LFS size limit exceeded: %s > %s for repo %-v", - base.FileSize(predictedCombinedAfter), - base.FileSize(limit), - repo, - ) - ctx.JSON(http.StatusForbidden, private.Response{ - UserMsg: fmt.Sprintf("Combined repository+LFS size limit exceeded: %s > then limit of %s", - base.FileSize(predictedCombinedAfter), - base.FileSize(limit), - ), - }) - return - } - } - ctx.PlainText(http.StatusOK, "ok") } diff --git a/routers/web/admin/repos.go b/routers/web/admin/repos.go index d5e2052847..550caac3a6 100644 --- a/routers/web/admin/repos.go +++ b/routers/web/admin/repos.go @@ -33,9 +33,8 @@ func Repos(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("admin.repositories") ctx.Data["PageIsAdminRepositories"] = true - ctx.Data["RepoSizeLimit"] = base.FileSize(setting.RepoSizeLimit) - ctx.Data["LFSSizeLimit"] = base.FileSize(setting.LFSSizeLimit) - ctx.Data["LFSSizeInRepoSize"] = setting.LFSSizeInRepoSize + ctx.Data["GitSizeMax"] = base.FileSize(setting.Repository.GitSizeMax) + ctx.Data["LFSSizeMax"] = base.FileSize(setting.Repository.LFSSizeMax) explore.RenderRepoSearch(ctx, &explore.RepoSearchOptions{ Private: true, @@ -46,28 +45,16 @@ func Repos(ctx *context.Context) { } func UpdateRepoPost(ctx *context.Context) { - temp := web.GetForm(ctx) - if temp == nil { - ctx.Data["Err_Repo_Size_Limit"] = "" - explore.RenderRepoSearch(ctx, &explore.RepoSearchOptions{ - Private: true, - PageSize: setting.UI.Admin.RepoPagingNum, - TplName: tplRepos, - OnlyShowRelevant: false, - }) - return - } - form := temp.(*forms.UpdateGlobalRepoFrom) + form := web.GetForm(ctx).(*forms.UpdateGlobalRepoFrom) ctx.Data["Title"] = ctx.Tr("admin.repositories") ctx.Data["PageIsAdminRepositories"] = true - ctx.Data["RepoSizeLimit"] = form.RepoSizeLimit - ctx.Data["LFSSizeLimit"] = form.LFSSizeLimit - ctx.Data["LFSSizeInRepoSize"] = form.LFSSizeInRepoSize + ctx.Data["GitSizeMax"] = form.GitSizeMax + ctx.Data["LFSSizeMax"] = form.LFSSizeMax - repoSizeLimit, err := base.GetFileSize(form.RepoSizeLimit) + gitSizeMax, err := base.GetFileSize(form.GitSizeMax) if err != nil { - ctx.Data["Err_Repo_Size_Limit"] = form.RepoSizeLimit + ctx.Data["Err_Git_Size_Max"] = form.GitSizeMax explore.RenderRepoSearch(ctx, &explore.RepoSearchOptions{ Private: true, PageSize: setting.UI.Admin.RepoPagingNum, @@ -77,9 +64,9 @@ func UpdateRepoPost(ctx *context.Context) { return } - lfsSizeLimit, err := base.GetFileSize(form.LFSSizeLimit) + lfsSizeMax, err := base.GetFileSize(form.LFSSizeMax) if err != nil { - ctx.Data["Err_LFS_Size_Limit"] = form.LFSSizeLimit + ctx.Data["Err_LFS_Size_Max"] = form.LFSSizeMax explore.RenderRepoSearch(ctx, &explore.RepoSearchOptions{ Private: true, PageSize: setting.UI.Admin.RepoPagingNum, @@ -89,19 +76,9 @@ func UpdateRepoPost(ctx *context.Context) { return } - err = setting.SaveGlobalRepositorySetting(repoSizeLimit, lfsSizeLimit, form.LFSSizeInRepoSize) - if err != nil { - ctx.Data["Err_Repo_Size_Save"] = err.Error() - explore.RenderRepoSearch(ctx, &explore.RepoSearchOptions{ - Private: true, - PageSize: setting.UI.Admin.RepoPagingNum, - TplName: tplRepos, - OnlyShowRelevant: false, - }) - return - } + setting.UpdateGlobalRepositoryLimit(gitSizeMax, lfsSizeMax) - ctx.Flash.Success(ctx.Tr("admin.config.repository_setting_success")) + ctx.Flash.Success(ctx.Tr("admin.repos.update_success")) ctx.Redirect(setting.AppSubURL + "/-/admin/repos") } diff --git a/routers/web/admin/repos_test.go b/routers/web/admin/repos_test.go deleted file mode 100644 index 17594fad2e..0000000000 --- a/routers/web/admin/repos_test.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package admin - -import ( - "testing" - - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/services/contexttest" - - "github.com/stretchr/testify/assert" -) - -func TestUpdateRepoPost(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx, _ := contexttest.MockContext(t, "admin/repos") - contexttest.LoadUser(t, ctx, 1) - - ctx.Req.Form.Set("enable_size_limit", "on") - ctx.Req.Form.Set("repo_size_limit", "222 kcmcm") - - UpdateRepoPost(ctx) - - assert.NotEmpty(t, ctx.Flash.ErrorMsg) -} diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index df4fd2361f..3a0976ffa0 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -288,7 +288,6 @@ func CreatePost(ctx *context.Context) { IsTemplate: form.Template, TrustModel: repo_model.DefaultTrustModel, ObjectFormatName: form.ObjectFormatName, - SizeLimit: form.SizeLimit, }) if err == nil { log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name) diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index ab3d28514e..2f18e5bf28 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -15,7 +15,6 @@ import ( repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/indexer/code" @@ -62,11 +61,8 @@ func SettingsCtxData(ctx *context.Context) { ctx.Data["MinimumMirrorInterval"] = setting.Mirror.MinInterval ctx.Data["CanConvertFork"] = ctx.Repo.Repository.IsFork && ctx.Doer.CanCreateRepoIn(ctx.Repo.Repository.Owner) 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["RepoSizeLimit"] = setting.RepoSizeLimit - ctx.Data["LFSSizeLimit"] = setting.LFSSizeLimit - ctx.Data["LFSSizeInRepoSize"] = setting.LFSSizeInRepoSize + ctx.Data["GitSizeMax"] = ctx.Repo.Repository.GetActualSizeLimit() + ctx.Data["LFSSizeMax"] = ctx.Repo.Repository.GetActualLFSSizeLimit() signing, _ := gitrepo.GetSigningKey(ctx) ctx.Data["SigningKeyAvailable"] = signing != nil @@ -214,56 +210,9 @@ 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 - var repoSizeLimit int64 - var err error - if form.RepoSizeLimit != "" { - repoSizeLimit, err = base.GetFileSize(form.RepoSizeLimit) - if err != nil { - ctx.Data["Err_RepoSizeLimit"] = true - 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.invalid_repo_size_limit_repo"), tplSettingsOptions, &form) - return - } - - if !ctx.Doer.IsAdmin && repo.SizeLimit != repoSizeLimit { - ctx.Data["Err_RepoSizeLimit"] = true - ctx.RenderWithErr(ctx.Tr("repo.form.repo_size_limit_only_by_admins"), tplSettingsOptions, &form) - return - } - repo.SizeLimit = repoSizeLimit - - // Handle LFS size limit (admin-only) - var lfsSizeLimit int64 - if form.LFSSizeLimit != "" { - lfsSizeLimit, err = base.GetFileSize(form.LFSSizeLimit) - if err != nil { - ctx.Data["Err_LFSSizeLimit"] = true - 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.invalid_lfs_size_limit_repo"), tplSettingsOptions, &form) - return - } - if !ctx.Doer.IsAdmin && repo.LFSSizeLimit != lfsSizeLimit { - ctx.Data["Err_LFSSizeLimit"] = true - ctx.RenderWithErr(ctx.Tr("repo.form.lfs_size_limit_only_by_admins"), tplSettingsOptions, &form) - return - } - repo.LFSSizeLimit = lfsSizeLimit - if err := repo_service.UpdateRepository(ctx, repo, false); err != nil { ctx.ServerError("UpdateRepository", err) return diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index a3129d6a25..58409f330e 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -18,13 +18,6 @@ import ( "gitea.com/go-chi/binding" ) -// UpdateGlobalRepoFrom for updating global repository setting -type UpdateGlobalRepoFrom struct { - RepoSizeLimit string - LFSSizeLimit string - LFSSizeInRepoSize bool -} - // CreateRepoForm form for creating repository type CreateRepoForm struct { UID int64 `binding:"Required"` @@ -50,7 +43,11 @@ type CreateRepoForm struct { ForkSingleBranch string ObjectFormatName string - SizeLimit int64 +} + +type UpdateGlobalRepoFrom struct { + GitSizeMax string + LFSSizeMax string } // Validate validates the fields @@ -114,8 +111,6 @@ type RepoSettingForm struct { PushMirrorInterval string Template bool EnablePrune bool - RepoSizeLimit string - LFSSizeLimit string // Advanced settings EnableCode bool diff --git a/services/lfs/server.go b/services/lfs/server.go index 6f27e45214..60e6e58c85 100644 --- a/services/lfs/server.go +++ b/services/lfs/server.go @@ -244,70 +244,42 @@ func BatchHandler(ctx *context.Context) { // Create content store once, reuse for tracing + normal logic below. contentStore := lfs_module.NewContentStore() - currCombinedTotal := repository.GitSize + repository.LFSSize - // Baseline repo stats and limits traceBatchDecision(rc, br.Operation, - "req=%s auth=%t isUpload=%t repoID=%d sizes: git=%s lfs=%s combined=%s limits: repo=%s lfs=%s LFSSizeInRepoSize=%v", + "req=%s auth=%t isUpload=%t repoID=%d sizes: git=%s lfs=%s limits: git=%s lfs=%s", reqID, ctx.IsSigned || ctx.Doer != nil, isUpload, repository.ID, base.FileSize(repository.GitSize), base.FileSize(repository.LFSSize), - base.FileSize(currCombinedTotal), base.FileSize(repository.GetActualSizeLimit()), base.FileSize(repository.GetActualLFSSizeLimit()), - setting.LFSSizeInRepoSize, ) // Check LFS size limits for upload operations - if isUpload && (repository.ShouldCheckLFSSize() || (setting.LFSSizeInRepoSize && repository.ShouldCheckRepoSize())) { + if isUpload && repository.ShouldCheckLFSSize() { // Sum sizes of objects that are NEW TO THIS REPO (no meta row) var incomingNewToRepoLFS int64 - var invalidCount, metaMissingCount, metaPresentCount, storeExistsCount int64 for _, p := range br.Objects { if !p.IsValid() { - invalidCount++ continue } meta, _ := git_model.GetLFSMetaObjectByOid(ctx, repository.ID, p.Oid) - exists, _ := contentStore.Exists(p) - if exists { - storeExistsCount++ - } - if meta == nil { - metaMissingCount++ incomingNewToRepoLFS += p.Size - } else { - metaPresentCount++ } } predictedLFS := repository.LFSSize + incomingNewToRepoLFS - predictedTotal := repository.GitSize + predictedLFS - - traceBatchDecision(rc, br.Operation, - "req=%s accounting: objects=%d invalid=%d metaMissing=%d metaPresent=%d storeExists=%d incomingNewToRepoLFS=%s predictedLFS=%s predictedTotal=%s", - reqID, - len(br.Objects), - invalidCount, - metaMissingCount, - metaPresentCount, - storeExistsCount, - base.FileSize(incomingNewToRepoLFS), - base.FileSize(predictedLFS), - base.FileSize(predictedTotal), - ) // LFS-only limit if we are over, but size doesn't increase allow - if repository.ShouldCheckLFSSize() && predictedLFS > repository.GetActualLFSSizeLimit() && predictedLFS > repository.LFSSize { + if predictedLFS > repository.GetActualLFSSizeLimit() && predictedLFS > repository.LFSSize { traceBatchDecision(rc, br.Operation, - "req=%s DECISION=FORBID reason=LFS_LIMIT predictedLFS=%s limit=%s", + "req=%s DECISION=FORBID reason=LFS_LIMIT predictedLFS=%s limit=%s (NewObjects=%d ObjectsPresentInStore=%d MetaPresent=%d StoreExists=%d Invalid=%d)", reqID, base.FileSize(predictedLFS), base.FileSize(repository.GetActualLFSSizeLimit()), ) writeStatusMessage(ctx, http.StatusForbidden, @@ -316,20 +288,6 @@ func BatchHandler(ctx *context.Context) { return } - // Combined limit (conservative: ignores git delta/removals) if we are over, but combined size doesn't increase allow - if setting.LFSSizeInRepoSize && repository.ShouldCheckRepoSize() { - if predictedTotal > repository.GetActualSizeLimit() && predictedTotal > currCombinedTotal { - traceBatchDecision(rc, br.Operation, - "req=%s DECISION=FORBID reason=COMBINED_LIMIT predictedTotal=%s limit=%s", - reqID, base.FileSize(predictedTotal), base.FileSize(repository.GetActualSizeLimit()), - ) - writeStatusMessage(ctx, http.StatusForbidden, - fmt.Sprintf("Repository size %s after LFS addition would exceed limit %s", - base.FileSize(predictedTotal), base.FileSize(repository.GetActualSizeLimit()))) - return - } - } - traceBatchDecision(rc, br.Operation, "req=%s DECISION=ALLOW size-check passed", reqID) } diff --git a/services/repository/create.go b/services/repository/create.go index 8eb2082738..7439fc8f08 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -52,8 +52,6 @@ type CreateRepoOptions struct { TrustModel repo_model.TrustModelType MirrorInterval string ObjectFormatName string - SizeLimit int64 - LFSSizeLimit int64 } func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir string, opts CreateRepoOptions) error { @@ -249,8 +247,6 @@ func CreateRepositoryDirectly(ctx context.Context, doer, owner *user_model.User, Status: opts.Status, IsEmpty: !opts.AutoInit, TrustModel: opts.TrustModel, - SizeLimit: opts.SizeLimit, - LFSSizeLimit: opts.LFSSizeLimit, IsMirror: opts.IsMirror, DefaultBranch: opts.DefaultBranch, DefaultWikiBranch: setting.Repository.DefaultBranch, diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl index f65d0460c0..aebddeb19c 100644 --- a/templates/admin/repo/list.tmpl +++ b/templates/admin/repo/list.tmpl @@ -1,31 +1,13 @@ {{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin")}}

- {{ctx.Locale.Tr "admin.config.global_repo_size_limit_manage_panel"}} + {{ctx.Locale.Tr "admin.repositories"}} +
+ {{ctx.Locale.Tr "repo.repo_size"}} Max: {{.GitSizeMax}} + {{ctx.Locale.Tr "repo.lfs_size"}} Max: {{.LFSSizeMax}} +

-
-
- {{.CsrfTokenHtml}} -
- - -
-
- - -
-
- -
- -
-
-
- -
-
-
-

+

{{ctx.Locale.Tr "admin.repos.repo_manage_panel"}} ({{ctx.Locale.Tr "admin.total" .Total}})
{{ctx.Locale.Tr "admin.repos.unadopted"}} @@ -126,6 +108,28 @@ {{template "base/paginate" .}}
+
+

+ {{ctx.Locale.Tr "admin.repos.settings"}} +

+
+
+ {{$.CsrfTokenHtml}} +
+ + +

{{ctx.Locale.Tr "admin.repos.git_size_max_helper"}}

+
+
+ + +

{{ctx.Locale.Tr "admin.repos.lfs_size_max_helper"}}

+
+ +
+
+
+